Ph: 1130177421890
Name:
E-Mail:
URL:
HTML Syntax: Allowed
 on 
[ about:blank ]
Default style (Cherry Eve). Switch styles (Capricorn). XML Feed Calendar
All | technology | misc | jsf | maven | hibernate | spring
ArcMind: JSF, Spring, and/or Hibernate Training, Consulting.  , Spring Whitepaper (Introduction for Managers)
20060203 Friday February 03, 2006

Managed Beans in Spring application context

(Warning: This blog is a personal diary... not a magazine...)
[image]

I held out for a while. I resisted the temptation to switch to Spring 2.0. I can resist no longer.

I am downloding Spring 2.0 M2, which was just released this week so I can get its scoping support.

I've promised the project manager that I would not use any new features except the scoping support, and assured him that the bulk of the framework did not change (as reported in Florida and the Spring conference).

I'll let you know how it goes.

________________________________________

Update:

So far not so good....

It seems they no longer include JSF support in the spring.jar file.

I get this when I first start up the app with the Spring 2.0 M2 jar file.

 

javax.faces.FacesException: java.lang.ClassNotFoundException: org.springframework.web.jsf.DelegatingVariableResolver

at org.apache.myfaces.util.ClassUtils.simpleClassForName(ClassUtils.java:162)

at org.apache.myfaces.config.FacesConfigurator.getApplicationObject(FacesConfigurator.java:506)

at org.apache.myfaces.config.FacesConfigurator.configureApplication(FacesConfigurator.java:446)

at org.apache.myfaces.config.FacesConfigurator.configure(FacesConfigurator.java:130)

...

I think I remember Colin telling me they were going to split out the support. It seems

the JSF support is now in a jar file called spring-web.jar and the hibernate support is in a jar file called spring-hibernate3.jar and the dao support is in a jar file called spring-dao.jar

I understand why they did this, but it still is a pain for now. I wonder if the above is all I will need.

____________________________________________

Seems I was wrong. JSF support is in the spring.jar file as well as DAO support. But it seems Hibernate support is not, but for some reason iBatis is. (I forgot to refresh eclipse after I ran the maven command to copy the jar files to WEB-INF/lib).

I added the following to my pom.xml:

<dependency>

    <groupId>springframework</groupId>

    <artifactId>spring</artifactId>

    <version>2.0-m2</version>

    <scope>compile</scope>

    <type>jar</type>

</dependency>

<dependency>

    <groupId>springframework</groupId>

    <artifactId>spring-hibernate3</artifactId>

    <version>2.0-m2</version>

    <scope>compile</scope>

    <type>jar</type>

</dependency>

a
--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Feb 03 2006, 01:05:19 PM MST Permalink
20060131 Tuesday January 31, 2006

My response to Bob Lee's comments on Spring

(Warning: This blog is a personal diary... not a magazine...)
[image]

I'd like to start off. I truly feel Bob Lee is brilliant. Call it what you want, but I honestly respect Bob's opinion very much. Anyone who responds to his post with personal attacks about his lack of techncial prowess is misled.

I've been using Spring for 2.5 years, and I enjoy using it.

Problems stated:
Many of the complaints you make about Spring are fixed in the 2.0 release. Some of the fixes, I don't agree with, but that is off topic.

No problem using Spring IoC container:
I don't see the problem putting course grained objects in the application context file (Spring IoC container if you will).

First the objects do not have to go in one large file. You can have a file per logical area or divide them up in any way that makes sense.

Second, objects defined in this file can be decorated with Aspects (even introductions) and add behavior that the client object and the object itself don't know about. This is very extensible. I have used this feature, and it is a real effort saver, and with autoproxies, it can be done very succinctly. How would you do this w/o Spring? Roll your own?

AspectJ you say. Well okay, but AspectJ has its own pluses and minuses. I'll leave my thoughts on AspectJ for another day.

