slim3-user-japanに投稿出来ない

slim3について質問があったのでディスカッションに投稿したら投稿エラーで弾かれた。投稿者を制限してるのかなー?最近スパムも多いみたいだし・・・。

投稿しようとした内容はfilterInMemory、sortInMemory関連。

filterInMemory、sortInMemory がサポートされたけれど、InMemory処理後に特定範囲の値の取得は独自で実装する事になるのかな?offsetInMemory、limitInMemoryみたいなメソッドが用意されると良いのにな。

PersistenceManagerインスタンスの寿命

Google App Engine for Java アプリのレスポンスが5秒程待たされる場合があるのですが、原因を調べてみるとPersistenceManagerインスタンスの取得に時間がかかってしまい結果レスポンスが落ちるようです。

PersistenceManagerインスタンスはおよそ2分程でその寿命が尽きてしまいその後インスタンス取得の処理が走ると新たにインスタンスを生成しています。PersistenceManagerインスタンスの取得は高価で新規のインスタンス取得には大体2秒程度かかります。アプリケーションが常に動いているならばインスタンスの寿命が尽きる事はないのですが、私のアプリのようにあまり使用されていない場合は頻繁にPersistenceManagerインスタンスの寿命が尽きてしまいます。

そこでインスタンスの寿命が尽きてしまわないようcronで定期的にPersistenceManagerインスタンスを取得するようにしました。ソースは以下のような感じ。

package simplebookmarks.servlet;

import java.io.IOException;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import simplebookmarks.PMF;

public class CronServlet extends HttpServlet {

 private static final long serialVersionUID = 1L;

 private static final Logger logger = Logger.getLogger(CronServlet.class.getName());

 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {

  PersistenceManager pm = null;
  try {
   pm = PMF.get().getPersistenceManager();
  } finally {
   pm.close();
  }

  logger.info("PMF.get().getPersistenceManager() : ok.");

  req.getRequestDispatcher("/cron.jsp").forward(req, resp);

 }

}

cron.xmlはこんな感じで。

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
	<cron>
		<url>/cron</url>
		<description>Pool the instance every 2 minutes</description>
		<schedule>every 2 minutes</schedule>
	</cron>
</cronentries>

これで快適にアプリを利用出来るようになりました。

*ひょっとしたら何かしらオプション指定でインスタンスの寿命を延ばすことが出来るかも?

JDOクエリで例外とその対応

Google App Engine for Java 少しずつ勉強しています。

JDOでBigTableへの問い合わせを下記のようなコードで書いていました。

String query = 
   "select from " + Collection.class.getName()
 + " where author == '" + user.getEmail() + "'"
 + " order by displayOrder";
List<Collection> collections = (List<Collection>) pm.newQuery(query).execute();

一応動いているんですが頻繁に例外が発生します。こんな感じで。

09-30 11:41PM 28.654
com.google.appengine.repackaged.com.google.common.base.FinalizableReferenceQueue$SystemLoader loadFinalizer: Not allowed to access system class loader.
09-30 11:41PM 28.667
com.google.appengine.repackaged.com.google.common.base.internal.Finalizer getInheritableThreadLocalsField: Couldn't access Thread.inheritableThreadLocals. Reference finalizer threads will inherit thread local values.
09-30 11:41PM 28.669
com.google.appengine.repackaged.com.google.common.base.FinalizableReferenceQueue <init>: Failed to start reference finalizer thread. Reference cleanup will only occur when new references are created.
java.lang.reflect.InvocationTargetException

どうして・・・。理由が分からなくてずっとほったらかしにしていたのですがGAE/Jのドキュメントを読んでいると下記のような書き方も出来るようなので試してみました。

Query query = pm.newQuery(Collection.class);
query.setFilter("author == authorParam");
query.setOrdering("displayOrder asc");
query.declareParameters("String authorParam");
List<Collection> collections = (List<Collection>) query.execute(user.getEmail());

すると例外が発生しなくなりました!(今のところ)。アプリがサクサク動いています。これぞGAE/Jだと言わんばかり!

JDOQL 文字列の構文だとその解釈に問題が出たりするのかな?。誰か詳しい人教えて下さい <(_ _)>

と言う訳でクエリは javax.jdo.Query を用いて組み立てるのが吉らしいです。


追記その1:例外はやっぱり出てしまいました。でも前よりは少なくなったような?しばらく様子見と勉強。Memcache を利用するとかの話じゃないよなぁ・・・。

追記その2:id:higayasuoさんよりコメントを頂きました。Finalizerは無視していいとの事。


シンプルなオンラインブックマーク Simple Bookmarks
http://simplebookmarks.appspot.com/

単一項目のチェックボックスを複数使うには

サブミットされるフォームデータにチェックされなかった項目は含まれないため, 繰り返しの中にチェックボタンしか項目がない場合, 繰り返しの数を正確に判定できない場合があります. これを回避するには,繰り返しの中に隠し項目を含めるようにします.

例:テンプレートHTML (foo.html)

