You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Apache Wiki <wi...@apache.org> on 2007/06/30 07:28:48 UTC

[Tapestry Wiki] Update of "Tapestry4Spring" by NickWestgate

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for change notification.

The following page has been changed by NickWestgate:
http://wiki.apache.org/tapestry/Tapestry4Spring

------------------------------------------------------------------------------
  = Combining Tapestry 4 and Spring =
  
- How can you reference Spring beans from Tapestry 4? In earlier versions of Tapestry, the most common method was to extend the BaseEngine class. However, in Tapestry 4 the BaseEngine class is deprecated, and we now need to extend SpringBeanFactoryHolder.
+ How can you reference Spring beans from Tapestry 4? In earlier versions of Tapestry, the most common method was to extend the BaseEngine class. However, in Tapestry 4 the BaseEngine class is deprecated, and we now need to use or extend SpringBeanFactoryHolder.
+ 
+ In the following varying methods are described of setting up Spring support in Tapestry.
  
  (''Basic knowledge of Java, Tapestry, and Spring assumed.'')
  
  == Step 1: Hivemind Configuration ==
  
  Note: You can skip this step by downloading tapestry-spring.jar from http://sourceforge.net/projects/diaphragma and placing it on your classpath.
+ 
+ If you already take care of Spring elsewhere, you can avoid implementing your own SpringBeanFactoryHolder implementation class, just add an entry in your hivemodule.xml:
+ {{{
+ <implementation service-id="hivemind.lib.DefaultSpringBeanFactoryHolder">
+   <invoke-factory>
+     <construct autowire-services="false" class="engine.PersonalSpringBeanFactoryHolder">
+       <event-listener service-id="hivemind.ShutdownCoordinator" />
+       <set-object property="context" value="service:tapestry.globals.WebContext" />
+     </construct>
+   </invoke-factory>
+ </implementation>
+ }}}
+ 
  
  Continue reading, if you're interested in how it works...
  
@@ -196, +211 @@

  
  NandaFirdausi: I've seen your implementation, and I like it too, just like your other code ;). I thing your implementation doesn't need spring listener anymore, am I right? If so, then the choice to the user is if they do have another spring wep application with nothing to do with tapestry, it's better to set the spring listener and do like this page says. If your web application is all tapestry based (with spring as back-end support), then your code looks cleaner for me ;) 
  