Third, you can change the implementation you are injecting. For some client frameworks and application this may not be a big deal for others it is. And yes, sometimes you do want to do this. Take Acegi for example, you can use Spring IoC to configure Acegi. You can inject different authentication mechanisms. You can have a development version of Acegi perhaps using mock authentication, and you can also have an integration environment where you use SiteMinder, CAS, etc. (hopefully one day a production env. and don't forget the QA env.). Acegi is very extensible and by simply reading the easy-to-read Spring app context, you can see its extension points. I have used this feature and it is a real effort saver. How do you suggest accomplishing this w/o Spring? Let me guess roll your own.

Fourth, the Spring application context file is about the easiest thing I know of to read. It is really intuitive. You act as if uses Latin for elements. <etherialize-instantiation> or something. To me, it is the very definition of intuitive.

I feel very strongly that the Spring framework promotes the use of IoC and AOP. Can it be improved? Sure. But, it is the best implementation that I know of. Certainly the most popular as well.

Spring does promote good practices and it is lighter weight than J2EE.

I wrote this white paper that is geared toward developers who are learning about Spring IoC and Spring AOP for the first time. In the paper, I state all of the reasons I feel developers should consider using Spring. I will not repeat them here.

Many of the principles and practices in JEE 5 are very similar to what is practiced in Spring. And Spring, is a lot more elegant, evolvable implementation of these principles and practice. If you know of a better framework, let hear about it.

This is officially my 2 cents...

Now I should go back to billable hours or spend some time with the family.

I've fresh French bread to make.

In other news, my French bread came out perfect.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 31 2006, 06:37:42 PM MST Permalink

Spring under attack?

(Warning: This blog is a personal diary... not a magazine...)
[image]

Spring is not exactly the framework that everyone loves to hate. It seems Bob Lee, a brilliant developer in his own right, does not agree with the implementation of Spring IoC mechanism, and now Spring is under attack by two classes of people: folks who think they are too smart for Spring (misinformed though they are), and people who never really understood the value of Spring, and never took the time to figure it out.

There is nothing you can do about the first group. For the second group, I created this white paper: http://www.arc-mind.com/whitepapers/SpringIntroduction.pdf. The paper is more about the why of IoC, AOP and Spring templates.

Btw Bob Lee is not against IoC or AOP, two foundations of Spring. His point of view is that you should create your own IoC container, and those in-the-know know that he created his own widely unsuccessful AOP framework (which was one of the first and way ahead of its time).

After studying Spring, I could write my own IoC, but why would I want to? Spring does what I need an IoC container to do.

I am not a Spring die hard fanboy see my comments on the JBoss microcontainer, but I do like and use Spring. So maybe Bob is right and Spring IoC could use some overhaul. I am certainly not convinced of this per se.

The parade of misinformation about Spring is shocking. In short, IoC and AOP are good things (Bob of all people acknowledges this.). Spring is the most successful implemetation of those important concepts (IoC and AOP).

Even if Spring were merely an IoC/AOP container, it would be worthy of consideration; however, Spring goes beyond just being an IoC container or an AOP framework. Spring goes one step further than the other containers which support IoX and AOP by building a framework to simplify J2EE development.

Please investigate Spring and make your own decisions.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 31 2006, 04:50:31 PM MST Permalink
20060117 Tuesday January 17, 2006

Update: Logging into Acegi from a JSF action

(Warning: This blog is a personal diary... not a magazine...)
[image]

Tony Kerz pointed this link out to me. http://jroller.com/page/fairTrade?entry=integrating_acegi_and_jsf_revisited

It is a more detailed (step by step) way to integrate JSF and Acegi.

The above link, makes the rest of this a bit out dated.

Well, I found the code example I was lookign for at: http://w.kazed.net:8080/nowhere/space/java/jsf

 

JSF Integration with Acegi

"Acegi is a security toolkit created to be used with Spring. You could use the basic authentication or login form method of authentication. The standard login form authentication requires you to submit a form with the username and password fields to an Acegi URL. This problem with this is that it doesn't integrate well with JSF. You can also use the authentication machinery of Acegi in your own JSF backing bean and perform the authentication." --koert

Koert provides this code example:

public String loginUser()
{
    String result = null;
    Authentication authReq = new UsernamePasswordAuthenticationToken(userName, password);
    Authentication auth = null;
    try {
        auth = authenticationManager.authenticate(authReq);
    } catch (AuthenticationException e) {
        String exceptionMessage = null;
        if (e instanceof AuthenticationServiceException) {
            exceptionMessage = "AUTHENTICATION_SERVICE_EXCEPTION";
        } if (e instanceof BadCredentialsException) {
            exceptionMessage = "BAD_CREDENTIALS_EXCEPTION";
        } if (e instanceof DisabledException) {
            exceptionMessage = "DISABLED_EXCEPTION";
        } if (e instanceof LockedException) {
            exceptionMessage = "LOCKED_EXCEPTION";
        } if (e instanceof ProxyUntrustedException) {
            exceptionMessage = "PROXY_UNTRUSTED_EXCEPTION";
        }
        // Add msg to JSF context
        FacesMessage msg = new FacesMessage(exceptionMessage);
        msg.setSeverity(FacesMessage.SEVERITY_ERROR);
        FacesContext.getCurrentInstance().addMessage(null, msg);           
        // Set ACEGI vars
        Map sessionMap = getExternalContext().getSessionMap();
        sessionMap.put(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY, e);
    }
if (auth != null) { result = "success"; } else { FacesContext facesContext = FacesContext.getCurrentInstance(); FacesMessage facesMessage = new FacesMessage("You have entered an invalid user name and/or password"); facesContext.addMessage("loginForm", facesMessage); result = "failure"; } return result; }

"The Acegi AuthenticationProvider will store the authentication in a ThreadLocal SecureContext object, so that other services could check if the user is authenticated or not." --koert


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 17 2006, 11:55:22 PM MST Permalink

Easiest Acegi setup

(Warning: This blog is a personal diary... not a magazine...)
[image]

Here is the easiest, simplest Acegi 1.0 setup for those who want to get started with Acegi quickly.  I tried to post the listings to my blog to no avail. They just don't fit.

http://www.thearcmind.com/confluence/display/SpribernateSF/Simplest+Acegi+Integration+Possible

 

I am still looking for an example that uses JSF page instead of a straight JSP.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 17 2006, 12:23:43 PM MST Permalink
20060113 Friday January 13, 2006

CONTINUED: Acegi for mere mortals... simple step by step to add Acegi to an application that already has Spring support.

(Warning: This blog is a personal diary... not a magazine...)
[image]

I could not fit the full listings on this blog entry. To see the full listing go to: http://www.thearcmind.com/confluence/display/SpribernateSF/Simplest+Acegi+Integration+Possible.

 

 

Step 2 Add the following to your Spring application context:

 

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

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd" >

<beans>

 

 

 

<bean id="memoryAuthenticationDao" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">

  <property name="userMap">

    <value>

      marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR

      dianne=emu,ROLE_TELLER

      scott=wombat,ROLE_TELLER

      user=pass,ROLE_USER

      peter=opal,disabled,ROLE_TELLER

    </value>

  </property>

</bean>    

 

      <bean id="daoAuthenticationProvider"

            class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">

            <property name="userDetailsService">

                  <ref local="memoryAuthenticationDao" />

            </property>

      </bean>

 

      <bean id="authenticationManager"

            class="org.acegisecurity.providers.ProviderManager">

            <property name="providers">

                  <list>

                        <ref bean="daoAuthenticationProvider" />

                  </list>

            </property>

      </bean>

 

   <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">

      <property name="authenticationManager" ref="authenticationManager"/>

      <property name="authenticationFailureUrl"><value>/acegilogin.jsp?login_error=1</value></property>

      <property name="defaultTargetUrl"><value>/</value></property>

      <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>

   </bean>

 

 

      <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />

 

   <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">

      <property name="allowIfAllAbstainDecisions"><value>false</value></property>

      <property name="decisionVoters">

         <list>

            <ref bean="roleVoter"/>

         </list>

      </property>

   </bean>

  

      <bean id="securityEnforcementFilter"

            class="org.acegisecurity.intercept.web.SecurityEnforcementFilter">

            <property name="filterSecurityInterceptor">

                  <ref bean="filterInvocationInterceptor" />

            </property>

            <property name="authenticationEntryPoint">

                  <ref bean="authenticationEntryPoint" />

            </property>

      </bean>

 

      <bean id="httpSessionIntegrationFilter"

            class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">

            <property name="context"> Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 13 2006, 06:29:03 PM MST Permalink

Acegi for mere mortals... simple step by step to add Acegi to an application that already has Spring support.

(Warning: This blog is a personal diary... not a magazine...)
[image]

I could not fit the full listings on this blog entry. To see the full listing go to: http://www.thearcmind.com/confluence/display/SpribernateSF/Simplest+Acegi+Integration+Possible.

I have Acegi integrated into Presto. This is the simplest possible Acegi integration. It took a while because the ?documentation? (someone?s blog entry) for the simplest setup was for pre 1.0.

From the .9 release to the 1.0 release (1.0 RC1 really), all of the package names changed (most a bit; some a lot). The class names of a few classes changed as well.

This is the simplest setup. This will allow us to write the integration point between our JSF security and Acegi (The prototype was against CMS). Then that task will be done.

Acegi already supports siteminder (out of the box). It should be an easy task to make it work with our internal security system. Also, you can write a custom AuthenticationProvider to integrate with our Internal corporate Authentication system. This setup does not use siteminder.

Having problems with the setup forced me to read the official documents which are really well written. It turned out to be a blessing in disguise. I feel confident, Acegi can work for us. I've been on projects that used Acegi, but it just worked and I didn't do much with it.

I?d like to change my earlier recommendation. Let?s use Acegi on our next project it should be straightforward. I'll volunteer to write the AuthenticationProvider or see if we can hit our system through LDAP integration already included with Acegi.

BTW I had to disable the MyFaces extension filters for security related links (login). It seem to conflict with Acegi. I am not sure why yet.

For those interested,

 

Here are the steps to add Acegi to an application that already has Spring support.

 

Step 1 Add the Acegi filter to web.xml:

 

      <filter>

            <filter-name>Acegi Filter Chain Proxy</filter-name>

            <filter-class>

                  org.acegisecurity.util.FilterToBeanProxy

            </filter-class>

            <init-param>

                  <param-name>targetClass</param-name>

                  <param-value>

                        org.acegisecurity.util.FilterChainProxy

                  </param-value>

            </init-param>

      </filter>

 

      <filter-mapping>

            <filter-name>Acegi Filter Chain Proxy</filter-name>

            <url-pattern>/*</url-pattern>

      </filter-mapping>

 

Step 2 Add the following to your Spring application context:

 

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

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd" >

<beans>

 

<bean id="memoryAuthenticationDao" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">

  <property name="userMap">

    <value>

      marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR

      dianne=emu,ROLE_TELLER

      scott=wombat,ROLE_TELLER

      user=pass,ROLE_USER

      peter=opal,disabled,ROLE_TELLER

    </value>

  </property>

</bean>    

 

      <bean id="daoAuthenticationProvider"

            class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">

            <property name="userDetailsService">

                  <ref local="memoryAuthenticationDao" />

            </property>

      </bean>

 

      <bean id="authenticationManager"

            class="org.acegisecurity.providers.ProviderManager">

            <property name="providers">

                  <list>

                        <ref bean="daoAuthenticationProvider" />

                  </list>

            </property>

      </bean>

 

   <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">

      <property name="authenticationManager" ref="authenticationManager"/>

      <property name="authenticationFailureUrl"><value>/acegilogin.jsp?login_error=1</value></property>

      <property name="defaultTargetUrl"><value>/</value></property>

      <property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>

   </bean>

 

 

      <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />

see http://jroller.com/page/RickHigh?entry=contiued_acegi_for_mere_mortals for the rest of the listings.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Jan 13 2006, 06:25:14 PM MST Permalink
20051102 Wednesday November 02, 2005

Spring/Hibernate, JSF and Portlets: OpenSessionInPhaseListener and SpringBasedPhaseListenerProxy

(Warning: This blog is a personal diary... not a magazine...)
[image]

Configuring Hibernate, Spring, Portlets, and OpenInSessionPhaseListener with IBM WebSphere Portal Server

NOTE: The OpenSessionInViewFilter does not work with Portlets as ServletFilters do not work wtih Portals (Liferay and WebSphere portal server for example). I had to create an OpenSessionInViewPhaseListener. A PhaseListener is a JSF lifecycle listener. Servlet Filters do not work for Portlets (in the IBM and Liferay implementation) see OpenInSessionViewPhaseListener and Configuring Hibernate, Spring, Portlets, and OpenInSessionViewFilter with IBM WebSphere Portal Server for more details. This solutions only works if you are doing JSF and Portlet 168 together. If you are doing just Portlet 168, this won't work. If you are doing just JSF, you don't need this at all.

Once the OpenSessionInViewPhaseListener was working in both Pluto and IBM WebSphere, I realized that the target project uses 9 different databases thus has 9 different SessionFactories configured in Spring applicationContext. OpenSessionInViewFilter and OpenSessionInViewPhaseListener only work with 1 sessionFactory at a time unless you subclass them. This does not seem like a good idea. We created a generic JSF/Spring Application Context bridge.

The SpringBasedPhaseListenerProxy allows JSF PhaseListeners to be registered in Spring Application Context; thus, allows PhaseListeners the benefits of Spring's AOP support and Dependency Injection (IoC). It was created so we could confgure OpenSessionInViewPhaseListener instead of having 9 subclasses that basically do very little.

You can see more about SpringBasedPhaseListenerProxy at the end of this file.

(see original at Configuring Hibernate, Spring, Portlets, and OpenInSessionPhaseListener with IBM WebSphere Portal Server)

Filters Don't Work with IBM Portlets

ServletFilters do not work with Portlets. Thus, OpenSessionInViewFilter will not work with Portlets. I confirmed this. I wrote a simple filter (prints out a message before and after each request). I tried it in a non-portal env. (Tomcat) and in WebSphere Portal server. It worked in Tomcat and did not work in WebSphere portal server. I also found some discussion on line that says the same thing, and Bob S. said the same thing. So I come to this conclusion based on testing, experience of other people and documentation online.

Work around for Filters not working with Portlets: OpenSessionInViewPhaseListener

My original idea was this: to get around the OpenInSessionViewFilter, we can use the Spring JSF project and the HibernateInterceptor to decorate the backing beans. Upon further reflection, it would not work b/c the JSP hits the backing bean several times.

Then walking home from work, the idea of an OpenSessionInViewPhaseListener. I created a quick and dirty prototype that worked.

This has been greatly improved since my last posting.

OpenSessionInViewPhaseListener.java
package com.goldenpath.jsf.hibernate.support;

/*
 *  Much of this code was liberally stolen from the OpenInSessionViewFilter 
 * from the Spring project.
 * 
 * 
 *
 */

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;


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;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;



/**
 * This class is functionally equivalent to OpenSessionInViewFilter.
 * A good portion of the code was copied from OpenSessionInViewFilter.
 * OpenSessionInViewPhaseListener exists because ServletFilters do now work with 
 * 168 Portlets in Liferay Portal and the IBM WebSphere Portal Server.
 * 
 * This seems to be a limitation of the JSF 168 spec. Future versions
 * will support PortletFilters.
 * 
 * This class exists to provide OpenSessionInViewFilter functionality for 
 * JSF based 168 portlets running in WebSphere Portal Server, and Liferay 
 * (and any other server where ServletFilters do not work wtih Portlets).
 * 
 * @author Rick
 *
 */
public class OpenSessionInViewPhaseListener implements PhaseListener {

        public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";


        private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;

        private boolean singleSession = true;
        
        /**
         * Holds the thread data for the current request cycle.
         */
        private static ThreadLocal ts = new ThreadLocal();
        
        /** This class is a bit of hack
         * In the OpenSessionInViewFilter, the filtering is accomplished in one method.
         * This is not an option with this approach.
         * This class holds all of the local variables that were in the original doFilter method
         * of OpenSessionInViewFilter.
         * @author Rick
         *
         */
        private class ThreadData {
                private boolean participate;
                private Session session;
                private SessionFactory sessionFactory;
                private int beforeTimes;
                private int afterTimes;
        }
        
        /** @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         * 
         */
        public void beforePhase(PhaseEvent pe) {
                /* commons-logging is not working yet in WebSphere this is temporary */
                System.out.println("######## BEFORE PHASE = " + pe.getPhaseId() + " viewId= " 
                                + (pe.getFacesContext().getViewRoot()!=null ? pe.getFacesContext().getViewRoot().getId(): "no view root"));
                
                /* Setup the Hiberante Spring in the current thread using Spring API */
                if (pe.getPhaseId() == PhaseId.RESTORE_VIEW) {
                        ThreadData td = getThreadData();
                        /* This code is here b/c MyFaces seems to call every phase handler method twice. */
                        if (td.beforeTimes==0) {
                                setupSession(pe, td);
                        }
                } /* HACK ALERT!
                     The before restore_view does not get called for Apache JSF Portlet 
                     bridge when first viewing the JSF Portlet.
                         Thus we will do this just before the render_response phase.*/ 
                else if (pe.getPhaseId() == PhaseId.RENDER_RESPONSE){
                                
                                ThreadData td = getThreadDataNoCreate();
                                if (td==null){
                                        System.out.println("BEFORE RENDER_RESPONSE SETTING UP SESSION");
                                        td = getThreadData();
                                        setupSession(pe, td);                                   
                                }
                        
                }
        }
        
        /** Get the current thread data */
        private ThreadData getThreadData() {
                ThreadData td = (ThreadData) ts.get();
                if (td == null) {
                        td = new ThreadData();
                        ts.set(td);
                } else {
                        td.beforeTimes++;
                }
                return td;

        }

        /** Get the thread data don't create it if it does not exist */
        private ThreadData getThreadDataNoCreate() {
                ThreadData td = (ThreadData) ts.get();
                return td;

        }
        
        /* Setup the Hiberante session. This was taken from the first half of the doFilter method. */
        private void setupSession(PhaseEvent pe, ThreadData td) {
                System.out.println("SETUP SESSION");
                td.sessionFactory = lookupSessionFactory(pe.getFacesContext());
                Assert.notNull(td.sessionFactory);
                td.session = null;
                td.participate = false;

                if (isSingleSession()) {
                        // single session mode
                 if (TransactionSynchronizationManager.hasResource(td.sessionFactory)) {
                                // Do not modify the Session: just set the participate flag.
                         td.participate = true;
                        }
                        else {
                                td.session = getSession(td.sessionFactory);
                                TransactionSynchronizationManager.bindResource(td.sessionFactory, new SessionHolder(td.session));
                        }
                }
                else {
                        // deferred close mode
                 if (SessionFactoryUtils.isDeferredCloseActive(td.sessionFactory)) {
                                // Do not modify deferred close: just set the participate flag.
                         td.participate = true;
                        }
                        else {
                                SessionFactoryUtils.initDeferredClose(td.sessionFactory);
                        }
                }
        }

        /** @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         * 
         */     
        public void afterPhase(PhaseEvent pe) {
                System.out.println("AFTER = " + pe.getPhaseId() + " viewId= " + pe.getFacesContext().getViewRoot().getId());
                
                

                if (pe.getPhaseId() == PhaseId.RENDER_RESPONSE) {
                        System.out.println("############################# IN AFTER RENDER_RESPONSE PHASE CLEANING UP SESSION ") ;
                        ThreadData td = getThreadDataNoCreate();
                        if (td==null) return;
                        td.afterTimes++;
                        cleanupSession(td);
                        
                        ts.set(null);
                }
        }

        /* Cleanup the Hiberante session. This was taken from the first half of the doFilter method. */
        private void cleanupSession(ThreadData td) {
                System.out.println("CLEAN UP SESSION");
                if (!td.participate) {
                        if (isSingleSession()) {
                                // single session mode                                 
                         try {
                                        TransactionSynchronizationManager.unbindResource(td.sessionFactory);
                                        System.out.println("UNBINDING RESOURCE");
                                        closeSession(td.session, td.sessionFactory);
                                        System.out.println("SESSION CLOSED");
                                }
                                catch (RuntimeException ex) {
                                        ex.printStackTrace();
                                }
                        }
                        else {
                                // deferred close mode
                         SessionFactoryUtils.processDeferredClose(td.sessionFactory);
                        }
                }
        }


        /**
         * Register for all Phase Events.
         *  @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         * 
         */     
        public PhaseId getPhaseId() {
                return PhaseId.ANY_PHASE;
        }



        /**
         * 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;
        }

        /**
         * Set whether to use a single session for each request. Default is "true".
         * <p>If set to false, each data access operation or transaction will use
         * its own session (like without Open Session in View). Each of those
         * sessions will be registered for deferred close, though, actually
         * processed at request completion.
         * @see SessionFactoryUtils#initDeferredClose
         * @see SessionFactoryUtils#processDeferredClose
         */
        public void setSingleSession(boolean singleSession) {
                this.singleSession = singleSession;
        }

        /**
         * Return whether to use a single session for each request.
         */
        protected boolean isSingleSession() {
                return singleSession;
        }




        /**
         * Look up the SessionFactory that this filter should use.
         * <p>Default implementation looks for a bean with the specified name
         * in Spring's root application context.
         * @return the SessionFactory to use
         * @see #getSessionFactoryBeanName
         */
        protected SessionFactory lookupSessionFactory(FacesContext facesContext) {              
                WebApplicationContext wac = FacesContextUtils.getRequiredWebApplicationContext(facesContext);
                return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
        }

        /**
         * Get a Session for the SessionFactory that this filter uses.
         * Note that this just applies in single session mode!
         * <p>The default implementation delegates to SessionFactoryUtils'
         * getSession method and sets the Session's flushMode to NEVER.
         * <p>Can be overridden in subclasses for creating a Session with a custom
         * entity interceptor or JDBC exception translator.
         * @param sessionFactory the SessionFactory that this filter uses
         * @return the Session to use
         * @throws DataAccessResourceFailureException if the Session could not be created
         * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
         * @see org.hibernate.FlushMode#NEVER
         */
        protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
                Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                session.setFlushMode(FlushMode.NEVER);
                return session;
        }

        /**
         * Close the given Session.
         * Note that this just applies in single session mode!
         * <p>The default implementation delegates to SessionFactoryUtils'
         * releaseSession method.
         * <p>Can be overridden in subclasses, e.g. for flushing the Session before
         * closing it. See class-level javadoc for a discussion of flush handling.
         * Note that you should also override getSession accordingly, to set
         * the flush mode to something else than NEVER.
         * @param session the Session used for filtering
         * @param sessionFactory the SessionFactory that this filter uses
         */
        protected void closeSession(Session session, SessionFactory sessionFactory) {
                SessionFactoryUtils.releaseSession(session, sessionFactory);
        }
        
}

