Hatena::ブログ(Diary)

bufferings

2010-01-26

Slim3でGWTのブランクプロジェクトを作る手順

| 03:06 |

2010-01-27 追記

MyFirstProject.gwt.xml の設定を変更するのを忘れてました。スミマセン。一番下に追記します。



すぐに忘れるのでメモ。

Eclipse使ってます。


Slim3 公式の GWT ブランクプロジェクトがそのうちできるんじゃないかと思うけど。

今日は slim3demo プロジェクトからザーッと作りまうす。

slim3demo はチェックアウトしといてね。


眠いので殴り書くよぃ。

プロジェクトは MyFirstProject

ルートは myproject.first にしとこか。


あ、設定は僕の好みだかんねっ。


1. プロジェクトを作る

こうして

f:id:bufferings:20100127012953j:image


こうして

f:id:bufferings:20100127012954j:image


こうなる!

f:id:bufferings:20100127012955j:image


2. JDOをはずす

こうして

f:id:bufferings:20100127013844j:image


こうして

f:id:bufferings:20100127013843j:image


こうなる!!

f:id:bufferings:20100127013845j:image


META-INFも削除

f:id:bufferings:20100127013846j:image


3. ロガー設定ファイル

src/log4j.properties を削除して

f:id:bufferings:20100127014549j:image


war/WEB-INF/logging.properties を src/logging.properties に移動する

f:id:bufferings:20100127014550j:image


こうなる

f:id:bufferings:20100127014551j:image


logging.properties を編集。僕は DataNucleus 周りのを消して CONFIG 設定の1行だけにするなー。

.level = CONFIG

appengine-web.xml のロガーのところを今のに合わせて編集する

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app
  xmlns="http://appengine.google.com/ns/1.0">
  <application></application>
  <version></version>

  <system-properties>
    <property
      name="java.util.logging.config.file"
      value="WEB-INF/classes/logging.properties" />
  </system-properties>
</appengine-web-app>

と、ここまではまだ Slim3 関係ない。設定がうまくいってるか。試しに動かしてみよか?


こうして

f:id:bufferings:20100127020415j:image


こうなる

f:id:bufferings:20100127020414j:image


動いたよね。じゃ、こっから Slim3 の設定。


4. ライブラリの設定

プロジェクト直下に lib と libsrc フォルダを作って

f:id:bufferings:20100127022043j:image


slim3demo から

lib の中に slim3-gen-EA1-SNAPSHOT.jar

libsrc の中に slim3-EA1-SNAPSHOT-sources.jarslim3-gen-EA1-SNAPSHOT-sources.jar をコピ

f:id:bufferings:20100127022044j:image


war/WEB-INF/lib に slim3-EA1-SNAPSHOT.jarjunit-4.7.jar をコピ

f:id:bufferings:20100127022045j:image


その2つの jarビルドパスに追加

f:id:bufferings:20100127022046j:image


slim3-EA1-SNAPSHOT.jar にソースを関連付けとく

f:id:bufferings:20100127022047j:image


こうなった

f:id:bufferings:20100127022042j:image


5. web.xml を編集

slim3demo の web.xml の1行目から frontController の filter-mapping までをコピーして

  • GWTServiceServlet の設定を書いて
  • greetServlet の設定を消して
  • slim3 のルートパッケージを myproject.first にして

こうなった

<?xml version="1.0" encoding="utf-8"?>

<web-app
  xmlns="http://java.sun.com/xml/ns/javaee"
  version="2.5">

  <context-param>
    <param-name>slim3.rootPackage</param-name>
    <param-value>myproject.first</param-value>
  </context-param>
  <context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.localizationContext
    </param-name>
    <param-value>application</param-value>
  </context-param>
  <context-param>
    <param-name>javax.servlet.jsp.jstl.fmt.request.charset
    </param-name>
    <param-value>UTF-8</param-value>
  </context-param>

  <filter>
    <filter-name>hotReloadingFilter</filter-name>
    <filter-class>org.slim3.controller.HotReloadingFilter
    </filter-class>
  </filter>
  <filter>
    <filter-name>datastoreFilter</filter-name>
    <filter-class>org.slim3.datastore.DatastoreFilter
    </filter-class>
  </filter>
  <filter>
    <filter-name>frontController</filter-name>
    <filter-class>org.slim3.controller.FrontController
    </filter-class>
  </filter>

  <filter-mapping>
    <filter-name>hotReloadingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>
  <filter-mapping>
    <filter-name>datastoreFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>
  <filter-mapping>
    <filter-name>frontController</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
  </filter-mapping>

  <servlet>
    <servlet-name>GWTServiceServlet</servlet-name>
    <servlet-class>org.slim3.gwt.server.rpc.GWTServiceServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>GWTServiceServlet</servlet-name>
    <url-pattern>*.s3gwt</url-pattern>
  </servlet-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>MyFirstProject.html</welcome-file>
  </welcome-file-list>

</web-app>

6. リモートサービスの変更

GreetingServiceImpl は RemoteServiceServlet を継承するのをやめる。

  • getServletContext() の代わりに ServletContextLocator.get() を使う
  • getThreadLocalRequest() の代わりに RequestLocator.get() を使う

こうなった

