0

I'm working on a web application using Java EE, Spring, EclipseLink and Glassfish server.

My application was working just fine before I tried to change my bean configuration in order to inject the entitymanager with Spring and to enable better transaction management... I don't get it to work... (before that I had it all the same, except I had to call emf.createEntityManager() before each database request).

Here are my configuration files :

  • web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
    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_3_0.xsd"
    id="WebApp_ID" version="3.0">
    
     <!-- jsp config => automatic inclusions in all jsp files -->
    <jsp-config>
       <jsp-property-group>
         <url-pattern>/WEB-INF/views/*</url-pattern>
        <include-prelude>taglibs.jsp</include-prelude>
        <include-prelude>setLanguage.jsp</include-prelude>
    </jsp-property-group>
    </jsp-config>
    <display-name>MEANS</display-name>
    
    <!-- Beans in these files will makeup the configuration of the root web 
    application context -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/applicationContext.xml</param-value>
    </context-param>
    
     <!-- Bootstraps the root web application context before servlet initialization -->
      <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    <!-- Deploys the 'dispatcher' dispatcher servlet whose configuration resides 
    in /WEB-INF/spring/mvc-config.xml -->
     <servlet>
        <servlet-name>dispatcher</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/mvc-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
       <servlet-name>dispatcher</servlet-name>
       <url-pattern>*.do</url-pattern><!-- detect all urls ending with ".do" -->
    </servlet-mapping>
    
    <!-- Security Spring filter -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
       <filter-name>springSecurityFilterChain</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <session-config>
       <session-timeout>30</session-timeout><!-- the session is automatically 
        disconnected after 30 min of inactivity -->
    </session-config>
    
     <listener id="myLogger">
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
     </listener>
    
    <welcome-file-list>
         <welcome-file>index.do</welcome-file>
      </welcome-file-list>
    </web-app>
    
  • security-config.xml:

          <?xml version="1.0" encoding="UTF-8"?>
           <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             xmlns:util="http://www.springframework.org/schema/util"
         xmlns:security="http://www.springframework.org/schema/security"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
                       http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                       http://www.springframework.org/schema/util 
                       http://www.springframework.org/schema/util/spring-util-3.1.xsd
                       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
    
        <security:authentication-manager><!-- custom authentication manager-->
          <security:authentication-provider
        user-service-ref="userService">
          <security:password-encoder ref="passwordEncoder">
             <security:salt-source user-property="username" />
           </security:password-encoder>
          </security:authentication-provider>
        </security:authentication-manager>
    
       <bean id="passwordEncoder"
    class="org.springframework.security.authentication.encoding.MessageDigestPasswordEncoder">
    <constructor-arg value="MD5" />
      </bean>
    
      <bean id="userService" class="fr.services.security.MyUserDetailsService"><!-- custom userDetailService -->
            <constructor-arg ref="myDAOFactory"/>
      </bean>
    
       <!-- disable authentication for those free access components-->
       <security:http pattern="/login.jsp*" security="none" />
       <security:http pattern="/disconnected.jsp*" security="none" />
       <security:http pattern="/css/**" security="none" />
       <security:http pattern="/javascript/**" security="none" />
       <security:http pattern="/jquery/**" security="none" />
    
      <security:http access-denied-page="/denied.jsp"   use-expressions="true">
    <security:form-login login-page="/login.jsp"
        authentication-failure-url="/login.jsp?errorMessage=invalidConnection"
        default-target-url='/index.do' always-use-default-target='false' />
    <security:intercept-url pattern="/admin*.do"
        access="hasRole('ROLE_ADMINISTRATEUR')" />
    <security:intercept-url pattern="/access*.do"
        access="isAnonymous() or isAuthenticated()" />
    <security:intercept-url pattern="/**"
        access="isAuthenticated()" />
    <security:logout logout-success-url="/disconnected.jsp" />
      </security:http>
    
       </beans>
    
  • persistence-config.xml:

      <?xml version="1.0" encoding="UTF-8"?>
       <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    
    
      <bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource"
      p:driverClassName="org.postgresql.Driver"
      p:url="jdbc:postgresql://localhost:5432/databaseName"
      p:username="postgres"
      p:password="pwd" />
    
     <tx:annotation-driven/>
    
     <tx:jta-transaction-manager>
       <property name="entityManagerFactory" ref="entityManagerFactory" />
     </tx:jta-transaction-manager>
    
     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="databaseName" />
     </bean>
    
     <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
       <property name="entityManagerFactory" ref="entityManagerFactory" />
      </bean>
     <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    
    </beans>
    
  • application-context.xml:

    <?xml version="1.0" encoding="UTF-8"?>
     <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:p="http://www.springframework.org/schema/p"
         xmlns:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
      <import resource="persistence-config.xml"/>
      <import resource="security-config.xml"/>
    
      <context:component-scan base-package="fr" />
    
      <context:annotation-config/>
    
    
        <!--  Define the location of the property file to change properties dependent on the application environment -->
         <context:property-placeholder location="/WEB-INF/configuration.properties" />
    
    </beans>
    
  • mvc-config.xml:

       <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
            http://www.springframework.org/schema/tx  
            http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context.xsd">
    
     <import resource="persistence-config.xml"/>
     <import resource="security-config.xml"/>
    
    
        <!-- Spring MVC Support for annotations (JSR-303) -->
      <mvc:annotation-driven>
    
      </mvc:annotation-driven>
    
      <context:component-scan base-package="fr.controller, fr.dao, fr.services" />
    
      <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    p:prefix="/WEB-INF/views/" p:suffix=".jsp" />
    
      <bean id="urlMap" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="urlMap">
        <map>
            <entry key="accessPasswordLost.do" value-ref="urlFilenameViewController"/>
            <entry key="documentation.do" value-ref="urlFilenameViewController"/>
            <entry key="hierarchyDialog.do" value-ref="urlFilenameViewController"/>
            <entry key="nameDialog.do" value-ref="urlFilenameViewController"/>
            <entry key="openCopyDialog.do" value-ref="urlFilenameViewController"/>
            <entry key="tableErosionParameterLS.do" value-ref="urlFilenameViewController"/>
            <entry key="training.do" value-ref="urlFilenameViewController"/>
            <entry key="userSettings.do" value-ref="urlFilenameViewController"/>
        </map>
    </property>
      </bean>
      <!-- For direct mapping between URL (i.e. index.htm index) and the JSP to render -->
       <bean id="urlFilenameViewController"
    class="org.springframework.web.servlet.mvc.UrlFilenameViewController">
       </bean>
    
       <import resource="aspects-config.xml"/>
    
     </beans>
    

I'm not sure about the imports for persistence-config and security-config...

To make sure, you have all the information, here is the class MyDAOFactory which gets the entitymanager injected:

 /**
 * A factory for creating DAO objects.
 */