There are no test cases yet. I wanted to see if it would work (prototype) before spending a lot of time writing test cases (since I was not sure this idea would fly). As you can see, I hacked up the OpenSessionInViewFilter code. I had to split the doFilter code into code that executes before the restore view phase and code that executes after render response. I took all of the local variable methods in the doFilter method and rammed them into a ThreadLocal via a hacked ThreadData class. Both MyFaces and IBM Faces call the phases multiple times so I hacked together a routine whereby our getting-a-hibernate-session-per-request is only called once per request. I'll need to change the sysouts to logging and write some test, but other than that it pretty much works.

You have to install it in the faces-config.xml file as follows:

faces-config.xml
<lifecycle>
                <phase-listener>com.qualcomm.jsf.hibernate.jsf.support.OpenSessionInViewPhaseListener</phase-listener>
        </lifecycle>

SIDE NOTE: Log4J Logging does not work with IBM WebSphere 5.1 Portal server

I can't find an env where log4j logging (needed for debugging Hibernate and Spring) works. I've tried every commons-logging possibility listed on the IBM site. (I listed some of this trial and tribulation here: Jar file hell, commons-logging and IBM WebSphere.)

SpringBasedPhaseListenerProxy

SpringBasedPhaseListenerProxy is as follows:

SpringBasedPhaseListenerProxy.java
import java.util.Iterator;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;


