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ファイルです。


 
  slim3.rootPackage
  com.brightgenerous.s3.sample.data
 

 
  contextConfigLocation
  classpath:applicationContext*.xml
 

 
  DatastoreFilter
  org.slim3.datastore.DatastoreFilter
 

 
  struts2
  org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
  
   actionPackages
   com.brightgenerous.s3.sample.action
  
 

 
  DatastoreFilter
  /*
  REQUEST
 
 
  struts2
  /*
 

 
  GlobalTransactionServlet
  org.slim3.datastore.GlobalTransactionServlet
  1
 

 
  GlobalTransactionServlet
  /slim3/gtx
 

 
  org.springframework.web.context.ContextLoaderListener
 
 
  com.brightgenerous.s3.sample.gae.GaeInitListener
 

 
  
   /slim3/gtx
  
  
   admin
  
 


Slim3の新規プロジェクトを作成して生成されるweb.xmlの内容のほとんど少しを消すことになります。
「com.brightgenerous.s3.sample.gae.GaeInitListener」は、独自に実装するクラスです。ソースは「実装するクラス」にて後述します。
applicationContext.xml
「classpath:applicationContext*.xml」(src直下)に配置します。


 

 

 

 
 
  
   
    PROPAGATION_REQUIRED
   
  
 
 
  
  
 
 
  
   
    transactionInterceptor
   
  
  
   
    *ServiceImpl
   
  
 


「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
package com.brightgenerous.s3.sample.gae;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import ognl.OgnlRuntime;

public class GaeInitListener implements ServletContextListener, HttpSessionListener, HttpSessionAttributeListener {

 @Override
 public void contextInitialized(ServletContextEvent sce) {
  OgnlRuntime.setSecurityManager(null);
 }

 @Override
 public void contextDestroyed(ServletContextEvent arg0) {
 }

 @Override
 public void sessionCreated(HttpSessionEvent arg0) {
 }

 @Override
 public void sessionDestroyed(HttpSessionEvent arg0) {
 }

 @Override
 public void attributeAdded(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeRemoved(HttpSessionBindingEvent arg0) {
 }

 @Override
 public void attributeReplaced(HttpSessionBindingEvent arg0) {
 }
}
OGNLのセキュリティマネージャがなんとかかんとか...理由については、詳しく調べていません。
2.com.brightgenerous.s3.sample.gae.GaeFixInternalPersistenceAnnotationProcessor
package com.brightgenerous.s3.sample.gae;

public class GaeFixInternalPersistenceAnnotationProcessor {
}
「java.lang.NoClassDefFoundError」を回避することだけが目的です。
3.com.brightgenerous.s3.sample.gae.Slim3TransactionManager
package com.brightgenerous.s3.sample.gae;

import org.slim3.datastore.Datastore;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;

public class Slim3TransactionManager extends AbstractPlatformTransactionManager {

 private static final long serialVersionUID = 3423679590583692519L;

 @Override
 protected void doBegin(Object x_arg0, TransactionDefinition x_arg1) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0;
  transactionObject.setTransaction(Datastore.beginGlobalTransaction());
 }

 @Override
 protected void doCommit(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.getTransaction().commit();
 }

 @Override
 protected Object doGetTransaction() throws TransactionException {
  return new TransactionObject();
 }

 @Override
 protected void doRollback(DefaultTransactionStatus x_arg0) throws TransactionException {
  TransactionObject transactionObject = (TransactionObject) x_arg0.getTransaction();
  transactionObject.getTransaction().rollback();
 }
}
package com.brightgenerous.s3.sample.gae;

import java.io.Serializable;

import org.slim3.datastore.GlobalTransaction;

public class TransactionObject implements Serializable {

 private static final long serialVersionUID = -8353097480608667182L;

 private GlobalTransaction p_transaction;

 public GlobalTransaction getTransaction() {
  return p_transaction;
 }

 public void setTransaction(GlobalTransaction x_transaction) {
  p_transaction = x_transaction;
 }
}
package com.brightgenerous.s3.sample.gae;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Slim3GlobalTransaction {
}
「org.springframework.jdbc.datasource.DataSourceTransactionManager」の実装を参考にして、Slim3用に簡単に対応させています。
「TransactionObject」、「Slim3GlobalTransaction」も作成します。 このクラスのみ自作です。間違い等あれば指摘していただけるとありがたいです。
4.com.brightgenerous.s3.sample.gae.Slim3TransactionInterceptor
 private void injectTransaction(Object x_obj, TransactionInfo x_txInfo) {
  TransactionStatus txStatus = x_txInfo.getTransactionStatus();
  DefaultTransactionStatus dtxStatus;
  if (!(txStatus instanceof DefaultTransactionStatus)) {
   return;
  }
  dtxStatus = (DefaultTransactionStatus) txStatus;
  Object tx = dtxStatus.getTransaction();
  if (tx instanceof TransactionObject) {
   tx = ((TransactionObject) tx).getTransaction();
  }
  if (!(tx instanceof GlobalTransaction)) {
   return;
  }
  @SuppressWarnings("rawtypes")
  Class clazz = x_obj.getClass();
  do {
   Field[] fields = clazz.getDeclaredFields();
   for (Field field : fields) {
    if (!field.isAccessible()) {
     field.setAccessible(true);
    }
    Slim3GlobalTransaction s3gtx = field.getAnnotation(Slim3GlobalTransaction.class);
    if (s3gtx != null) {
     if (field.getType().isInstance(tx)) {
      try {
       field.set(x_obj, tx);
      } catch (IllegalAccessException e) {
       throw new RuntimeException(e);
      } catch (IllegalArgumentException e) {
       throw new RuntimeException(e);
      }
     }
    }
   }
   clazz = clazz.getSuperclass();
  } while ((clazz != null) && !clazz.equals(Object.class));
 }
「org.springframework.transaction.interceptor.TransactionInterceptor」のソースをコピーしてきて、 上記のメソッドが適当な箇所で呼ばれるように書き換えます。
ライセンスを考慮して全体は公開しませんが、元のソースを読めば変更箇所はわかるはずです。

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

0 件のコメント:

コメントを投稿