package myproject.first.server;

import myproject.first.client.GreetingService;

import org.slim3.util.RequestLocator;
import org.slim3.util.ServletContextLocator;

/**
 * The server side implementation of the RPC service.
 */
public class GreetingServiceImpl implements
    GreetingService {

  public String greetServer(String input) {
    String serverInfo = ServletContextLocator.get().getServerInfo();
    String userAgent = RequestLocator.get().getHeader("User-Agent");
    return "Hello, "
      + input
      + "!<br><br>I am running "
      + serverInfo
      + ".<br><br>It looks like you are using:<br>"
      + userAgent;
  }
}

GreetingService の URL拡張子(?)を".s3gwt"にする。

package myproject.first.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet.s3gwt")
public interface GreetingService extends RemoteService {
  String greetServer(String name);
}

以上ですー。

動作確認してみよか。

f:id:bufferings:20100127022048j:image


動いた−。よかった。


あ、忘れてた。

7. テストフォルダをソースフォルダにする

test フォルダをソースフォルダにして

f:id:bufferings:20100127025617j:image


ビルドパスの構成で

「ソース・フォルダごとに出力フォルダの指定を可能にする」を選択

f:id:bufferings:20100127025618j:image


test ソースフォルダの出力先を build にでもしとく

f:id:bufferings:20100127025619j:image


あー。もういっこ忘れてた。これ重要

8. build.xml をコピーする

build.xml と build.properties をプロジェクト直下にコピー

f:id:bufferings:20100127025620j:image


gen-model ターゲットを実行して「Employee」を作ってみたら

こうなったよー

f:id:bufferings:20100127025621j:image


おしまい。



・・・あっ!!もっと重要なことを忘れてた。

9. 注釈処理(APT)の設定

f:id:bufferings:20100127030503j:image


これしとかないとね。


ちなみに

greetServlet を残して、そのまま使ってみようとしたらこうなった。


HotReloadingFilter を外せば動くので、 Cool な RemoteServiceServlet から Hot な GreetServiceImpl を触ろうとしてエラーになってるんじゃないかと予想。

致命的: [1264527189734000] javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
java.lang.SecurityException: Blocked attempt to invoke method
'public abstract java.lang.String myproject.first.client.GreetingService.greetServer(java.lang.String)'
on target 'myproject.first.server.GreetingServiceImpl' with invalid arguments[GWT User]
	at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:572)
	at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:188)
	at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:224)
	at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:713)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
	at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1093)
	at org.slim3.controller.FrontController.doFilter(FrontController.java:331)
	at org.slim3.controller.FrontController.doFilter(FrontController.java:285)
	at org.slim3.controller.FrontController.doFilter(FrontController.java:247)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at org.slim3.datastore.DatastoreFilter.doFilter(DatastoreFilter.java:53)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at org.slim3.controller.HotReloadingFilter.doHotReloading(HotReloadingFilter.java:223)
	at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:187)
	at org.slim3.controller.HotReloadingFilter.doFilter(HotReloadingFilter.java:157)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:51)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:121)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1084)
	at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
	at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:712)
	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
	at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
	at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:352)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:139)
	at org.mortbay.jetty.Server.handle(Server.java:313)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:506)
	at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:844)
	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:644)
	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:381)
	at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:396)
	at org.mortbay.thread.BoundedThreadPool$PoolThread.run(BoundedThreadPool.java:442)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:100)
	at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:562)
	... 40 more

今日の反省

ふとんを売りつけるサギみたいになったこと

しかし、売りつける元気がなくなり、しりすぼみになったこと


2010-01-27 追記

MyFirstProject.gwt.xml の設定

これをしておかないと shared パッケージにあるモデルを client で触れないです。忘れてました。

<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='myfirstproject'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>
  <inherits name='org.slim3.gwt.emul.S3Emulation' />

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.standard.Standard'/>
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
  <entry-point class='myproject.first.client.MyFirstProject'/>

  <!-- Specify the paths for translatable code                    -->
  <source path='client'/>
  <source path="shared" />

</module>

  <inherits name='org.slim3.gwt.emul.S3Emulation' />

  <source path="shared" />

を追加してます。


S3Emulation は client パッケージで Key を扱うために必要で shared をソースパス指定してるのは

client パッケージでモデルを使うことができるようにするためです。


エラーの調査

昨日出たエラー。ちょっとは調査しとこうと思ったので追いかけた。


HotReloadingFilter の doHotReloading の Thread.currentThread().setContextClassLoader の部分を

コメントアウトしたらエラーなしで動く。


この部分

  protected synchronized void doHotReloading(HttpServletRequest request,
          HttpServletResponse response, FilterChain chain,
          ClassLoader previousLoader) throws IOException, ServletException {
      Thread.currentThread().setContextClassLoader(
          new HotReloadingClassLoader(
              previousLoader,
              rootPackageName,
              coolPackageName));

クラスローダー絡みか・・・。これ以上はどうやって調べたらいいか分からない。


分かったのは HotReloading を使ってる時には普通のGWTサーブレット(RemoteServiceServlet を継承したもの)を使うと怒られるということ。

Slim3 を使ってるなら普通は作ることはないだろうけどっ。どっ。