import org.springframework.context.ApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;

/**
 * Allows JSF PhaseListeners to be registered in Spring Application Context.
 * Allows PhaseListeners the benefits of Spring's AOP support and Dependency Injection (IoC).
 * @author Rick
 *
 */
public class SpringBasedPhaseListenerProxy implements PhaseListener {

        Map listeners = null;
        
        /** Get the application context */
        protected ApplicationContext applicationContext() {             
                return FacesContextUtils.getRequiredWebApplicationContext(FacesContext.getCurrentInstance());
        }
        
        /** Get all of the listeners in the application context */
        protected void initListOfPhaseListeners () {
                listeners = applicationContext().getBeansOfType(PhaseListener.class);
        }
        
        {//instance initializer gets called before class constructor.
         initListOfPhaseListeners();
        }

        /** Helper interface for processing all of the phase listeners */
        interface ForEachListener { void processListener(PhaseListener listener);
        }
        
        /** 
         * Process all phases. 
         * @param pe PhaseEvent from Before or After
         * @param forEach to use for processing this list of PhaseListeners.
         * */
        private void phaseProcessing(PhaseEvent pe, final ForEachListener forEach) {
                Iterator iterator = listeners.values().iterator();
                while (iterator.hasNext()) {
                        PhaseListener listener = (PhaseListener) iterator.next();
                        if (shouldExecute(listener, pe)) {
                                forEach.processListener(listener);
                        }
                }
        }

