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の設定は完了です。
次回こそ、このフレームワークを使用して簡単なサンプルを書いてみます。

0 件のコメント:

コメントを投稿