Google App Engine for Java (GAE/J) で Bigtable CRUDを行う Flex アプリ作成法
週末、GoogleAppEngine for Java (GAE/J)で動作し BigtableでCRUDを試せる簡易Flexアプリを作成してみた。
以下に作成法を紹介する。
RemotingにはBlazeDS を使用。
http://koichi-hamada.appspot.com/
作成において、次のサイトを参考にした。今回、参考サイトになかった Update、Deleteも追加している。
Flex Meets Google App Engine
Flex Remoting on Google App Engine
前提/環境
以下、GAE/J Eclipse plugin、Flex Builder plugin のEclipseインストールは完了している前提で進める。
インストールは次をご参考に:
Google App Engine for Javaを使ってみよう!(1)Google Plugin for Eclipse
Adobe Flex Builder 3.0.2 Professional Eclipse Plug-inのダウンロード
構築環境:
Java: 1.6.0_12
Eclipse: 3.4.2
Google App Engine Java SDK: 1.2.1
Google Plugin for Eclipse 3.4: 1.0.1
Google Web Tool Kit SDK: 1.6.4
Flex Builder Professional Eclipse plug-in: 3.0.2
作成手順
Server側 (Java)
gaeblog.server packageに次の3クラスを追加。
Content.java
Big Tableに永続化するEntity Class。Blog内容を表す。
package gaeblog.server; import javax.jdo.annotations.IdGeneratorStrategy; import javax.jdo.annotations.PersistenceCapable; import javax.jdo.annotations.Persistent; import javax.jdo.annotations.PrimaryKey; @PersistenceCapable public class Content { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) Long id; @Persistent private String title; @Persistent private String comment; //getters, setters... }
BlogService.java
Big TableでのCRUDのService Interface。
package gaeblog.server; import java.util.List; public interface BlogService { public boolean addContent(Content content); public boolean updateContent(Content content); public boolean deleteContent(Integer id); public List<Content> getContentList(); }
BlogServiceImpl.java
BlogServiceの実装Class。
package gaeblog.server; import java.util.List; import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import javax.jdo.annotations.Transactional; public class BlogServiceImpl implements BlogService{ //Persistence Manager private static final PersistenceManagerFactory factory = JDOHelper.getPersistenceManagerFactory("transactions-optional"); //PersistenceManager pm; @Override @Transactional public boolean addContent(Content content) { PersistenceManager pm = factory.getPersistenceManager(); try{ if(content.getId().intValue()==0){ content.setId(null); } pm.makePersistent(content); return true; }catch (Exception e){ return false; }finally{ pm.close(); } } @Override @Transactional public boolean updateContent(Content content){ PersistenceManager pm = factory.getPersistenceManager(); try{ Content persistenctContent = pm.getObjectById(Content.class, content.getId()); persistenctContent.setTitle(content.getTitle()); persistenctContent.setComment(content.getComment()); return true; }catch (Exception e) { return false; }finally{ pm.close(); } } @Override @Transactional public boolean deleteContent(Integer id){ PersistenceManager pm = factory.getPersistenceManager(); try{ Content persistenctContent = pm.getObjectById(Content.class, id); pm.deletePersistent(persistenctContent); return true; }catch (Exception e) { return false; }finally{ pm.close(); } } @Override @Transactional @SuppressWarnings("unchecked") public List<Content> getContentList() { PersistenceManager pm = factory.getPersistenceManager(); String query = "select from " + Content.class.getName(); return (List<Content>) pm.newQuery(query).execute(); } }
Client側(Flex)
次にClient(Flex)側の作成。
プロジェクト上で右クリックし、「Flexプロジェクトの特性」⇒「Flexプロジェクトの特性を追加」。
出力先は "war" にする。(ビルドされたファイルがサーバーにDeployされる)
SWFビルドエラーが出る。
エラーを右クリック⇒「HTMLテンプレートの再作成」を選択し解消する。
Content.as
gaeblog.client packageに、
Server側のContent.java とBindさせるAction Scriptを作成。
package gaeblog.client { [Bindable] [RemoteClass(alias = "gaeblog.server.Content")] public class Content { public var id:int; public var title:String; public var comment:String; public function Content(){} } }
war/WEB-INF/web.xmlへの追加
Remoting用のSpring設定。
war/WEB-INFフォルダ内の web.xmlの web-app タグ内に設定追加。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- Remoting用 Spring設定追加:ここから --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/webAppContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>testdrive</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>testdrive</servlet-name> <url-pattern>/messagebroker/*</url-pattern> </servlet-mapping> <!-- Remoting用 Spring設定追加:ここまで --> <!-- Default page to serve --> <welcome-file-list> <welcome-file>GaeBlog.html</welcome-file> </welcome-file-list> <!-- Servlets --> <servlet> <servlet-name>greetServlet</servlet-name> <servlet-class>gaeblog.server.GreetingServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>greetServlet</servlet-name> <url-pattern>/gaeblog/greet</url-pattern> </servlet-mapping> </web-app>
war/WEB-INF/webAppContext.xmlの新規作成
BlazeDSでのchannel、destinationの設定。
上記のweb.xmlで Spring の ContextLoaderListenerに渡している webAppContext.xmlを次のように作成。
BlogServiceImplをSpring beanとし、BlogDestinationという名で、destinationに設定している。
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:flex="http://www.springframework.org/schema/flex" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/flex http://www.springframework.org/schema/flex/spring-flex-1.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <!-- channel設定 --> <flex:message-broker> <flex:message-service default-channels="my-streaming-amf,my-longpolling-amf,my-polling-amf" /> </flex:message-broker> <!-- Big Tableでの CRUD実装した Service Classを設定--> <bean id="blogBean" class="gaeblog.server.BlogServiceImpl"/> <flex:remoting-destination destination-id="BlogDestination" ref="blogBean" /> </beans>
war/WEB-INF/appengine-wab.xmlへの追加
GAE/JでSessionを使えるよう、appengine-web.xmlに、
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>koichi-hamada</application> <version>1</version> <!--追加: Sessionを使えるようにする --> <sessions-enabled>true</sessions-enabled> <!-- Configure java.util.logging --> <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> </system-properties> </appengine-web-app>
設定
war/WEB-INF下にflexフォルダを作成しflex設定ファイルを置く。
これらの設定ファイルは参考サイトのサンプルプロジェクトからも取得できる。
Flex UI作成:GaeBlog.mxml
GaeBlog.mxmlで次のUIを作成。
その内容は次。
RemoteObjectを、 destinationを上記の"BlogDestination"で設定。
endpointは、Local実行用に"http://localhost:8080/messagebroker/amf"で設定。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init()"> <mx:Label x="40" y="23" text="title"/> <mx:Label x="40" y="49" text="comment"/> <mx:TextInput x="105" y="21" id="titleText" width="181" text="{grid.selectedItem.title}"/> <mx:TextInput x="105" y="47" id="commentText" width="181" text="{grid.selectedItem.comment}"/> <mx:Button x="294" y="10" label="insert" click="createContent()" width="66"/> <mx:Button x="294" y="36" label="update" click="updateContent()"/> <mx:Button x="294" y="60" label="delete" click="deleteContent()" width="66"/> <mx:DataGrid id="grid" x="43" y="90" dataProvider="{blogcontents}" width="317" height="177"> <mx:columns> <mx:DataGridColumn headerText="Title" dataField="title"/> <mx:DataGridColumn headerText="Comment" dataField="comment"/> <mx:DataGridColumn dataField="id" visible="false"/> </mx:columns> </mx:DataGrid> <!-- For GAE Server <mx:RemoteObject id ="remoteObject" result="showContentList(event)" destination="BlogDestination" endpoint="http://koichi-hamada.appspot.com/messagebroker/amf"/> --> <!-- For Local --> <mx:RemoteObject id ="remoteObject" result="showContentList(event)" destination="BlogDestination" endpoint="http://localhost:8080/messagebroker/amf"/> <mx:Script> <![CDATA[ import mx.binding.utils.BindingUtils; import gaeblog.client.Content; import mx.collections.ArrayCollection; import mx.rpc.events.ResultEvent; import mx.controls.Alert; [Bindable] public var blogcontents:ArrayCollection; public function init():void{ remoteObject.getContentList(); } public function createContent():void{ var content:Content=new Content(); content.title=titleText.text; content.comment=commentText.text; remoteObject.addContent(content); } public function updateContent():void{ if(null!=grid.selectedItem){ var content:Content=new Content(); content.id=grid.selectedItem.id; content.title=titleText.text; content.comment=commentText.text; remoteObject.updateContent(content); } } public function deleteContent():void{ if(null!=grid.selectedItem){ remoteObject.deleteContent(grid.selectedItem.id); } } public function showContentList(event:ResultEvent):void{ if(event.result!=null){ if(event.result==false){ Alert.show("failed"); }else if (event.result==true){ Alert.show("successful"); remoteObject.getContentList(); }else{ blogcontents=event.result as ArrayCollection; } } } ]]> </mx:Script> </mx:Application>
war/WEB-INF/libにJARの追加
war/WEB-INF/libに、
Spring BlazeDSに必要なJARを追加する。
こちらも、参考サイトのサンプルプロジェクトから取得できる。
追加後の war/WEB-INF/libの中身は次:
antlr-3.0.1.jar
aopalliance.jar
appengine-api-1.0-sdk-1.2.1.jar
aspectjrt.jar
aspectjweaver.jar
backport-util-concurrent.jar
cglib-nodep-2.1_3.jar
commons-codec-1.3.jar
commons-httpclient-3.0.1.jar
commons-logging.jar
concurrent.jar
datanucleus-appengine-1.0.1.final.jar
datanucleus-core-1.1.0.jar
datanucleus-jpa-1.1.0.jar
flex-messaging-common.jar
flex-messaging-core.jar
flex-messaging-opt.jar
flex-messaging-proxy.jar
flex-messaging-remoting.jar
geronimo-jpa_3.0_spec-1.1.1.jar
geronimo-jta_1.1_spec-1.1.1.jar
gwt-servlet.jar
jackson-core-asl-0.9.9-6.jar
jdo2-api-2.3-SNAPSHOT.jar
org.springframework.aop-3.0.0.M3.jar
org.springframework.asm-3.0.0.M3.jar
org.springframework.aspects-3.0.0.M3.jar
org.springframework.beans-3.0.0.M3.jar
org.springframework.context-3.0.0.M3.jar
org.springframework.context.support-3.0.0.M3.jar
org.springframework.core-3.0.0.M3.jar
org.springframework.expression-3.0.0.M3.jar
org.springframework.flex-1.0.0.RC1.jar
org.springframework.jdbc-3.0.0.M3.jar
org.springframework.jms-3.0.0.M3.jar
org.springframework.transaction-3.0.0.M3.jar
org.springframework.web-3.0.0.M3.jar
org.springframework.web.servlet-3.0.0.M3.jar
spring-security-acl-2.0.4.jar
spring-security-catalina-2.0.4.jar
spring-security-core-2.0.4.jar
spring-security-core-tiger-2.0.4.jar
spring-security-taglibs-2.0.4.jar
xalan.jar
GAE ServerへのDeploy前に行うべきこと
GAE ServerにDeployする前、GAE Server動作用に次の補正が必要。
GaeBlog.mxml
RemotingObjectのendpointを
Local実行用の"http://localhost:8080/"からGAE Serverのものに修正。
例:"http://koichi-hamada.appspot.com/の場合
<mx:RemoteObject id ="remoteObject" result="showContentList(event)" destination="BlogDestination" endpoint="http://koichi-hamada.appspot.com/messagebroker/amf"/>
war/WEB-INF/libに Jarを追加
GoogleAppEngine Server上での動作用に補正された flex-messagingのJARを追加。
次サイトからも取得できる。
flex-messaging-core-FIXED_1.jar
関連文献
Action Script, Adobe Flex のオススメ本
上記使用している Flex, MXML, Action Script, BlazeDS 等に関し今まで読んだ本で、オススメのものを以下に紹介する。初めての人でも分かりやすい入門本、および、応用に使える本。
親切なAction Scriptの入門書。サンプルや例題が分かりやすく親切に載っており初学者でも読みやすい。
ActionScriptで凝ったことをやり始めようとする人に最適。どのように使うかが具体的に丁寧に書かれている。ActionScriptの入門書を読んだ後に応用を学ぶのによい本。
MXMLの記述に詳しい。Flex Builderのインストール方法、設定など詳しく書かれており初学者でも読み進めやすい。
Server側とFlexの具体的な連携方法が具体的に書かれている。インストール方法、設定方法から親切に書かれている。BlazeDSとS2Daoを利用した通信方法、DBアクセスなど実際に使える方法。
※他参考・関連サイト:
・AppEngine & Adobe BlazeDS (fix)
・BlazeDSでFlexとJavaを通信させる必要最低限の設定
・NEW UPDATE TO THE SPRING BLAZEDS INTEGRATION TEST DRIVE