        /**
         * Process listeners for afterPhase.
         * @param pe Phase Event
         *  @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         */
        public void afterPhase(final PhaseEvent pe) {
                phaseProcessing(pe, 
                                new ForEachListener() { //anonymous inner class
                                 public void processListener(PhaseListener listener){listener.afterPhase(pe);}});
        }
        
        /**
         *  Process listeners for beforePhase.
         *  @param pe Phase Event
         *   @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         */
        public void beforePhase(final PhaseEvent pe) {
                phaseProcessing(pe, new ForEachListener() {public void processListener(PhaseListener listener){listener.beforePhase(pe);}});
        }

        /** 
         * Determine if the current Phase listener should process this PhaseEvent.
         * @param listener the current Phase Listener.
         * @param pe the current PhaseEvent 
         */
        private boolean shouldExecute(PhaseListener listener, PhaseEvent pe) {
                PhaseId phaseId = pe.getPhaseId();
                return phaseId == listener.getPhaseId() || PhaseId.ANY_PHASE == listener.getPhaseId();  
        }


        /**
         *  @see javax.faces.event.PhaseListener(javax.faces.event.PhaseEvent pe)
         */
        public PhaseId getPhaseId() {
                return PhaseId.ANY_PHASE;
        }

}

Now instead of configuring specific PhaseListeners in faces-config.xml, you can register this in faces-config.xml and put all of the other PhaseListners in the application context where you can inject properties and add AOP proxies.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)






