SyntaxHighlighter

ラベル 技術 の投稿を表示しています。 すべての投稿を表示
ラベル 技術 の投稿を表示しています。 すべての投稿を表示

2012年11月8日木曜日

グルーピングして連番を振る

後で整形。備忘録。

レコードが

tableA
ID 日時 名称
1 11/4 データA
1 11/5 データB
2 11/3 データC
2 11/4 データD
2 11/5 データE
3 11/3 データF
3 11/5 データG

グルーピングして、各グループ内で連番を採番したい場合のSQL

SELECT
ROW_NUMBER() OVER ( partition by ID order by ID, 日時 ) as num
, ID
, 日時
, 名称
FROM tableA

これで、このような結果を取得できる。


num ID 日時 名称
1 1 11/4 データA
2 1 11/5 データB
1 2 11/3 データC
2 2 11/4 データD
3 2 11/5 データE
1 3 11/3 データF
2 3 11/5 データG


続けて縦横変換。

2012年2月3日金曜日

PowerShellを使う前に

しょっちゅう忘れてしまうので備忘録を兼ねて。

初期状態ではPowerShellのスクリプト(.ps1ファイル)の動作が禁止されています
ps > Get-ExecutionPolicy
を実行すると、「Restricted」などが返ってきます。
この状態ではスクリプトの実行ができません。

スクリプトを実行できるようにするためには以下のコマンドを「管理者権限で起動したPowerShell」上から実行します
ps > Set-ExecutionPolicy RemoteSigned

これでPowerShellのスクリプトが実行できるようになっているはずです

2012年2月2日木曜日

Windows Intune

Windows Intune?
Office 365?

ナニソレウマイノカ?

それぞれ、無料お試し期間30日を過ぎたあとは、月額11ドルと600円らしい。
Exchangeを試したいだけなんだけどな。。。

2011年10月8日土曜日

GAE/Jにてインデックスエラーでデプロイが失敗する場合の対処など


この記事は、下記の素晴らしい記事について、MAC環境での補足を行うものです。

GAE/J使いのためのインデックス削除ツール(y-kawazの日記)
http://d.hatena.ne.jp/y-kawaz/20100416/1271401890


MACの場合は

  1. http://code.google.com/intl/ja/appengine/downloads.html の「GoogleAppEngineLauncher-1.5.4.dmg」をダウンロード、マウントします。
  2. 中にある「GoogleAppEngineLauncher.app」をアプリケーションフォルダに移動
  3. 起動してメニューから「GoogleAppEngineLauncher」ー「Make Symlinks」で「/usr/local/bin」にappcfg.pyのエイリアス?を作成します。
  4. 適当な名前のディレクトリを作成し、その中に「app.yaml」ファイルを作成
  5. 「app.yaml」ファイルの中身はy-kawazさんとこの記事を参照
  6. 「appcfg.py vacuum_indexes 4のディレクトリ」をシェルから打って実行


以上でリンク先と同じように各インデックスのメンテについて聞いてきます。

ありがちなのは、カスタムインデックスが必要なクエリを書いており、ローカルではAutoGenerateのインデックスが効いているのだけれど、自分で定義するインデックス用XMLファイルに記述を写していない場合などです。
(500〜ってなエラーが出ると思います)

そんな場合は、

  1. インデックス削除
  2. カスタムインデックスファイルに記述を追加(Auto〜から移動ですね)
  3. (インデックス一覧からDeletingが消えてから)再度デプロイ

最後の再度デプロイはDeleting中でも一応いけましたが、念のためDeletingが消えてからのデプロイをオススメします。
インデックス削除は上記のケースでひとつだけ消した場合で2〜3分程度で終わりました。

2011年9月7日水曜日

Google Libraries API での書き方

Google Libraries API なんてのがあるんですね。
しかもバージョンを「1」と書いておけば1系の最新がリンクできるという・・・
(1.6と書けば1.6系の最新が取得できる)

開発者向けガイドはこちら(英語)