@Component
public class MyDAOFactory implements IMyDAOFactory{

/** The map dao. */
private Map<Class<?>, IDao<?>> mapDAO;

/** The entity manager. */
@PersistenceContext(unitName="databaseName")
public EntityManager em;


/**
 * Instantiates a new my dao factory.
 */

public MyDAOFactory() {

    mapDAO = new HashMap<Class<?>, IDao<?>>();

}


/**
 * Gets the em.
 *
 * @return the em
 */
public EntityManager getEm() {
    return em;
}

/**
 * Sets the em.
 *
 * @param emf the new em
 */
public void setEm(EntityManager em) {
    this.em = em;
}



@Override
public <T extends Serializable> IDao<T> getBasicDAO(Class<T> entity) {
    if(mapDAO.containsKey(entity)){
        return (IDao<T>) mapDAO.get(entity);
    }else{
        BasicDAO<T> dao = new BasicDAO<T>(entity);
        dao.setEm(em);
        mapDAO.put(entity,  dao);
        return dao;
    }
}

}

And the custom UserDetailsService:

  @Transactional
  public class MyUserDetailsService implements UserDetailsService {

  /** The my dao factory. */
  IMyDAOFactory myDAOFactory;

/**
 * Instantiates a new means user details service.
 *
 */
public MyUserDetailsService(IMyDAOFactory myDAOFactory) {
    this.myDAOFactory = myDAOFactory;
}


/* (non-Javadoc)
 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
 */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException{
    IDao<Utilisateur> userDAO = this.myDAOFactory.getBasicDAO(Utilisateur.class);
    Map<String, String> map = new HashMap<String, String>();
    map.put("loginLdap", username);
    Utilisateur user = userDAO.findByAttributes(map).get(0);
    IMyUserDetails userDetails = new MyUserDetails(user.getLoginLdap(), user.getPwdLocal(), user.getActif(), user.getDateValidite().getTime()>System.currentTimeMillis(), true, true, AuthorityUtils.createAuthorityList("ROLE_"+user.getProfil().getNomProfil().toUpperCase()));
    userDetails.setUser(user);
    return userDetails;     
}

 }