If you leave a comment and it gets marked as SPAM. Don't worry. I will unmark it as SPAM. Unless of course it is SPAM, then I will melt your car.

Nov 02 2005, 05:15:08 PM MST Permalink
20051026 Wednesday October 26, 2005

Work Around: Portlets: OpenSessionInViewPhaseListener for Filters Don't Work with IBM Portlets

(Warning: This blog is a personal diary... not a magazine...)
[image]

The OpenSessionInViewFilter does not work with Portlets. Since we are using JSF, I created an OpenSessionInViewPhaseListener to simulate OpenSessionInViewFilter . The bottom line seems to be Servlet Filters do not work for Portlets (in the IBM and Liferay implementation) see OpenInSessionViewPhaseListener and Configuring Hibernate, Spring, Portlets, and OpenInSessionViewFilter with IBM WebSphere Portal Server for more details.

 

OpenSessionInViewPhaseListener should work with both IBM Portlets and Liferay Portlets (possibly others). LifeRay and IBM Portlets do not allow ServletFilters to decorate Portlets. (Does JetSpeed? I don't know. IBM is based on an earlier version of JetSpeed so I am going to guess you need this for JetSpeed as well.)

JSR-168 does not have a Portlet filter. Of course the above will only work if you are using JSF and Portlets (with IBM WebSphere Portal Server, JetSpeed and LifeRay). It has only been tested wtih IBM WebSphere Portal Server.

(This is very much a work in progress. Take it with a grain of salt.)

Filters Don't Work with IBM Portlets

ServletFilters do not work with Portlets. Thus, OpenSessionInViewFilter will not work with Portlets. I confirmed this. I wrote a simple filter (prints out a message before and after each request). I tried it in a non-portal env. (Tomcat) and in WebSphere Portal server. It worked in Tomcat and did not work in WebSphere portal server. I also found some discussion on line that says the same thing, and Bob said the same thing. So I come to this conclusion based on testing, experience of other people and documentation online.

Work around for Filters not working with Portlets: OpenSessionInViewPhaseListener

My original idea was this: to get around the OpenInSessionViewFilter, we can use the Spring JSF project and the HibernateInterceptor to decorate the backing beans. Upon further reflection, it would not work b/c the JSP hits the backing bean several times.

Then walking home from work, the idea of an OpenSessionInViewPhaseListener. I created a quick and dirty prototype that worked.

OpenSessionInViewPhaseListener.java
package com.goldenpath.jsf.hibernate.support;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

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;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.jsf.FacesContextUtils;


public class OpenSessionInViewPhaseListener implements PhaseListener {

        private static ThreadLocal ts = new ThreadLocal();
        private class ThreadData {
                private boolean participate;
                private Session session;
                private SessionFactory sessionFactory;
                private int beforeTimes;
                private int afterTimes;
        }
        
        public void beforePhase(PhaseEvent pe) {
                System.out.println("BEFORE PHASE = " + pe.getPhaseId());
                
                ThreadData td = (ThreadData) ts.get();
                if (td == null) {
                        td = new ThreadData();
                } else {
                        td.beforeTimes++;
                        System.out.println("This phase executed this many times for this thread: " + td.beforeTimes);
                        return;
                }
                if (pe.getPhaseId() == PhaseId.RESTORE_VIEW) {
                        System.out.println("##########################             IN RESTORE VIEW PHASE");
                        td.sessionFactory = lookupSessionFactory(pe.getFacesContext());
                        td.session = null;
                        td.participate = false;

                        if (isSingleSession()) {
                                // single session mode
                         if (TransactionSynchronizationManager.hasResource(td.sessionFactory)) {
                                        // Do not modify the Session: just set the participate flag.
                                 td.participate = true;
                                }
                                else {
                                        td.session = getSession(td.sessionFactory);
                                        TransactionSynchronizationManager.bindResource(td.sessionFactory, new SessionHolder(td.session));
                                }
                        }
                        else {
                                // deferred close mode
                         if (SessionFactoryUtils.isDeferredCloseActive(td.sessionFactory)) {
                                        // Do not modify deferred close: just set the participate flag.
                                 td.participate = true;
                                }
                                else {
                                        SessionFactoryUtils.initDeferredClose(td.sessionFactory);
                                }
                        }

                }
                ts.set(td);
        }

        public void afterPhase(PhaseEvent pe) {
                System.out.println("AFTER = " + pe.getPhaseId());
                
                

                if (pe.getPhaseId() == PhaseId.RENDER_RESPONSE) {
                        System.out.println("################################### IN RENDER_RESPONSE PHASE ");
                        ThreadData td = (ThreadData) ts.get();
                        if (td==null) return;
                        if (td.afterTimes!=0 ) {
                                System.out.println("this phase has been executed this many times for this thread " + td.afterTimes);
                                td.afterTimes++;
                                return;
                        }
                        td.afterTimes++;
                        if (!td.participate) {
                                if (isSingleSession()) {
                                        // single session mode
                                 TransactionSynchronizationManager.unbindResource(td.sessionFactory);
                                        
                                        try {
                                                closeSession(td.session, td.sessionFactory);
                                        }
                                        catch (RuntimeException ex) {
                                                ex.printStackTrace();
                                        }
                                }
                                else {
                                        // deferred close mode
                                 SessionFactoryUtils.processDeferredClose(td.sessionFactory);
                                }
                        }
                        
                        ts.set(null);
                }
        }


        public PhaseId getPhaseId() {
                return PhaseId.ANY_PHASE;
        }

        public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";


        private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;

        private boolean singleSession = true;


        /**
         * 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;
        }

        /**
         * Set whether to use a single session for each request. Default is "true".
         * <p>If set to false, each data access operation or transaction will use
         * its own session (like without Open Session in View). Each of those
         * sessions will be registered for deferred close, though, actually
         * processed at request completion.
         * @see SessionFactoryUtils#initDeferredClose
         * @see SessionFactoryUtils#processDeferredClose
         */
        public void setSingleSession(boolean singleSession) {
                this.singleSession = singleSession;
        }

        /**
         * Return whether to use a single session for each request.
         */
        protected boolean isSingleSession() {
                return singleSession;
        }




        /**
         * Look up the SessionFactory that this filter should use.
         * <p>Default implementation looks for a bean with the specified name
         * in Spring's root application context.
         * @return the SessionFactory to use
         * @see #getSessionFactoryBeanName
         */
        protected SessionFactory lookupSessionFactory(FacesContext facesContext) {
                
                
//                     WebApplicationContext wac =
//                                      WebApplicationContextUtils.getRequiredWebApplicationContext(sContext);
                 WebApplicationContext wac = FacesContextUtils.getRequiredWebApplicationContext(facesContext);
                        return (SessionFactory) wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
        }

        /**
         * Get a Session for the SessionFactory that this filter uses.
         * Note that this just applies in single session mode!
         * <p>The default implementation delegates to SessionFactoryUtils'
         * getSession method and sets the Session's flushMode to NEVER.
         * <p>Can be overridden in subclasses for creating a Session with a custom
         * entity interceptor or JDBC exception translator.
         * @param sessionFactory the SessionFactory that this filter uses
         * @return the Session to use
         * @throws DataAccessResourceFailureException if the Session could not be created
         * @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
         * @see org.hibernate.FlushMode#NEVER
         */
        protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
                Session session = SessionFactoryUtils.getSession(sessionFactory, true);
                session.setFlushMode(FlushMode.NEVER);
                return session;
        }

        /**
         * Close the given Session.
         * Note that this just applies in single session mode!
         * <p>The default implementation delegates to SessionFactoryUtils'
         * releaseSession method.
         * <p>Can be overridden in subclasses, e.g. for flushing the Session before
         * closing it. See class-level javadoc for a discussion of flush handling.
         * Note that you should also override getSession accordingly, to set
         * the flush mode to something else than NEVER.
         * @param session the Session used for filtering
         * @param sessionFactory the SessionFactory that this filter uses
         */
        protected void closeSession(Session session, SessionFactory sessionFactory) {
                SessionFactoryUtils.releaseSession(session, sessionFactory);
        }
        
}