以下ポイント
  • JQueryを呼ぶだけならAPI Keyは指定しなくてもよい
  • jsapiのリンクを書くときは、typeが先に来てないと全く読み込んでくれない(常識??)
  • google.loadで呼ぶ場合は「google.setOnLoadCallback」で括ってやる必要がある
  • google.load使わないなら
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
    または
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
    と書ける


type が先にこないといけない件
×ダメ
<script src="https://www.google.com/jsapi" type="text/javascript"></script>
◯OK
<script type="text/javascript" src="https://www.google.com/jsapi"></script>

実際に書くとこんな感じ

<!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" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>Google Librries API お試し</title>

<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">

google.load("jquery", "1");
google.setOnLoadCallback(function()
{
  $(function() {
  	//hoge
  });

  //試しにバージョン表示
  alert($.fn.jquery);

});

</script>

</head>
<body>
  <p>contens</p>
</body>
</html>

いろんなライブラリを使うなら、いちいちgoogle.setOnLoadCallbackで括ってられないと思うので、load使わない下記の書き方のほうがいいかも。


<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">

  $(function() {
  	//hoge
  });

  //試しにバージョン表示
  alert($.fn.jquery);

</script>

2011年8月16日火曜日

GAE, Slim3, Scenic3 環境下でAjaxなBlobstore利用

検証コード書いてちゃんと動作したので備忘録的な感じで。
後日丁寧に書く予定。とりあえず箇条書き

前提
・jQuery.upload利用
・コントローラ側は「アップロードURL返す」「Blobstoreアップロードからの戻り直後」「戻り直後からリダイレクトされJSON生成」の3段階用意


1,アップロードボタン押す
2,UploadUrlリクエスト、Jsonで取得しそのままアップロード
3,戻りURLをコントローラを指定しておいて、そこに戻す。そのタイミングではRedirectでなければ戻せない約束事なので一旦別のコントローラにリダイレクト(画像URLを一緒に渡す)
4,別のコントローラは画像URLをJSON化してクライアントに戻す
5,クライアントはAJAXな動きでBLOBSTOREを利用することができる


リクエスト用HTML&JavaScript
<!--
BlobStore直接アップロード検証コード
 -->

<script type = "text/javascript" src="/jquery.upload-1.0.2.js"></script>
<input type="file" name="newprofileimage" id="newprofileimage" />
<input type="button" name="entry" value="登録" onclick='javascript:submitUpload("newprofileimage");' />
<br />
<br />

<script type="text/javascript">
function submitUpload(targetid)
{
 var jsonUrl = "/image/getdirectupdurl";
 var uploadurl = "";

 $.getJSON(jsonUrl, function(urldata) {

  if(urldata)
  {
   uploadurl = urldata.url;

   $('#' + targetid ).upload(
    uploadurl,
	function(data)
	{
	 alert('File uploaded[' + data[0].imageurl + ']');
	 $('#imagearea').html('<img src="' + data[0].imageurl +  '" />');
	},
	'json'
   );
   }
  });
}
</script>

<div id="imagearea">アップロード後の画像が表示される場所</div>

サーバサイドコード
/**
 * BlobStoreへの直接アップロード用URLを取得する.
 * JSON形式にて返す
 * @return
 */
@ActionPath("getdirectupdurl")
public Navigation getDirectUploadUrl()
{
 Navigation nav = null;

 String returnUrl = "/image/dupdcomplete";
 String uploadUrl = service.createUploadUrl(returnUrl); //BlobstoreServiceのcreateUploadUrlを呼び出している

 Map<String,String> jsonData = new HashMap<String,String>();
 jsonData.put("url", uploadUrl);

 try
 {
  nav = this.responseJson(jsonData);
 }
 catch (IOException e)
 {
  e.printStackTrace();
  nav = this.redirect("errorurl");
 }

 return nav;
}


/**
 * Blobstoreに直接アップロードした後、リダイレクトされる場所
 * @param blobUrl
 * @return
 */
@ActionPath("dupdcomplete")
public Navigation directUploadComplete()
{
 Navigation nav = null;

 BlobKey blobKey = service.getBlobKey(request, "newprofileimage");

 nav = this.redirect( "/image/dupdrd?bloburl=" + service.getServingUrl(blobKey));

 return nav;
}