<input type="hidden" id="optionItemsSave" />
<div id="optionItems">
    <label>
        <input type="checkbox" id="checked" value="dummy" />
        <span id="name" te:omittag="true">dummy</span>
    </label>
    <input type="hidden" id="name-2" value="dummy" />
</div>

隠し項目を含めるイメージがソースを読まないと想像付きにくいかも。とりあえずメモ。

単一のチェックボックスでdisabledを適応させる

disabled属性はリクエストパラメータに含まれません。そのため、サーバー側からチェックされていないように見えます。
hiddenに値を持たせ、disabled属性の値を@PageScopeで持つ事でdisable属性の付いたcheckboxの値を扱う事が出来ます。

checkbox.html

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>checkbox disabled.</title>
</head>
<body>
<form id="cform">
<input type="checkbox" id="c1" name="c1" disabled="disabled"/>c1<br/>
<input type="checkbox" id="c2" name="c2" disabled="disabled"/>c2<br/>
<input type="checkbox" id="c3" name="c3"/>c3<br/>
<input type="submit"/>
<input type="hidden" id="c1hidden"/>
<input type="hidden" id="c2hidden"/>
<input type="hidden" id="c3hidden"/>
</form>
</body>
</html>

CheckboxPage.java

package example.teeda;

import org.seasar.teeda.extension.annotation.scope.PageScope;

public class CheckboxPage {

	public boolean c1;

	public boolean c2;

	public boolean c3;

	@PageScope
	public boolean c1Disabled;

	@PageScope
	public boolean c2Disabled;

	@PageScope
	public boolean c3Disabled;

	public Class initialize() {
		//チェックボックスの値
		c1 = true;
		c2 = false;
		c3 = false;
		//チェックボックスのdisabledプロパティ
		c1Disabled = true;
		c2Disabled = true;
		c3Disabled = false;
		return null;
	}

	public Class prerender() {
		System.out.println("c1:" + c1);
		System.out.println("c2:" + c2);
		System.out.println("c3:" + c3);
		return null;
	}

	public boolean getC1hidden() {
		return c1;
	}
	
	public boolean getC2hidden() {
		return c2;
	}
	
	public boolean getC3hidden() {
		return c3;
	}

	public void setC1hidden(boolean b) {
		if (c1Disabled) {
			c1 = b;
		}
	}

	public void setC2hidden(boolean b) {
		if (c2Disabled) {
			c2 = b;
		}
	}

	public void setC3hidden(boolean b) {
		if (c3Disabled) {
			c3 = b;
		}
	}

}

--user-data-dirオプションを簡単に追加するスクリプト

先日エントリーした「Google Chrome アプリケーションショートカットを個別のユーザーデータで起動する」にある--user-data-dirオプションを簡単に追加するvbsスクリプトを書きました。アプリケーションのショートカットを作成後、下記スクリプトにドラッグ&ドロップで追加されます。ユーザーデータフォルダはsUserDataFolder = "hogehoge"のhogehogeを書き換えればいくつも作る事ができます。

ユーザーデータフォルダ設定 - hogehoge - .vbs

Option Explicit



Dim sUserDataFolder

sUserDataFolder = "hogehoge"



Call Main(WScript.Arguments)



Private Sub Main(oArgs)

	If oArgs.Count = 0 Then

		WScript.Echo "ショートカットをこのアイコンへドラッグ&ドロップしてください。"

		Exit Sub

	End If

	WScript.Echo SetUserDataFolder(oArgs)

End Sub



Private Function SetUserDataFolder(oArgs)

	Dim WshShell, re, i, flg, oShellLink, oMatchesLnk, oMatchesChrome, oMatchesUserDataDir, sOption

	sOption = " --user-data-dir=" & Chr(34) & "..\User Data\" & sUserDataFolder & Chr(34)

	flg = False

	Set WshShell = WScript.CreateObject("WScript.Shell")

	Set re = new RegExp

	re.Global = True

	For i = 0 To oArgs.Count - 1

		re.Pattern = "(.lnk)"

		Set oMatchesLnk = re.Execute(oArgs(i))

		If oMatchesLnk.Count = 1 Then

			Set oShellLink = WshShell.CreateShortcut(oArgs(i))

			re.Pattern = "chrome.exe"

			Set oMatchesChrome = re.Execute(oShellLink.TargetPath)

			If oMatchesChrome.Count = 1 Then

				If oMatchesChrome(0) = "chrome.exe" Then

					flg = True

					re.Pattern = "--user-data-dir"

					Set oMatchesUserDataDir = re.Execute(oShellLink.Arguments)

					If oMatchesUserDataDir.Count = 0 Then

						With oShellLink

							oShellLink.Arguments = sOption & " " & .Arguments

							Call oShellLink.Save()

						End With

					End If

				End If

			End If

		End If

	Next

	If flg Then

		SetUserDataFolder = "ユーザーデータフォルダ " & sOption & " を設定しました。"

	Else

		SetUserDataFolder = "ユーザーデータフォルダを設定出来ませんでした。"

	End If

End Function