There are no test cases yet. I wanted to see if it would work (prototype) before spending a lot of time writing test cases (since I was not sure this idea would fly). As you can see, I hacked up the OpenSessionInViewFilter code. I had to split the doFilter code into code that executes before the restore view phase and code that executes after render response. I took all of the local variable methods in the doFilter method and rammed them into a ThreadLocal via a hacked ThreadData class. Both MyFaces and IBM Faces call the phases multiple times so I hacked together a routine whereby our getting-a-hibernate-session-per-request is only called once per request. I'll need to change the sysouts to logging and write some test, but other than that it pretty much works.

You have to install it in the faces-config.xml file as follows:

faces-config.xml
<lifecycle>
                <phase-listener>com.goldenpath.jsf.hibernate.support.OpenSessionInViewPhaseListener</phase-listener>
        </lifecycle>

SIDE NOTE: Log4J Logging does not work with IBM WebSphere 5.1 Portal server

I can't find an env where log4j logging (needed for debugging Hibernate and Spring) works. I've tried every commons-logging possibility listed on the IBM site. (I listed some of this trial and tribulation here: Jar file hell, commons-logging and IBM WebSphere.)

h3.Useless Thoughts by Jack Handy

I decided to end my blog entries like David Geary with a song or poem blurb that reflects the impact that my cultural heritage had on my outlook for life. Here Goes:

"I am the king of rock there aint none higher... sucker MCs should call me sire." --Run-DMC 1985.

Yo!

And I want to see if anyone reads this far.


--Rick Hightower
Email: rhightower AT arc DASH mind DOT com

[image] Run more. Lift more. Play more. Play hard. Procrastinate less. Don't waste time. Time is short. Be bold. Be nice. [image]

"I'm personally looking forward to having my ideas used and improved on by others." --Paul Penfield, Jr (Engineer, MIT)