/**
 * Blobstoreに直接アップロードした後リダイレクトされたものをさらにリダイレクトされる場所.
 * @param blobUrl
 * @return
 */
@ActionPath("dupdrd")
public Navigation forDirectUploadRedirect(
  @RequestParam("bloburl") String blobUrl
)
{
 Navigation nav = null;

 if(blobUrl != null && blobUrl.isEmpty() == false )
 {
  List<Map<String,String>> jsonDataList = new ArrayList<Map<String,String>>();

  Map<String,String> jsonData = new HashMap<String,String>();
  jsonData.put("imageurl", blobUrl);
  jsonDataList.add(jsonData);

  try
  {
   nav = this.responseForImageJson(jsonDataList);//この前のエントリの内容
  }
  catch (IOException e)
  {
   e.printStackTrace();
  }
 }

 return nav;
}

2011年8月15日月曜日

GAE/J Slim3でScenic環境下でjQuery uploadを利用&Jsonで戻りデータ取得

結論からいいますと、やっつけデス。。。
コードはかなり美しさにかけて、めっちゃ遅いんちゃう!?くらいの勢いですがとりあえず動きます。

ポイント。

  • jQuery.uploadはjsonでリクエストした際、iframeを利用してシームレスなアップロードを実現するためJsonデータが戻ってくる際のContentTypeが「text/html」でないと<pre>タグがついてしまってうまく処理できない
  • Scenic側でNavigationオブジェクトを渡す際、responseJsonで渡すと強制的に「text/JavaScript」になってしまう

そらそうや、ってお話です。JSONですし。
responseJsonした後にContentTypeを強制的に変えれればなんとかなりそうだったんですが、全く変えれず。。。setContentTypeは意味をなさない。
responseJsonしたあとにキャッシュ的なんと_out〜とかいう変数に1が入ってるのがポイントなんじゃろか?

response( String ) でJson文字列を自分で作って渡してやればいいんじゃないか!?と思いましたが、強制的に「text/Plain」になりました。

結果、responseDirect( byte[], String(=ContentType) ) を使うことにしました。
以下、ざっくり手順を。


  1. まず、StringBufferなどでJSON文字列を作成します。
  2. 次にStringBuffer.toString.toCharArrayでchar[]配列を取り出します
  3. char[]配列の長さでbyte[]配列を作成
  4. ひたすらchar[]配列をループして1つずつbyte[]配列にコピー!!!(これなんかええのんないかなー?)
  5. 最後にresponseDirectに渡してやって完了。

あ、ContentTypeは「"text/html; charset=UTF-8"」みたいな感じで。

とりあえず動きました。ええ、とりあえず。。。