When I started the server in the debug mode, it seems that the entitymanager was set up when the myDAOFactory is injected in the MyUserDetailsService but somehow the call to loadUserByName does not happen anymore, when I try to login to my app... (although I did not change this part of the configuration...).

Any help vould be greatly appreciated!

Coralie
  • 107
  • 3
  • 17

2 Answers2

1

A few pointers:

  1. Don't import security and persistence configs in the MVC config, they are already defined in the root app context (via imports) whose beans are made visible to the MVC context

  2. DriverManagerDataSource is only usable in tests, replace it with a real pooling DataSource (e.g. a container-managed DataSource looked up via JNDI) in production at the latest

  3. Looking at your DataSource configuration, you are probably not using JTA. If so, remove the <tx:jta-transaction-manager /> bean definition

  4. Debug to try and pinpoint the problem. If it is persistence-related, start with a minimal root appliction context with just the persistence (JPA) stuff and a simple bean to test it:

    <bean class="pkg.Test" init-method="persistence" />

  5. If you can't get it to work, post more info here

Jukka
  • 4,583
  • 18
  • 14
  • Thanks for your suggestions. I finally got it work so I posted the right configurations below. Thanks again – Coralie Jun 10 '13 at 16:41
0

After trying all possible configurations for a few days, I finally found a correct solution for my transaction management using spring, eclipseLink and Glassfish :

  • The main problem was due to a wrong use of the spring tag context:component scan which I included in applicationContext.xml as well as in mvc-config.xml. Apparently, it creates 2 different contexts which do not share beans or transactions (see: Why DispatcherServlet creates another application context?)
  • I also tested lots of different configurations between JTA, jndi:lookup and LocalEntityManagerFactoryBean but using Glassfish and EclipseLink seems to make it more restrictive (JTA is the only viable option)

Here are my corrections, if it can help somebody someday:

applicationContext.xml

<beans ...>

  <import resource="persistence-config.xml"/>
  <import resource="security-config.xml"/>

  <context:component-scan base-package="fr.dao, fr.services" /><!-- do not scan the controllers here  -->

  <context:annotation-config/>


  <!--  Define the location of the property file to change properties dependent on the application environment -->
  <context:property-placeholder location="/WEB-INF/configuration.properties" />

</beans>

mvc-config.xml

<beans >


<!-- Spring MVC Support for annotations (JSR-303) -->
<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="fr.utils.CurrentUserWebArgumentResolver"></bean>
    </mvc:argument-resolvers>
</mvc:annotation-driven>

<context:component-scan base-package="fr.controller" /><!-- only scan controllers here -->

... 

<import resource="aspects-config.xml"/>

</beans>

And the persistence-config.xml:

<beans ...>

 <tx:annotation-driven/>

 <!-- We want to locate the container transaction manager -->
 <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
            <property name="showSql" value="true" />
        </bean>
    </property>
    <property name="jpaDialect">
        <bean
            class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
    </property>
    <property name="persistenceUnitName" value="databaseName"/>
    <property name="persistenceUnitManager">
        <bean 
            class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"/>
    </property>
  </bean>   

  <tx:jta-transaction-manager/>

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
</beans>
Community
  • 1
  • 1
Coralie
  • 107
  • 3
  • 17