SyntaxHighlighter

ラベル 備忘録 の投稿を表示しています。 すべての投稿を表示
ラベル 備忘録 の投稿を表示しています。 すべての投稿を表示

2013年2月22日金曜日

ASP.NET開発サーバーでのMIME指定

ASP.NETの開発時に、デバッグはIISを使ったりVisualStudioが勝手に(?)立ち上げてくれる「ASP.NET開発サーバー」を使ったりできるわけです

で。

HTML5のApplicationCacheなどを使いたいなーってときはMIMEの追加が必要だったりします。
IISならサクッと追加できますが、VisualStudioが起動する「ASP.NET開発サーバー」でのMIMEタイプ追加はどないすんねん?
というお話。

IISで追加した際に生成されるweb.configをみれば答えが書いてました。
こんな感じで追加してやればOK。
(その他タグを省略してるので適宜置き換えて読んでください)
<configuration>
  <system.webServer>
    <staticContent>
      <mimeMap>
        <mimeMap fileExtension=".appcache" mimeType="text/cache-manifest" />
      <mimeMap>
    <staticContent>
  <system.webServer>
<configuration>
フタを開けてみればそらそうや、てな感じ
でもこういうのん見つけるまでが大変だよねぇ。。

2013年2月10日日曜日

【備忘録】Numbersのショートカット


徐々に仕事もNumbersメインに移行中。
ショートカットはExcelとは違うので備忘録的な感じで。
(利用者が少ない?それともヘルプに載ってるのか、ググっても見つからんので。。)
  1. WindowsでExcelだと「F2」でセルへの入力状態に移行できますが、Numbersでは「Option+Enter」
  2. セル内での改行はExcelだと「Alt+Enter」だけれど、Numbersでは「Option+Enter」

コツコツ追加していきたいな(*´ω`*)

2013年2月7日木曜日

【備忘録】SQLServerでのトランザクションログの切り捨てでファイルが縮小し ない場合(2008R2)

sys.databasesのlog_reuse_wait_descがLOG_BACKUPな場合、バックアップが適切に行われていないケースがある。

このようなバックアップが適切に行われていない状況でトランザクションログのファイルサイズ縮小を行いたい場合、バックアップ(トランザクションログの切り捨て)と圧縮を2回繰り返す必要がある

1,1回目のバックアップで、切り捨てが行われる

2,次に圧縮をかけると、ログファイル内の仮想ログを先頭に集める(サイズ縮小は行われない)
このタイミングでフラグがLOG_BACKUPに変わる(再度バックアップが必要)

3,2回目のバックアップで、LOG_BACKUPからNOTHINGにフラグが切り替わる(圧縮可能に?)

4,圧縮を実施すると、ファイルが縮小される

たぶん、こんな感じ。

なお、バックアップは「NUL」へのバックアップでも事象は同じ

詳しくは、Microsoftの「ログの切り捨てが遅れる原因となる要因」を参照

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のスクリプトが実行できるようになっているはずです

2011年12月17日土曜日

Artwork Gofer が動かなくなった

iTunes on MAC で、アルバム画像を取得するために「Artwork Gofer」ってのを使ってたんです。

ところが、久しぶりに使ってみるとエラーを吐いて全く画像を取得できない・・・

調べてみると公式サイトにこんなことが書いてあった

Note. The application will stop working on or around Oct 25 2011 due to the changes in the Amazon requirements for their API use.
えええ!?2011年10月25日からはAmazonのAPI仕様が変わるから、動かなくなるぜ!・・・だそうで。。なんて清々しいお知らせ・・・

新しいAPIに対応してる別のソフトはないだろうか・・・と調べてみたところ、ありました


ほほーこれは便利!
あ。これはMac用ね。Windowsはどっか別で探してくださいませ。。。

◯インストール方法

  1. 上記サイトのDownloadからZipファイルをダウンロード
  2. 解凍して出てきたAppファイルをApplicationフォルダに移動


◯使い方

  1. AAAを起動
  2. iTunesでアルバム画像を取得したい曲を選択
  3. AAA側で「Get Albums」ボタンを押す
  4. なんかいっぱい画像が出てくる
  5. 「Add Immediately」ボタンを押すと反映される


何が便利って、手軽さと曲を一曲選択して検索するだけで、同じアルバムが一括で設定できること。

これはいいソフトだなー(*´ω`*)

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月19日日曜日

VARDIA S-303でリモコンが効かない




ブログ移転先の同記事へ 0 秒後にジャンプします。



自動的にジャンプしない場合は、こちらをクリック




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

備忘録:GAEのローカルでの管理コンソール

備忘録。GAEはEclipseなどでローカルデバッグができます。
この際の、DataStoreなどをあれやこれやするための管理コンソールは
http://localhost:8888/_ah/admin/
にアクセスすればOK

あ。
Scenic3使ってたりする場合は、AppUrlsに
excludes("/_ah/*");
を書いておくのを忘れないように。

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に一旦集約
  • その都度の値により狙った処理を行うように制御
といったことが可能になります。

備忘録・Scenic3

いつも所在を忘れてしまうので備忘録として記録。

Scenic3
http://d.hatena.ne.jp/shuji_w6e/20110116/1295184829

もうちょい使い込まないと。。。