2011年3月31日木曜日

enumを使いたおしたい

こういうパターンはいかがでしょうか?という内容です。

javaのenumについてです。

先日、ふと、Effective Javaを読み直してenumについての認識を改めました。

enumの変数というのは、「唯一のインスタンス」
その結果、定数のようにイコールの演算子で一致するのです。

以前からそれは理解していました。
が、実は、思ってた以上に普通のクラスと同じで、インターフェースを実装できます。
さらには、mainメソッドを書いて実行することもできるのです。

これを定数としてしか使わないのは勿体無い。
ということで、有効活用をちょっと考えてみた。

こういうのはいかが?
  1. package sample;  
  2.   
  3. import java.math.BigDecimal;  
  4.   
  5. enum NumberUtility {  
  6.   
  7.  SIMPLE {  
  8.   
  9.   @Override  
  10.   Integer createInteger(String x_str) {  
  11.    return Integer.valueOf(x_str);  
  12.   }  
  13.   
  14.   @Override  
  15.   BigDecimal createBigDecimal(String x_str) {  
  16.    return new BigDecimal(x_str);  
  17.   }  
  18.  },  
  19.   
  20.  QUIET {  
  21.   
  22.   @Override  
  23.   Integer createInteger(String x_str) {  
  24.    Integer result = null;  
  25.    if (x_str != null) {  
  26.     try {  
  27.      result = SIMPLE.createInteger(x_str);  
  28.     } catch (NumberFormatException e) {  
  29.     }  
  30.    }  
  31.    return result;  
  32.   }  
  33.   
  34.   @Override  
  35.   BigDecimal createBigDecimal(String x_args) {  
  36.    BigDecimal result = null;  
  37.    if (x_args != null) {  
  38.     try {  
  39.      result = SIMPLE.createBigDecimal(x_args);  
  40.     } catch (NumberFormatException e) {  
  41.     }  
  42.    }  
  43.    return result;  
  44.   }  
  45.  };  
  46.   
  47.  abstract Integer createInteger(String x_str);  
  48.   
  49.  abstract BigDecimal createBigDecimal(String x_str);  
  50. }  
  51.   
  52. abstract class Test {  
  53.   
  54.  void exec() {  
  55.   System.out.println("100 => " + getNumberUtility().createInteger("100"));  
  56.   System.out.println("10.0 => " + getNumberUtility().createBigDecimal("10.0"));  
  57.   System.out.println("null => " + getNumberUtility().createInteger("null"));  
  58.  }  
  59.   
  60.  abstract NumberUtility getNumberUtility();  
  61. }  
  62.   
  63. public class TestMain {  
  64.   
  65.  public static void main(String[] args) {  
  66.   
  67.   try {  
  68.    new Test() {  
  69.   
  70.     @Override  
  71.     NumberUtility getNumberUtility() {  
  72.      return NumberUtility.SIMPLE;  
  73.     }  
  74.    }.exec();  
  75.   } catch (Exception e) {  
  76.    System.out.println("catch exception. => " + e.getMessage());  
  77.   }  
  78.    
  79.   System.out.println();  
  80.   
  81.   try {  
  82.    new Test() {  
  83.   
  84.     @Override  
  85.     NumberUtility getNumberUtility() {  
  86.      return NumberUtility.QUIET;  
  87.     }  
  88.    }.exec();  
  89.   } catch (Exception e) {  
  90.    System.out.println("catch exception. => " + e.getMessage());  
  91.   }  
  92.  }  
  93. }  
enumの中にabstractメソッドがあって、その場で実装しています。こんなことができるんですねぇ。知りませんでした。
書いた内容について簡単に言うと、Utilityクラスの切り替えです。
Utilityの処理の役割としてはstaticなメソッドで実装したいのだけど、ロジックの違いをインスタンスで変えたいって感じです。

Javaの言語仕様での、enumの実現方法がシンプルだからこそ、これだけの自由度があるのでしょう。
単純さが自由になります。

1行目でエラーが出ましたと言わせたい

今回は、ちょっとしたネタです。
知っている人も多いかもしれません。

内容はタイトルの通り「ソースコードの1行目でエラーを出す」方法です。
もしかすると、JVMに依存するかもしれませんのであしからず。

エラーを吐くコードを1行目に書く方法
  1. public class ExceptionAtLine1 {public static void main(String[] x_args) { System.out.println(x_args[100]); } }  
  1. Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100  
  2.  at ExceptionAtLine1.main(ExceptionAtLine1.java:1)  
すぐに思いつく方法です。

いや、そんなの当たり前だろって思われてる人も多いでしょう。

では、以下の方法はどうでしょうか?