+ 
+ == Tapestry 4 (another solution) ==
+ 
+ JarekWoloszyn: Here is another solution for Tapestry4.
+ 
+ We need a helper class which will create WebApplicationContext for the ServletContext. Hivemind can't call factory methods (from WebApplicationContextUtils), so we create a POJO. Spring Context is re-created everytime we change ServletContext.
+ {{{
+ package org.your.application;
+ 
+ import javax.servlet.ServletContext;
+ 
+ import org.springframework.web.context.WebApplicationContext;
+ import org.springframework.web.context.support.WebApplicationContextUtils;
+ 
+ 
+ public class SpringContextFactory {
+     private ServletContext servletContext;
+     private WebApplicationContext appContext;
+     
+     public WebApplicationContext getAppContext() {
+         return appContext;
+     }    
+     
+     public ServletContext getServletContext() {
+         return servletContext;
+     }
+ 
+     public void setServletContext(ServletContext servletContext) {
+         this.servletContext = servletContext;
+         appContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
+     }     
+ }
+ }}}
+ 
+ Tapestry 4 has Spring integration out of the box. We must only say which BeanFactory should be used.
+ In hivemind.xml, we define a service-point for our helper class. This class takes ServletContext as parameter. 
+ We configure then Hivemind to use appContext member as spring-bean factory.
+ {{{
+ <?xml version="1.0"?>
+ <module id="app" version="1.0.0" package="org.your.application">
+   
+   <contribution configuration-id="hivemind.ApplicationDefaults">
+     <default symbol="hivemind.lib.spring-bean-factory" value="service-property:app.SpringContextFactory:appContext"/>
+   </contribution>
+   
+     <service-point id="SpringContextFactory">        
+         Create WebApplicatonContext for Spring
+         <invoke-factory>
+             <construct class="SpringContextFactory">
+                 <set-service property="servletContext" service-id="tapestry.globals.ServletContext"/>
+             </construct>
+         </invoke-factory>        
+         
+     </service-point>
+     
+ </module>
+ }}}
+ 
+ And that's all. Now you can use spring: prefix to access Spring beans:
+ {{{
+     @InjectObject("spring:DAOFactory")
+     public abstract DAOFactory getDao();
+ }}}
+ 
+ == Injecting Spring Beans ==
+ 
+ You may inject beans managed by Spring into pages and components just like you inject anything else as described by [http://tapestry.apache.org/tapestry4/UsersGuide/injection.html]. You can describe the injection in the .page/.jwc file or using an @InjectObject annotation. Just use the 'spring:' prefix like:
+ {{{
+ @InjectObject("spring:name.from.applicationContext.xml")
+ }}}
+ 
+ You may also wire Spring managed beans in HiveMind services such as IEngine services.
+ {{{
+ <service-point id="MyService" interface="org.apache.tapestry.engine.IEngineService">
+   <invoke-factory>
+     <construct class="engine.MyService">
+     <set-object property="springBean" value="spring:name.of.spring.bean"/>
+     </construct>
+   </invoke-factory>
+ </service-point>
+ }}}
+ 
+ 
+ == Spring, Tapestry and Hibernate ==
+ 
+ In order to use lazy loading and for most Spring Hibernate templates, you need access to a Hibernate Session. The traditionnal Spring solution is to use the "OpenSessionInView" pattern where a Hibernate session is open for the whole request.
+ However, OpenSessionInView has to be configured in the web.xml which is not very friendly when you setup some particular Tapestry URLs.
+ 
+ A better solution is to integrate the session opening/closing in Hivemind by using the WebRequestServicerFilter interface. Also, for Tapestry assets, it is not necessary to create a session. This code is based on the Spring OpenSessionInViewFilter.
+ 
+ This requires to implement a class and wire it into Tapestry.
+ Here is the code for the class:
+ 
+ {{{
+ package actualis.web.tapestry.framework;
+ 
+ import java.io.IOException;
+ 
+ import org.apache.commons.logging.Log;
+ import org.apache.commons.logging.LogFactory;
+ import org.apache.hivemind.lib.SpringBeanFactoryHolder;
+ import org.apache.tapestry.Tapestry;
+ import org.apache.tapestry.services.ServiceConstants;
+ import org.apache.tapestry.services.WebRequestServicer;
+ import org.apache.tapestry.services.WebRequestServicerFilter;
+ import org.apache.tapestry.web.WebRequest;
+ import org.apache.tapestry.web.WebResponse;
+ import org.hibernate.FlushMode;
+ import org.hibernate.Session;
+ import org.hibernate.SessionFactory;
+ import org.springframework.dao.DataAccessResourceFailureException;
+ import org.springframework.orm.hibernate3.SessionFactoryUtils;
+ import org.springframework.orm.hibernate3.SessionHolder;
+ import org.springframework.transaction.support.TransactionSynchronizationManager;
+ 
+ public class HibernateRequestFilter implements WebRequestServicerFilter {
+ 
+   private static Log logger = LogFactory.getLog(HibernateRequestFilter.class);
+ 
+   public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
+ 
+   private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
+ 
+   private SpringBeanFactoryHolder _beanFactoryHolder;
+ 
+   /**
+    * Set the bean name of the SessionFactory to fetch from Spring's root
+    * application context. Default is "sessionFactory".
+    * 
+    * @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
+    */
+   public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
+     this.sessionFactoryBeanName = sessionFactoryBeanName;
+   }
+ 
+   /**
+    * Return the bean name of the SessionFactory to fetch from Spring's root
+    * application context.
+    */
+   protected String getSessionFactoryBeanName() {
+     return sessionFactoryBeanName;
+   }
+ 
+   public void service(WebRequest request, WebResponse response,
+       WebRequestServicer servicer) throws IOException {
+     String svcValue = request.getParameterValue(ServiceConstants.SERVICE);
+     if (Tapestry.ASSET_SERVICE.equals(svcValue)) {
+       servicer.service(request, response);
+       return;
+     }
+     logger.debug("entering into Hibernate Request Filter " + " service:" + svcValue
+         + " context:" + request.getContextPath() + " activation:"
+         + request.getActivationPath() + " path:" + request.getPathInfo());
+     SessionFactory sessionFactory = lookupSessionFactory(request);
+     Session session = null;
+ 
+     // single session mode
+ 
+     logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
+     session = getSession(sessionFactory);
+     TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(
+         session));
+ 
+     try {
+       servicer.service(request, response);
+     }
+ 
+     finally {
+       // single session mode
+       TransactionSynchronizationManager.unbindResource(sessionFactory);
+       logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
+       try {
+         closeSession(session, sessionFactory);
+       } catch (RuntimeException ex) {
+         logger.error("Unexpected exception on closing Hibernate Session", ex);
+       }
+     }
+ 
+   }
+ 
+   /** For injection. */
+   public final void setBeanFactoryHolder(SpringBeanFactoryHolder beanFactoryHolder) {
+     _beanFactoryHolder = beanFactoryHolder;
+   }
+ 
+   protected SessionFactory lookupSessionFactory(WebRequest request) {
+     if (logger.isDebugEnabled()) {
+       logger.debug("Using SessionFactory '" + getSessionFactoryBeanName()
+           + "' for OpenSessionInViewFilter");
+     }
+     return (SessionFactory) _beanFactoryHolder.getBeanFactory().getBean(
+         getSessionFactoryBeanName(), SessionFactory.class);
+   }
+ 
+   protected Session getSession(SessionFactory sessionFactory)
+       throws DataAccessResourceFailureException {
+     return openSession(sessionFactory);
+   }
+ 
+   protected Session openSession(SessionFactory sessionFactory)
+       throws DataAccessResourceFailureException {
+     Session session = SessionFactoryUtils.getSession(sessionFactory, true);
+     // session.setFlushMode(FlushMode.NEVER);
+     session.setFlushMode(FlushMode.COMMIT);
+     return session;
+   }
+ 
+   protected void closeSession(Session session, SessionFactory sessionFactory) {
+     session.close();
+     // SessionFactoryUtils.releaseSession(session, sessionFactory);
+   }
+ 
+ }
+ 
+ }}}
+ 
+ 
+ The wiring into tapestry is the following:
+ {{{
+     <service-point id="HibernateServicerFilter"
+         interface="org.apache.tapestry.services.WebRequestServicerFilter">
+         <invoke-factory>
+             <construct class="actualis.web.tapestry.framework.HibernateRequestFilter">
+                                 <set-object property="beanFactoryHolder" value="service:hivemind.lib.DefaultSpringBeanFactoryHolder"/>
+           </construct>
+         </invoke-factory>
+     </service-point>
+     
+     <contribution configuration-id="tapestry.request.WebRequestServicerPipeline">
+         <filter name="HibernateServicerFilter"
+             object="service:HibernateServicerFilter"/>
+     </contribution>
+ }}}
+ 

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org