まぁここの部分はファイルアップロードで1個画像を扱うだけなんで、もういいかな?とも思ったり。
まぁ後で時間できたときに見直しですねぇ。今は動けばいいや(涙

2011年8月9日火曜日

GAE/J デプロイにて 409 conflict が出た場合の対処

本日のGAEの障害?(19時〜のやつ)のタイミングでデプロイしてまして、その時は結局デプロイできなかったので数時間経ってから再度デプロイを行いました。
すると、失敗した時の何かが残っていたのか
「Unable to update app:〜ほにゃらら〜409 Conflict」
と表示されました。
これの対処について備忘録もかねて記述しておきます

メッセージには
「Another transaction by user〜」
と出ており
「That user can undo the transaction with "appcfg rollback".」
てなメッセージが出ていました。

調べてみると、コマンドラインから上記コマンドを実行してやればよいようです


machine:~ hoge$ cd /Applications/eclipse/plugins/com.google.appengine.eclipse.sdkbundle_1.5.2.r36v201107221751/appengine-java-sdk-1.5.2/bin/
machine:bin hoge$ ls -al
total 32
drwxr-xr-x   6 hoge  hoge  204  8  2 07:27 .
drwxr-xr-x  14 hoge  hoge  476  8  2 07:27 ..
-rw-r--r--   1 hoge  hoge  276  7 22 15:02 appcfg.cmd
-rw-r--r--   1 hoge  hoge  582  7 22 15:02 appcfg.sh
-rw-r--r--   1 hoge  hoge  368  7 22 15:02 dev_appserver.cmd
-rw-r--r--   1 hoge  hoge  670  7 22 15:02 dev_appserver.sh

なんかそれっぽいのがいました。実行権限を付与して、実行してみる
machine:bin hoge$ ./appcfg.sh rollback /myproject/war/
Reading application configuration data...
2011/08/09 22:26:06 com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
####: Successfully processed /myproject/war/WEB-INF/appengine-web.xml
2011/08/09 22:26:06 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
####: Successfully processed /myproject/war/WEB-INF/web.xml
2011/08/09 22:26:06 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
####: Successfully processed /myproject/war/WEB-INF/cron.xml
2011/08/09 22:26:06 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
####: Successfully processed /myproject/war/WEB-INF/queue.xml
2011/08/09 22:26:06 com.google.apphosting.utils.config.IndexesXmlReader readConfigXml
####: Successfully processed /myproject/war/WEB-INF/appengine-generated/datastore-indexes-auto.xml
Beginning server interaction for myproject...
0%  on backend null.
Email: *****@gmail.com
Password for *****@gmail.com: *****
Success.
Cleaning up temporary files...
machine:bin hoge$ 

EmailとPasswordはデプロイする権限のあるユーザを入力しました
この後、通常通りにデプロイすることでデプロイが完了しました

2011年7月12日火曜日

【備忘録】gitでローカル・リモートブランチの削除(Assembla)

最近備忘録ばかりですが、後で助かるんですよね。
※あの時のオレは別人的な
※そういやポタアンその後も書かないと・・・汗


gitでローカルブランチ・リモートブランチを削除する方法です。

まず、GUIでGitboxを利用していますが、それらしいコマンドがありませんでした。

しかたないので、MacOSXでこのへんからDMGファイルを落としてきて、コマンドラインから作業しました。
あ。x86-64をダウンロードしています(MacBookAir、OSX10.6.8)

リポジトリのディレクトリへ移動
$ cd /dev/sources/project/

ブランチ一覧を表示
$ git branch -a
  master
* origin
  remotes/Assembla/master
  remotes/Assembla/origin

今回はoriginだけを残したいので、その他を削除
ローカル「master」を削除
$ git branch -D master
Deleted branch master (was e61420b).

一覧を表示して確認してみる(ローカルmasterが消えている)
$ git branch -a
* origin
  remotes/Assembla/master
  remotes/Assembla/origin

リモートのmasterを削除する
$ git push Assembla :master

warning: Deleting the current branch can cause confusion by making the next
warning: 'git clone' not check out any file.
warning: 
warning: You can set 'receive.denyDeleteCurrent' configuration variable to
warning: 'refuse' in the remote repository to disallow deleting the current
warning: branch.
warning: 
warning: You can set it to 'ignore' to allow such a delete without a warning.
warning: 
warning: To make this warning message less loud, you can set it to 'warn'.
warning: 
warning: Note that the default will change in a future version of git
warning: to refuse deleting the current branch unless you have the
warning: configuration variable set to either 'ignore' or 'warn'.
warning: deleting the current branch

To git@git.assembla.com:プロジェクト名
 - [deleted]         master

一覧を表示すると、リモートも削除できている
$ git branch -a
* origin
  remotes/Assembla/origin

ほんとはmasterを消すのはよくないかも?
まぁ今回はoriginを中心に残したかったってことで。

2011年7月7日木曜日

【備忘録】Slim3のModelRefでクエリ

ModelRefで問い合わせしたいなーと思い、ネットの海を彷徨ってみつけた情報。
とってもこれ貴重やわぁ。。。助かります。

そんなわけで備忘録。

参考サイト:ふじやん雑記帳さん
※中断から下あたりにある「ModelRefで問い合わせ」

ModelRefで問い合わせ

  • ModelRefAttributeMetaを使えば、ModelRefで問い合わせが可能
  • 例えば、部署(1)対従業員(多)の場合、
// 部署
@Model(schemaVersion = 1)
public class Dept implements Serializable {
}

// 従業員
@Model(schemaVersion = 1)
public class Employee implements Serializable {
    private ModelRef<dept> deptRef = new ModelRef<dept>(Dept.class);
    
    public ModelRef<dept> getDeptRef() {
        return deptRef;
    }
}
とある場合に、「ある部署に所属する従業員全員」という問い合わせは下記のコードで可能。
Key deptKey = [ある部署のKey];

EmployeeMeta employeeMeta = new EmployeeMeta();
ModelRefAttributeMeta<employee, ModelRef<Dept>, Dept> refMeta = employeeMeta.deptRef;
List<employee> list = Datastore.query(Employee.class).filter(refMeta.equal(deptKey));
  • これを使えば、親子関連を持たせたい場合に、entityGroupを使わなくても良い。

2011年6月15日水曜日

備忘録:Slim3でのデータストア暗号化IFについて

備忘録です。

Slim3のモデル暗号化についての内容です。

※ハマっちゃいました。filterの書き方大事。
    ☓ MyModel userData = Datastore.query(t)
          .filter("myname", FilterOperator.EQUAL, "hoge").asSingle();
    ◯ MyModel userData = Datastore.query(t)
          .filter( t.myname.equal("hoge")).asSingle();
なんでオレ、上の書き方してたんやろ・・・


以下、Slim3公式の説明より引用

元となったディスカッションはこちら
http://groups.google.com/group/slim3-user-japan/browse_thread/thread/4999dfd7553c5f38?pli=1

Cipher Text

https://sites.google.com/site/slim3appengine/slim3-datastore/defining-data-classes/cipher-text

個人情報や機密情報を安全に扱うために暗号化をする必要がある場合があります。Slim3は暗号化機能をサポートしています。暗号化アルゴリズムと対象のフィールド型は次の通りです:

  • アルゴリズム: AES (Advanced Encryption Standard)
  • 暗号利用モード(Block cipher mode): CBC (Cipher Block Chaining)
  • 暗号鍵: 128bit (192 bit と 256bit はサポートしていません)
  • フィールド型: java.lang.String, com.google.appengine.api.datastore.Text

Slim3でBigtableのデータを暗号化するのはとても簡単です:

  1. 対象のフィールドに @Attribute(cipher = true) アノテーションを使用します。
  2. 暗号鍵をSlim3 Datastoreに設定します。
  3. Slim3 Datastoreを使用した操作をします。

モデル定義の例です:

@Attribute(cipher=true) private String myString; @Attribute(cipher=true, lob=true) private String myLobString; @Attribute(cipher=true) private Text myText;

暗号鍵を設定する例です:

Datastore.setLimitedCipherKey("hogehogehogehoge"); Datastore.setGlobalCipherKey("hogehogehogehoge");

setLimitedCipherKeyは、スレッドローカルの暗号鍵を設定します。その鍵は現在のスレッド内の暗号化・復号化で使用されます。

FrontControllerは各コントローラを呼び出した後に暗号鍵をクリアします。このメソッド(setLimitedCipherKey)はコントローラー毎に異なる暗号鍵を使用する場合を想定しています。

setGlobalCipherKeyは暗号鍵をスタティック領域に設定します。その鍵は全てのスレッドで使用されます。FrontControllerはsetGlobalCipherKeyメソッドで設定された暗号鍵はクリアしません。このメソッドはシステム内で固定の暗号鍵を使用する場合を想定しています。

LimitedCipherKeyの方がGlobalCipherKeyよりも優先されます。さらに両方の値が未設定の場合、例外がスローされます。128bit(16文字)の暗号鍵を設定してください。

GlobalCipherKeyの値は appengine-web.xml で指定できます:

<system-properties>  <property name="slim3.cipherGlobalKey" value="hogehogehogehoge"/> </system-properties>

暗号化と復号化は自動的に行われます。

暗号化フィールドに対して、インメモリのフィルタとソートは全てサポートされています。

暗号化フィールドに対するデータストアクエリのフィルタには制限があります。以下のフィルタがサポートされています:

  • EqualCriterion
  • IsNotNullCriterion
  • NotEqualCriterion

データストアクエリのソートはサポートされていません。

2011年6月13日月曜日

備忘録:Scenic3でのJSON利用

備忘録です。

Scenic3では「ScenicPage.responseJson」を呼び出すことで、データをJSONとして返すことができます。

ただ、環境がそのままではエラーになってしまうので、「JSONIC」を入れてやります

  1. サイトからZipファイルをダウンロードします
  2. 解凍してあらわれた「jsonic-*.*.*.jar」を「war/lib/」「war/WEB-INF/lib」に配置します
  3. ビルドパスに「jsonic-*.*.*.jar」を追加します
※7/20修正

以上で「ScenicPage.responseJson」を使う環境が整います。
※ほんとSlim3+Scenic3はラクチン

今回動作確認した環境
  • Slim3:1.0.11
  • Scenic3:0.4.2
  • JSONIC:1.2.5

2011年6月9日木曜日

Eclipseでの接頭辞に「_(アンダーバー)」がついた際のsetter・getter

引用です。
http://andante0727.blog81.fc2.com/blog-entry-237.html

private String _name;
メニューの「ソース」→「getter および setter の生成」で上記のフィールドのゲッターセッターを生成すると以下のソースになる。
public String get_name() {
   return _name;
}

public void set_name(String _name) {
   this._name = _name;
}

これをgetNameやsetNameにしたい場合、
メニューの「ウィンドウ」→「設定」→「Java」→「コードスタイル」の「変数命令規則」の欄の「フィールド」を編集し「接頭語リスト」に「_」(アンダーバー)を入れて適用してからセッター、ゲッターを生成する。
public String getName() {
   return _name;
}

public void setName(String name) {
   _name = name;
}

いやほんと、Eclipseって便利ですねぇ

2011年6月8日水曜日

FrontControllerを使いたいときはデフォルトコンストラクタを

Slim3において、ふつーにマッピングしてる間は何も考えずにControllerクラスを生成すればOK。

ただし、URLマッピングしたりとかでControllerクラスに「引数つきコンストラクタ」なんか定義しちゃうと、そのままでは通常の名前からのController呼び出し( /aaa → slim3route.AaaController )が失敗しちゃいます。

他のコンストラクタを書いちゃったときは、暗黙の際に自動で定義されているデフォルトコンストラクタ(引数なしコンストラクタ)を定義してやりましょう。

つまり、
public class AaaController extends Controller
{
}
か、
public class AaaController extends Controller
{
	//これが必ず存在するようにする!
	public AaaController()
	{
	}

	public AaaController( type arg)
	{
	}
}
でないとダメと

2011年6月7日火曜日

Slim3でのURLマッピング

再度確認しました
 結論としては、@higayasuo さんがお話されていた通り「当たり前の挙動」を示しているだけなのかなーと。
 サーブレットに詳しくない自分としてはちょっとツラい感じです

※6/9修正しました。ソース部分をasXxxを使った形に書き換え&こちらを参考にrequestなどの取得について修正

確認しました。
POSTが噛んだときの、マッピングの判断条件の考え方が間違っていました。

RequestMapのgetは、内部でHttpRequestServletのgetAttribute()を呼び出します。
で。
  • Formのenctypeが「multipart/form-data」の場合
    POSTがない場合はこれでGETの引数としてのマッピング先(?key={key})が取得できるのですが、POSTデータがある場合はこの値がPOSTの内容になるようです。
    GETの値はどこかというと、「request.getParameter("key")」で取得できる模様

  • Formのenctypeが「text/plain」の場合
    RequestMapのgetは、GETの値、つまりURLマッピングの「?key={key}」が入っています。
    POSTの値は・・・getParameterでも取れませんでした。

とりあえずURLマッピングを絡めてPOSTを扱う場合は、「multipart/form-data」にしておくことをオススメします。
以下修正しました。
ひと通り動く版。POSTは「multipart/form-data」で。

参照リンク
http://sites.google.com/site/slim3documentja/documents/slim3-controller/url-mapping

Slim3は通常、
リクエストURLパス コントローラ
/ slim3.rootpackage.controller.IndexController
/xxx slim3.rootpackage.controller.XxxController
/xxx/ slim3.rootpackage.controller.xxx.IndexController
/xxx/yyy slim3.rootpackage.controller.xxx.YyyController
/xxx/yyy/zzz slim3.rootpackage.controller.xxx.yyy.ZzzController
※slim3.rootpackage は web.xml で定義されています。
というようにマッピングルールに基づいて、実行するコントローラが決まります。

リンク先に書いてあるのをちゃんと読んでいなかったのですが、このURLマッピングはある程度制御することができます

コントローラを取得する前にリクエストURLパスの書き換えをすることもできます。
URLリライティングを使うには slim3.rootpackage.controller.AppRouter クラスを先に定義する必要があります。

とのこと。実際にやってみましょう

slim3.rootpackageが「jp.tyato.test」だとすると、「jp.tyato.test.controller.AppRouter」クラスを生成します
  • 「jp.tyato.test.controller」のパッケージを用意する
  • 「jp.tyato.test.controller」配下に「AppRouter」クラスを生成。内容は次のような感じ

6/8修正
package jp.tyato.test.controller;

import org.slim3.controller.router.RouterImpl;

public class AppRouter extends RouterImpl
{
    Public AppRouter()
    {
        addRouting("/xxx/{key}", "/xxx?key={key}");
    }
}

次にIndexControllerクラスを生成します
  • 「build.xml」を開き、アウトラインから「gen-controller-without-view」を選択、実行から「1 Ant ビルド」を実行する
  • 名前の入力を求められるので、「/xxx/Index」と入力
  • 「jp.tyato.test.controller.xxx」配下に「IndexController.java」が作成される
  • メソッド「run」の中を書き換える

final String KEY_NAME = "key";

@Override
public Navigation run() throws Exception
{
    Navigation result = null;

    try
    {
        String keyword = asString(KEY_NAME);

        if(keyword == null)
        {
            //getParameterで再度試行
            keyword = request.getParameter(KEY_NAME);
        }

        if( keyword != null )
        {
            keyword = keyword.toLowerCase();
        }

        //Factoryパターン用コントローラ親クラスの定義
        Controller con = this.GetFromFactory(keyword);

        result = con.runBare();
    }
    catch (Throwable e)
    {
        e.printStackTrace();
    }

    return result;
}

public Controller GetFromFactory(String keyword)
{
    Controller con = null;

    try
    {
        if (keyword == null || keyword.isEmpty() || keyword.startsWith("index") )
        {
            //keywordがnull、空文字、indexで始まる文字のいずれかである場合、Index表示処理
            con = new ForDefaultController();
        }
        else if (keyword.equals("yyy"))
        {
            //keyが「yyy」である場合、yyy用の処理へ
            con = new YyyController();
        }
        else if (keyword.equals("zzz"))
        {
            //keyが「zzz」である場合、zzz用の処理へ
            con = new ZzzController();
        }
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }

    return con;
}
後は実際に処理を行うControllerクラスを実装します。
この場合は「ForDefaultController」「YyyController」「ZzzController」について
  • 「build.xml」を開き、アウトラインから「gen-controller-without-view」を選択、実行から「1 Ant ビルド」を実行する
  • 名前の入力を求められるので、「/xxx/???」と入力
  • 「jp.tyato.test.controller.xxx」配下に「???Controller.java」が作成される
???の部分をそれぞれ「ForDefault」「Yyy」「Zzz」に置き換えて読んでください。

しかしこのままですと、各Controllerを呼び出してもそれぞれの変数である
「protected ServletContext servletContext」
「protected HttpServletRequest request」
「protected HttpServletResponse response」
がnullのままです。

ですので、利用する前に各Locaterのgetを使って、値を取得してやります。

YyyControllerであれば、こんな感じです
package jp.tyato.test.controller.xxx;

import org.slim3.controller.Controller;
import org.slim3.controller.Navigation;
import org.slim3.util.RequestLocator;
import org.slim3.util.ResponseLocator;
import org.slim3.util.ServletContextLocator;

public class YyyController extends Controller
{
    @Override
    public Navigation run() throws Exception
    {
        this.CheckParentMember();

        //以降、実処理
    }

    protected void CheckParentMember()
    {
        if( request == null )
        {
            request = RequestLocator.get();
        }

        if( response == null )
        {
            response = ResponseLocator.get();
        }

        if( servletContext == null )
        {
            servletContext = ServletContextLocator.get();
        }
    }
}
「ForDefaultController」「ZzzController」についても同様に定義してやります。
※まぁ実際に使うときに〜Locator.get()を呼び出してやればいいんですが。

こうすることで、
  • Indexに一旦集約
  • その都度の値により狙った処理を行うように制御
といったことが可能になります。

2011年6月3日金曜日

有料でホスティングサービスを使おうかな?

最近、「ホスティングサービス」を使い始めました。

今使っているのは「Assembla」
他には「Github」とか「LaunchPad」とかありますね。

個人的な仕事でも使いたいなーと考えていて、そうするとPrivateRepositoryが欲しくなるわけです。

無料だと現時点ではAssembla一択なんですが、有料だとまた違う。
gitで管理していることもあって、今回は「Github」「Assembla」で比較してみました。



・・・う。なんかいろいろおかしいですね。。。
Githubの容量が全部0.3になっちゃってるのと、Assemblaのリポジトリ数がSpaceの値になってるのとがアレな感じ。

詳細なデータを突っ込まんとなんともいえませんが、Githubの12ドルプランがよさげ?
まぁ、正しい使い方ではありませんが、ブランチごとに別プロジェクトを管理する、なーんてことをすればリポジトリ数は関係なくなりますけど。

そんなわけで追調査。
Assemblaの無料トライアルが20日後くらいに切れるので、それまでには決めたいな



〜追記その1〜
どうやらGithubの容量は「一応制限はあるが、実際には使える」っぽいです。

ただ、濫用を防ぐ目的で、soft limitなるものが設定されている。Soft limitを越えると、こんな感じで警告っぽい表示が出る。ただ、だからといって、特に何かが起こるわけではない。

2011年6月2日木曜日

Eclipseの「未処理のイベント・ループ例外」への対応

Eclipseの「未処理のイベント・ループ例外」が発生した場合、以下の処理を行うことで復旧。

  1. ワークスペース配下の「.metadata」フォルダを丸ごと削除(MACではファインダーからは見えないので、ターミナルから rm -rf を実施)
  2. ワークスペースに既存プロジェクトをインポート
  3. 「-clean」にて起動

2011年5月26日木曜日

GAE/JではConvertは使えない

GAE/Jな環境でConvert使おうとしたら

com.sun.tools.javac.util.Convert is not supported by Google App Engine's Java runtime environment


なんだってさ。
つか、なぜConvertを使おうとしたんだオレ。。。

Integer.getInteger(string arg0)


を使うことで解決。

2011年5月25日水曜日

備忘録(GAE1.5.0でのSlim3について)

GAEを1.4系から1.5.0にアップデートした際、Slim3を1.5.0対応版の「1.0.11」に更新する必要がある

Slim3更新履歴
http://sites.google.com/site/slim3documentja/release-notes

以下、引用

Version 1.0.11 - May 14, 2011

org.slim3.datastore.ModelQuery.asQueryResultList(ModelQuery.java:295)でNullPointerExceptionが発生する問題を修正しました:
http://code.google.com/p/slim3/issues/detail?id=81
App Engine SDK 1.5.0のローカルテストでNullPointerExceptionが発生する問題を修正しました:
http://code.google.com/p/slim3/issues/detail?id=87
AppEngine SDK 1.5.0でテストしました