Genericsを ”利用して” エラーを吐く方法
  1. package sample;  
  2.   
  3. public class ExceptionAtLine1Zwei {  
  4.   
  5.  public static void main(String[] x_args) {  
  6.   IMethod method = new IMethodImpl();  
  7.   method.method(Integer.valueOf(100));  
  8.  }  
  9. }  
  10.   
  11. interface IMethod<t> {  
  12.  void method(T x_obj);  
  13. }  
  14.   
  15. class IMethodImpl implements IMethod<string> {  
  16.   
  17.  @Override  
  18.  public void method(String x_obj) {  
  19.   System.out.println(x_obj);  
  20.  }  
  21. }  
  22. </string></t>  
  1. Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String  
  2.  at sample.IMethodImpl.method(ExceptionAtLine1Zwei.java:1)  
  3.  at sample.ExceptionAtLine1Zwei.main(ExceptionAtLine1Zwei.java:7)  
どう見ても呼ばれるべきメソッドがありませんね。
Genericsを中途半端に使用すると恐ろしいことになる良い例です。

Eclipseで警告が出るようにしておきましょう。
厳しさは易しさです。

2011年3月26日土曜日

【修正版】 Struts2 + Spring + Slim3

「Struts2 + Spring + Slim3の連携に挑戦」の修正版です。

前回の内容を修正しました。
改めてSlim3のLocalTransactionとGlobalTransactionについて調べた結果、前回の内容では不完全だったことがわかりました。

今回は完全に書き直しです。

Slim3の新規プロジェクト作成
まず最初に、EclipseのSlim3プラグインを使用してSlim3の新規プロジェクトを作成します。
プロジェクト名とルートパッケージ
Project names3sample
Root Packagecom.brightgenerous.s3.sample.data
不要なファイルを削除しておきます。
削除するファイルとディレクトリ
/src/application_en.properties
/src/application_ja.properties
/war/css
/war/ktrwjr
/war/common.jsp
Jarファイル追加
以下のjarファイルを「war/WEB-INF/lib」に追加します。
追加するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-gae-pre3
javassist
ognl-3.0
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-spring-plugin-2.2.1.1
xwork-core-2.2.1.1
freemarker-gae-pre3は、gae用のfreemarkerです。
Springは2.5を使用します。
web.xml
「war/WEB-INF」に配置するweb.xmlファイルです。
  1. <web-app version="2.5" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  2.   
  3.  <context-param>  
  4.   <param-name>slim3.rootPackage</param-name>  
  5.   <param-value>com.brightgenerous.s3.sample.data</param-value>  
  6.  </context-param>  
  7.   
  8.  <context-param>  
  9.   <param-name>contextConfigLocation</param-name>  
  10.   <param-value>classpath:applicationContext*.xml</param-value>  
  11.  </context-param>  
  12.   
  13.  <filter>  
  14.   <filter-name>DatastoreFilter</filter-name>  
  15.   <filter-class>org.slim3.datastore.DatastoreFilter</filter-class>  
  16.  </filter>  
  17.   
  18.  <filter>  
  19.   <filter-name>struts2</filter-name>  
  20.   <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  21.   <init-param>  
  22.    <param-name>actionPackages</param-name>  
  23.    <param-value>com.brightgenerous.s3.sample.action</param-value>  
  24.   </init-param>  
  25.  </filter>  
  26.   
  27.  <filter-mapping>  
  28.   <filter-name>DatastoreFilter</filter-name>  
  29.   <url-pattern>/*</url-pattern>  
  30.   <dispatcher>REQUEST</dispatcher>  
  31.  </filter-mapping>  
  32.  <filter-mapping>  
  33.   <filter-name>struts2</filter-name>  
  34.   <url-pattern>/*</url-pattern>  
  35.  </filter-mapping>  
  36.   
  37.  <servlet>  
  38.   <servlet-name>GlobalTransactionServlet</servlet-name>  
  39.   <servlet-class>org.slim3.datastore.GlobalTransactionServlet</servlet-class>  
  40.   <load-on-startup>1</load-on-startup>  
  41.  </servlet>  
  42.   
  43.  <servlet-mapping>  
  44.   <servlet-name>GlobalTransactionServlet</servlet-name>  
  45.   <url-pattern>/slim3/gtx</url-pattern>  
  46.  </servlet-mapping>  
  47.   
  48.  <listener>  
  49.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  50.  </listener>  
  51.  <listener>  
  52.   <listener-class>com.brightgenerous.s3.sample.gae.GaeInitListener</listener-class>  
  53.  </listener>  
  54.   
  55.  <security-constraint>  
  56.   <web-resource-collection>  
  57.    <url-pattern>/slim3/gtx</url-pattern>  
  58.   </web-resource-collection>  
  59.   <auth-constraint>  
  60.    <role-name>admin</role-name>  
  61.   </auth-constraint>  
  62.  </security-constraint>  
  63.   
  64. </web-app>  
Slim3の新規プロジェクトを作成して生成されるweb.xmlの内容のほとんど少しを消すことになります。
「com.brightgenerous.s3.sample.gae.GaeInitListener」は、独自に実装するクラスです。ソースは「実装するクラス」にて後述します。
applicationContext.xml
「classpath:applicationContext*.xml」(src直下)に配置します。
  1. <beans xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans  
  2.    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  3.    http://www.springframework.org/schema/context  
  4.    http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  5.   
  6.  <context:annotation-config></context:annotation-config>  
  7.   
  8.  <context:component-scan base-package="com.brightgenerous.s3.sample.action"></context:component-scan>  
  9.   
  10.  <bean class="com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor" id="org.springframework.context.annotation.internalPersistenceAnnotationProcessor"></bean>  
  11.   
  12.  <bean class="com.brightgenerous.s3.sample.gae.Slim3TransactionManager" id="transactionManager"></bean>  
  13.  <bean class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource" id="transactionAttributeSource">  
  14.   <property name="properties">  
  15.    <props>  
  16.     <prop key="*">PROPAGATION_REQUIRED</prop>  
  17.    </props>  
  18.   </property>  
  19.  </bean>  
  20.  <bean class="com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor" id="transactionInterceptor">  
  21.   <property name="transactionManager" ref="transactionManager"></property>  
  22.   <property name="transactionAttributeSource" ref="transactionAttributeSource"></property>  
  23.  </bean>  
  24.  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator" id="beanNameAutoProxy">  
  25.   <property name="interceptorNames">  
  26.    <list>  
  27.     <value>transactionInterceptor</value>  
  28.    </list>  
  29.   </property>  
  30.   <property name="beanNames">  
  31.    <list>  
  32.     <value>*ServiceImpl</value>  
  33.    </list>  
  34.   </property>  
  35.  </bean>  
  36.   
  37. </beans>  
「transactionManager」のみ用意してやれば他はそのまま使えます。なかなかナイスな設計です。
「transactionManager」と「transactionInterceptor」を用意します。
「com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor」、 「com.brightgenerous.s3.sample.gae.Slim3TransactionManager」、 「com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor」については、後述です。
実装するクラス
1.com.brightgenerous.s3.sample.gae.GaeInitListener
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import javax.servlet.ServletContextEvent;  
  4. import javax.servlet.ServletContextListener;  
  5. import javax.servlet.http.HttpSessionAttributeListener;  
  6. import javax.servlet.http.HttpSessionBindingEvent;  
  7. import javax.servlet.http.HttpSessionEvent;  
  8. import javax.servlet.http.HttpSessionListener;  
  9.   
  10. import ognl.OgnlRuntime;  
  11.   
  12. public class GaeInitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {  
  13.   
  14.  @Override  
  15.  public void contextInitialized(ServletContextEvent sce) {  
  16.   OgnlRuntime.setSecurityManager(null);  
  17.  }  
  18.   
  19.  @Override  
  20.  public void contextDestroyed(ServletContextEvent arg0) {  
  21.  }  
  22.   
  23.  @Override  
  24.  public void sessionCreated(HttpSessionEvent arg0) {  
  25.  }  
  26.   
  27.  @Override  
  28.  public void sessionDestroyed(HttpSessionEvent arg0) {  
  29.  }  
  30.   
  31.  @Override  
  32.  public void attributeAdded(HttpSessionBindingEvent arg0) {  
  33.  }  
  34.   
  35.  @Override  
  36.  public void attributeRemoved(HttpSessionBindingEvent arg0) {  
  37.  }  
  38.   
  39.  @Override  
  40.  public void attributeReplaced(HttpSessionBindingEvent arg0) {  
  41.  }  
  42. }  
OGNLのセキュリティマネージャがなんとかかんとか...理由については、詳しく調べていません。
2.com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. public class GaeFixInternalPersistenceAnnotationProcessor {  
  4. }  
「java.lang.NoClassDefFoundError」を回避することだけが目的です。
3.com.brightgenerous.s3.sample.gae.Slim3TransactionManager
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import org.slim3.datastore.Datastore;  
  4. import org.springframework.transaction.TransactionDefinition;  
  5. import org.springframework.transaction.TransactionException;  
  6. import org.springframework.transaction.support.AbstractPlatformTransactionManager;  
  7. import org.springframework.transaction.support.DefaultTransactionStatus;  
  8.   
  9. public class Slim3TransactionManager extends AbstractPlatformTransactionManager {  
  10.   
  11.  private static final long serialVersionUID = 3423679590583692519L;  
  12.   
  13.  @Override  
  14.  protected void doBegin(Object x_arg0, TransactionDefinition x_arg1) throws TransactionException {  
  15.   TransactionObject transactionObject = (TransactionObject) x_arg0;  
  16.   transactionObject.setTransaction(Datastore.beginGlobalTransaction());  
  17.  }  
  18.   
  19.  @Override  
  20.  protected void doCommit(DefaultTransactionStatus x_arg0) throws TransactionException {  
  21.   TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();  
  22.   transactionObject.getTransaction().commit();  
  23.  }  
  24.   
  25.  @Override  
  26.  protected Object doGetTransaction() throws TransactionException {  
  27.   return new TransactionObject();  
  28.  }  
  29.   
  30.  @Override  
  31.  protected void doRollback(DefaultTransactionStatus x_arg0) throws TransactionException {  
  32.   TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();  
  33.   transactionObject.getTransaction().rollback();  
  34.  }  
  35. }  
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import org.slim3.datastore.GlobalTransaction;  
  6.   
  7. public class TransactionObject implements Serializable {  
  8.   
  9.  private static final long serialVersionUID = -8353097480608667182L;  
  10.   
  11.  private GlobalTransaction p_transaction;  
  12.   
  13.  public GlobalTransaction getTransaction() {  
  14.   return p_transaction;  
  15.  }  
  16.   
  17.  public void setTransaction(GlobalTransaction x_transaction) {  
  18.   p_transaction = x_transaction;  
  19.  }  
  20. }  
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. @Target(ElementType.FIELD)  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. public @interface Slim3GlobalTransaction {  
  11. }  
「org.springframework.jdbc.datasource.DataSourceTransactionManager」の実装を参考にして、Slim3用に簡単に対応させています。
「TransactionObject」、「Slim3GlobalTransaction」も作成します。 このクラスのみ自作です。間違い等あれば指摘していただけるとありがたいです。
4.com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor
  1. private void injectTransaction(Object x_obj, TransactionInfo x_txInfo) {  
  2.  TransactionStatus txStatus = x_txInfo.getTransactionStatus();  
  3.  DefaultTransactionStatus dtxStatus;  
  4.  if (!(txStatus instanceof DefaultTransactionStatus)) {  
  5.   return;  
  6.  }  
  7.  dtxStatus = (DefaultTransactionStatus) txStatus;  
  8.  Object tx = dtxStatus.getTransaction();  
  9.  if (tx instanceof TransactionObject) {  
  10.   tx = ((TransactionObject) tx).getTransaction();  
  11.  }  
  12.  if (!(tx instanceof GlobalTransaction)) {  
  13.   return;  
  14.  }  
  15.  @SuppressWarnings("rawtypes")  
  16.  Class clazz = x_obj.getClass();  
  17.  do {  
  18.   Field[] fields = clazz.getDeclaredFields();  
  19.   for (Field field : fields) {  
  20.    if (!field.isAccessible()) {  
  21.     field.setAccessible(true);  
  22.    }  
  23.    Slim3GlobalTransaction s3gtx = field.getAnnotation(Slim3GlobalTransaction.class);  
  24.    if (s3gtx != null) {  
  25.     if (field.getType().isInstance(tx)) {  
  26.      try {  
  27.       field.set(x_obj, tx);  
  28.      } catch (IllegalAccessException e) {  
  29.       throw new RuntimeException(e);  
  30.      } catch (IllegalArgumentException e) {  
  31.       throw new RuntimeException(e);  
  32.      }  
  33.     }  
  34.    }  
  35.   }  
  36.   clazz = clazz.getSuperclass();  
  37.  } while ((clazz != null) && !clazz.equals(Object.class));  
  38. }  
「org.springframework.transaction.interceptor.TransactionInterceptor」のソースをコピーしてきて、 上記のメソッドが適当な箇所で呼ばれるように書き換えます。
ライセンスを考慮して全体は公開しませんが、元のソースを読めば変更箇所はわかるはずです。

以上でStruts2 + Spring + Slim3の設定は完了です。
次回こそ、このフレームワークを使用して簡単なサンプルを書いてみます。

2011年3月24日木曜日

Struts2 + Spring + Slim3

この内容は修正されました。
修正後はこちら

Struts2 + Spring + Slim3の連携に挑戦。

ソースの省略はしません。
今回と次回で、簡単なサンプルを動作させるまでの手順を載せます。

Slim3の新規プロジェクト作成
まず最初に、EclipseのSlim3プラグインを使用してSlim3の新規プロジェクトを作成します。
プロジェクト名とルートパッケージ
Project names3sample
Root Packagecom.brightgenerous.s3.sample.data
不要なファイルを削除しておきます。
削除するファイルとディレクトリ
/src/application_en.properties
/src/application_ja.properties
/war/css
/war/ktrwjr
/war/common.jsp
Jarファイル追加
以下のjarファイルを「war/WEB-INF/lib」に追加します。
追加するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-gae-pre3
javassist
ognl-3.0
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-spring-plugin-2.2.1.1
xwork-core-2.2.1.1
freemarker-gae-pre3は、gae用のfreemarkerです。
Springは2.5を使用します。
web.xml
「war/WEB-INF」に配置するweb.xmlファイルです。
  1. <!--xml version="1.0" encoding="utf-8"?-->  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">  
  3.   
  4.  <context-param>  
  5.   <param-name>slim3.rootPackage</param-name>  
  6.   <param-value>com.brightgenerous.s3.sample.data</param-value>  
  7.  </context-param>  
  8.   
  9.  <context-param>  
  10.   <param-name>contextConfigLocation</param-name>  
  11.   <param-value>classpath:applicationContext*.xml</param-value>  
  12.  </context-param>  
  13.   
  14.  <filter>  
  15.   <filter-name>struts2</filter-name>  
  16.   <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  17.   <init-param>  
  18.    <param-name>actionPackages</param-name>  
  19.    <param-value>com.brightgenerous.s3.sample.action</param-value>  
  20.   </init-param>  
  21.  </filter>  
  22.   
  23.  <filter-mapping>  
  24.   <filter-name>struts2</filter-name>  
  25.   <url-pattern>/*</url-pattern>  
  26.  </filter-mapping>  
  27.   
  28.  <listener>  
  29.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  30.  </listener>  
  31.  <listener>  
  32.   <listener-class>com.brightgenerous.s3.sample.gae.GaeInitListener</listener-class>  
  33.  </listener>  
  34.   
  35. </web-app>  
Slim3の新規プロジェクトを作成して生成されるweb.xmlの内容のほとんどを消すことになります。
「com.brightgenerous.s3.sample.gae.GaeInitListener」は、独自に実装するクラスです。ソースは「実装するクラス」にて後述します。
applicationContext.xml
「classpath:applicationContext*.xml」(src直下)に配置します。
  1. <!--xml version="1.0" encoding="utf-8"?-->  
  2. <!--xml version="1.0" encoding="UTF-8" ?-->  
  3. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="  
  4.    http://www.springframework.org/schema/beans  
  5.    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  6.    http://www.springframework.org/schema/context  
  7.    http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  8.   
  9.  <context:annotation-config></context:annotation-config>  
  10.   
  11.  <context:component-scan base-package="com.brightgenerous.s3.sample.action"></context:component-scan>  
  12.   
  13.  <bean id="org.springframework.context.annotation.internalPersistenceAnnotationProcessor" class="com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor">  
  14.  </bean>  
  15.   
  16.  <bean id="transactionManager" class="com.brightgenerous.s3.sample.gae.Slim3TransactionManager">  
  17.  </bean>  
  18.  <bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">  
  19.   <property name="properties">  
  20.    <props>  
  21.     <prop key="regist*">PROPAGATION_REQUIRED</prop>  
  22.     <prop key="update*">PROPAGATION_REQUIRED</prop>  
  23.     <prop key="delete*">PROPAGATION_REQUIRED</prop>  
  24.     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>  
  25.    </props>  
  26.   </property>  
  27.  </bean>  
  28.  <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  29.   <property name="transactionManager" ref="transactionManager"></property>  
  30.   <property name="transactionAttributeSource" ref="transactionAttributeSource"></property>  
  31.  </bean>  
  32.  <bean id="beanNameAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  33.   <property name="interceptorNames">  
  34.    <list>  
  35.     <value>transactionInterceptor</value>  
  36.    </list>  
  37.   </property>  
  38.   <property name="beanNames">  
  39.    <list>  
  40.     <value>*ServiceImpl</value>  
  41.    </list>  
  42.   </property>  
  43.  </bean>  
  44.   
  45. </beans>  
「transactionManager」のみ用意してやれば他はそのまま使えます。なかなかナイスな設計です。
「com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor」、「com.brightgenerous.s3.sample.gae.Slim3TransactionManager」については、後述です。
実装するクラス
1.com.brightgenerous.s3.sample.gae.GaeInitListener
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import javax.servlet.ServletContextEvent;  
  4. import javax.servlet.ServletContextListener;  
  5. import javax.servlet.http.HttpSessionAttributeListener;  
  6. import javax.servlet.http.HttpSessionBindingEvent;  
  7. import javax.servlet.http.HttpSessionEvent;  
  8. import javax.servlet.http.HttpSessionListener;  
  9.   
  10. import ognl.OgnlRuntime;  
  11.   
  12. public class GaeInitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {  
  13.   
  14.  @Override  
  15.  public void contextInitialized(ServletContextEvent sce) {  
  16.   OgnlRuntime.setSecurityManager(null);  
  17.  }  
  18.   
  19.  @Override  
  20.  public void contextDestroyed(ServletContextEvent arg0) {  
  21.  }  
  22.   
  23.  @Override  
  24.  public void sessionCreated(HttpSessionEvent arg0) {  
  25.  }  
  26.   
  27.  @Override  
  28.  public void sessionDestroyed(HttpSessionEvent arg0) {  
  29.  }  
  30.   
  31.  @Override  
  32.  public void attributeAdded(HttpSessionBindingEvent arg0) {  
  33.  }  
  34.   
  35.  @Override  
  36.  public void attributeRemoved(HttpSessionBindingEvent arg0) {  
  37.  }  
  38.   
  39.  @Override  
  40.  public void attributeReplaced(HttpSessionBindingEvent arg0) {  
  41.  }  
  42. }  
OGNLのセキュリティマネージャがなんとかかんとか...理由については、詳しく調べていません。
2.com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. public class GaeFixInternalPersistenceAnnotationProcessor {  
  4.   
  5.  public GaeFixInternalPersistenceAnnotationProcessor() {  
  6.  }  
  7. }  
「java.lang.NoClassDefFoundError」を回避することだけが目的です。
3.com.brightgenerous.s3.sample.gae.Slim3TransactionManager
  1. package com.brightgenerous.s3.sample.gae;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import org.slim3.datastore.Datastore;  
  6. import org.springframework.transaction.TransactionDefinition;  
  7. import org.springframework.transaction.TransactionException;  
  8. import org.springframework.transaction.support.AbstractPlatformTransactionManager;  
  9. import org.springframework.transaction.support.DefaultTransactionStatus;  
  10.   
  11. import com.google.appengine.api.datastore.Transaction;  
  12.   
  13. public class Slim3TransactionManager extends AbstractPlatformTransactionManager {  
  14.   
  15.  private static final long serialVersionUID = 3423679590583692519L;  
  16.   
  17.  @Override  
  18.  protected void doBegin(Object x_arg0, TransactionDefinition x_arg1) throws TransactionException {  
  19.   TransactionObject transactionObject = (TransactionObject) x_arg0;  
  20.   transactionObject.p_transaction = Datastore.beginTransaction();  
  21.  }  
  22.   
  23.  @Override  
  24.  protected void doCommit(DefaultTransactionStatus x_arg0) throws TransactionException {  
  25.   TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();  
  26.   transactionObject.p_transaction.commit();  
  27.  }  
  28.   
  29.  @Override  
  30.  protected Object doGetTransaction() throws TransactionException {  
  31.   return new TransactionObject();  
  32.  }  
  33.   
  34.  @Override  
  35.  protected void doRollback(DefaultTransactionStatus x_arg0) throws TransactionException {  
  36.   TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();  
  37.   transactionObject.p_transaction.rollback();  
  38.  }  
  39.   
  40.  private class TransactionObject implements Serializable {  
  41.   
  42.   private static final long serialVersionUID = -8353097480608667182L;  
  43.   
  44.   Transaction p_transaction;  
  45.  }  
  46. }  
「org.springframework.jdbc.datasource.DataSourceTransactionManager」の実装を参考にして、Slim3用に簡単に対応させています。
このクラスのみ自作です。間違い等あれば指摘していただけるとありがたいです。

以上でStruts2 + Spring + Slim3の設定は完了です。
次回は、このフレームワークを使用して簡単なサンプルを書いてみます。

2011年3月22日火曜日

Hack For Japan

先日、3月21日はHack For Japnanに参加してきました。
京都会場のオフラインでの参加です。

今回は、これまでに何度か参加させていただいたHackathonとは趣旨が違いました。
Hackathonでは「お祭りのような感じで、その時間を楽しむ」のですが、 Hack For Japanでは、震災支援という「目的のために動く」のです。

参加を決めるに際して、おそらくは良い経験ができるだろうと思っていましたが、それ以上の貴重な経験がありました。
経験というより、体感です。
私のしょぼい語彙力では表現しきれませんが、あえて書かせてもらうと
「なんかすげー体験したのが良い経験になった」って感じです。
...書かないほうがよかったかな?


正直言って 役に立てませんでした。
以前から感じていたことですが、自分の弱点も明確になりました。
「準備無しに、いきなり走り出すと間違いなく転ぶ」
日ごろの準備が大切です。 そして、Launch and Iterateです。

最後にこれだけは言っておきます。
Hack For Japanは終わってません。始まったばかりです。
私にとってもこれからです。 いったん走り出してしまえば、自分はできる奴...だと思う。

2011年3月21日月曜日

Struts2 + Spring + MyBatis (その2)

Struts2 + Spring + MyBatis (その1)の続きです。

WEBアプリケーション開発で使用するフレームワークをStruts2.2 + Spring2.5 + MyBatis2.3 で構成してみます。
(その2)では、各xmlファイルとpropertiesファイルを揃えます。

必要な設定ファイルを記載しておきます。
(省略)と書かれているファイルの内容については、一例を記述する程度に留まるので説明しません。詳細について知りたい場合は、解説している他サイトを参照することをお勧めします。
設定ファイル
web.xmlServletの配備記述子
struts.xmlStruts2の設定ファイル(省略)
struts.propertiesStruts2のパラメータファイル(省略)
applicationContext.xmlSpringの設定ファイル
detabase.propertiesデータベースのパラメータファイル(省略)
sqlMapConfig.xmlMyBatisの設定ファイル(省略)
tiles.xmlTilesの設定ファイル(省略)
web.xml
「WEB-INF」フォルダ内にある配備記述子です。これが無いと始まりません。
どのプロジェクトでも共通した記述なので、ほぼそのままコピーすれば使えます。
Servlet2.5の構文で書いてみます。
  1. <!--xml version="1.0" encoding="utf-8"?-->  
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">  
  3.   
  4.  <context-param>  
  5.   <param-name>contextConfigLocation</param-name>  
  6.   <param-value>classpath:applicationContext*.xml</param-value>  
  7.  </context-param>  
  8.   
  9.  <filter>  
  10.   <filter-name>struts2</filter-name>  
  11.   <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>  
  12.   <init-param>  
  13.    <param-name>actionPackages</param-name>  
  14.    <param-value>sample.action</param-value>  
  15.   </init-param>  
  16.  </filter>  
  17.   
  18.  <filter-mapping>  
  19.   <filter-name>struts2</filter-name>  
  20.   <url-pattern>/*</url-pattern>  
  21.  </filter-mapping>  
  22.   
  23.  <servlet>  
  24.   <servlet-name>JspSupportServlet</servlet-name>  
  25.   <servlet-class>org.apache.struts2.views.JspSupportServlet</servlet-class>  
  26.   <load-on-startup>1</load-on-startup>  
  27.  </servlet>  
  28.   
  29.  <servlet-mapping>  
  30.   <servlet-name>JspSupportServlet</servlet-name>  
  31.   <url-pattern>/*</url-pattern>  
  32.  </servlet-mapping>  
  33.   
  34.  <listener>  
  35.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  36.  </listener>  
  37.  <listener>  
  38.   <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>  
  39.  </listener>  
  40.   
  41.  <session-config>  
  42.   <session-timeout>30</session-timeout>  
  43.  </session-config>  
  44.   
  45.  <welcome-file-list>  
  46.   <welcome-file>index.html</welcome-file>  
  47.   <welcome-file>index.jsp</welcome-file>  
  48.  </welcome-file-list>  
  49.   
  50.  <jsp-config>  
  51.   <taglib>  
  52.    <taglib-uri>http://tiles.apache.org/tags-tiles</taglib-uri>  
  53.    <taglib-location>/WEB-INF/tld/tiles-jsp.tld</taglib-location>  
  54.   </taglib>  
  55.  </jsp-config>  
  56.   
  57. </web-app>  
Struts2とSpringを使用するには、上記のように記述します。
画面(View)の実装では、JspではなくFreeMarkerを使用します。 FreeMarker内でJspのtaglib(tiles等)を使用するためには、「JspSupportServlet」を動作させておく必要があります。
applicationContext.xml
applicationContex.xmlはweb.xmlに記述した「classpath:applicationContext*.xml」で参照される場所に配置します。
以下の内容は記述例です。
  1. <!--xml version="1.0" encoding="UTF-8" ?-->  
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemalocation="  
  3.    http://www.springframework.org/schema/beans  
  4.    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
  5.    http://www.springframework.org/schema/context  
  6.    http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
  7.   
  8.  <context:annotation-config></context:annotation-config>  
  9.   
  10.  <context:component-scan base-package="sample.service.impl,sample.dao.impl">  
  11.  </context:component-scan>  
  12.   
  13.  <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  14.   <property name="locations">  
  15.    <list>  
  16.     <value>classpath:database.properties</value>  
  17.    </list>  
  18.   </property>  
  19.  </bean>  
  20.   
  21.  <!-- データソース -->  
  22.  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
  23.   <property name="driverClassName" value="${jdbc.driverClassName}"></property>  
  24.   <property name="url" value="${jdbc.url}"></property>  
  25.   <property name="username" value="${jdbc.username}"></property>  
  26.   <property name="password" value="${jdbc.password}"></property>  
  27.   <property name="defaultAutoCommit" value="true"></property>  
  28.   <property name="removeAbandoned" value="true"></property>  
  29.  </bean>  
  30.   
  31.  <!-- トランザクション -->  
  32.  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  33.   <property name="dataSource" ref="dataSource"></property>  
  34.  </bean>  
  35.  <bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">  
  36.   <property name="properties">  
  37.    <props>  
  38.     <prop key="regist*">PROPAGATION_REQUIRED</prop>  
  39.     <prop key="update*">PROPAGATION_REQUIRED</prop>  
  40.     <prop key="delete*">PROPAGATION_REQUIRED</prop>  
  41.     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>  
  42.    </props>  
  43.   </property>  
  44.  </bean>  
  45.  <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">  
  46.   <property name="transactionManager" ref="transactionManager"></property>  
  47.   <property name="transactionAttributeSource" ref="transactionAttributeSource"></property>  
  48.  </bean>  
  49.  <bean id="beanNameAutoProxy" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">  
  50.   <property name="interceptorNames">  
  51.    <list>  
  52.     <value>transactionInterceptor</value>  
  53.    </list>  
  54.   </property>  
  55.   <property name="beanNames">  
  56.    <list>  
  57.     <value>*ServiceImpl</value>  
  58.    </list>  
  59.   </property>  
  60.  </bean>  
  61.   
  62.  <!-- ibatis -->  
  63.  <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
  64.   <property name="configLocation" value="classpath:sqlMapConfig.xml"></property>  
  65.   <property name="dataSource" ref="dataSource"></property>  
  66.  </bean>  
  67.   
  68. </beans>  
MyBatis(iBatis)をSpringの管理下に置きます。
Springがトランザクションの管理(Begin、Rollback、Commit等)を、MyBatisがSQLの実行のみを担当するといった役割分担を可能にするためです。
そうすることで、MyBatisをSlim3に置き換えた場合にも同じ設計で実装できます。

大半を省略しましたが、設定ファイルについては以上です。
次回は、Google App Engine上でSlim3と連携させてみます。

2011年3月20日日曜日

Struts2 + Spring + MyBatis (その1)

WEBアプリケーション開発で使用するフレームワークをStruts2.2 + Spring2.5 + MyBatis2.3 で構成してみます。
最終的には、MyBatisをSlim3に置き換えてGoogle App Engine上で動作させるまでを予定しているので、そのための手順も含まれています。
(その1)では、必要なjarファイルの選定をします。

使用するフレームワークとそのバージョンを記載しておきます。
フレームワークとバージョン
Struts2Struts2.2.1.1
SpringSpring2.5.6
MyBatisMyBatis2.3.6

空のWebアプリケーションのプロジェクトを作成しておきます。
「Eclipse IDE for Java EE Developers」の「Dynamic Web Project」から新規作成を行うと簡単です。

Struts2
公式サイトからStruts2.2.1.1をダウンロードします。
ダウンローが完了したらファイルを解凍し、使用するjarファイルを選定してプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
使用するjarファイル
aopalliance-1.0
commons-beanutils-1.7.0
commons-digester-2.0
commons-fileupload-1.2.1
commons-io-1.3.2
commons-lang-2.3
commons-logging-1.0.4
commons-validator-1.3.1
freemarker-2.3.16
ognl-3.0
struts2-convention-plugin-2.2.1.1
struts2-core-2.2.1.1
struts2-json-plugin-2.2.1.1
struts2-spring-plugin-2.2.1.1
struts2-tiles-plugin-2.2.1.1
tiles-api-2.0.6
tiles-core-2.0.6
tiles-jsp-2.0.6
xwork-core-2.2.1.1
開発を進めていくうちに上記以外のjarファイルが必要になる事がありますが、その時点で追加します。
struts2にはspringのjarファイルも含まれていますが、springのjarファイルは別途用意します。
javassist
source forgeサイトからJavassist3.14をダウンロードします。
ダウンローが完了したらファイルを解凍し、javassist.jarファイルをプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
Spring
公式サイトからSpring2.5.6をダウンロードします。
ダウンローが完了したらファイルを解凍し、使用するJarファイルを選定してプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。
使用するJarファイル
spring-aop
spring-beans
spring-context
spring-core
spring-tx
spring-web
MyBatis
公式サイトからMyBatis2.3.5をダウンロードします。
ダウンローが完了したらファイルを解凍し、mybatis-2.3.5.jarファイルをプロジェクトの「WEB-INF/lib」フォルダ内にコピーします。

以上でjarファイルの選定は終わりです。
(その2)では、各フレームワークの設定ファイルであるxmlについて書きます。