Sharing spring context between web apps

Load all jars part of server classpath loading which will be used to create the spring application context and can be shared/available between Web apps/EARs.

Web.xml (in all WEB apps)

<context-param>
  <param-name>parentContextKey</param-name>
  <param-value>beanRefFactory</param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

 It looks for beanRefContext.xml part of classpath and a spring bean named “beanRefFactory”

beanRefContext.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"
 xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 <bean id="beanRefFactory"
  class="org.springframework.context.support.ClassPathXmlApplicationContext">
  <constructor-arg >
    <list>
      <value>classpath*:spring-context.xml</value>
   </list> 
  </constructor-arg>
 </bean>
</beans>

spring-context.xml contains spring beans definition needed by the application and been loaded to create spring application context. This will be the parent application context.

Ensure that applicationContext.xml is placed in WEB-INF folder, used to create its own spring context which extends the parent application context created already. Define the spring beans specific to web modules here. if not, have dummy declaration given below.

applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
</beans>

Merging two persistence units

We can merge two persistence unit if both have same persistence-unit name. This can be done using persistence unit post processor.

Note: “default” is the PU name for both persistence1.xml and persistence2.xml given below.

persistence1.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="default">
    <class>examples.A</class>
    .
    .
    .
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

persistence2.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="default">
    <class>samples.A</class>
    .
    .
    .
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>

2. Persistence post processor is used to merge all entities present in persistence1.xml & persistence2.xml and be

part of persistence unit manager.

package examples;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MergingPersistenceUnitPostProcessor
implements PersistenceUnitPostProcessor {
Map<String, List<String>> puiClasses = new HashMap<String, List<String>>();

public void postProcessPersistenceUnitInfo( MutablePersistenceUnitInfo pui ) {
List<String> classes = puiClasses.get( pui.getPersistenceUnitName() );
if ( classes == null ) {
classes = new ArrayList<String>();
puiClasses.put( pui.getPersistenceUnitName(), classes );
}
pui.getManagedClassNames().addAll( classes );
final List<String> names = pui.getManagedClassNames();
classes.addAll( pui.getManagedClassNames() );
}
}

3. Configure the merging persistence unit post processor in persistence unit manager.

    <bean    class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    <bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="websphereDataSource" />
        <property name="persistenceUnitManager" ref="pum" />
        <property name="jpaVendorAdapter">
            <bean
                class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="ORACLE" />
                <property name="showSql" value="true" />
            </bean>
        </property>
    </bean>

    <bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="persistenceXmlLocations">
            <list>
             <value>classpath*:META-INF/persistence1.xml</value>
             <value>classpath*:META-INF/persistence2.xml</value>
            </list>
        </property>
        <property name="defaultDataSource" ref="websphereDataSource"/>
        <property name="persistenceUnitPostProcessors">
            <bean class="examples.MergingPersistenceUnitPostProcessor"/>
        </property>
    </bean>

Migration from Spring module to ehcache-spring-annotation

Spring module project is dead. It is no longer supported in Spring 3.0+.

Currently ehcache spring annotations(ESA) provides support to ehcache integrated with Spring in annotated way.

You can move from spring modules to ESA easily.

Refer http://code.google.com/p/ehcache-spring-annotations/wiki/SwitchingFromSpringModules.

Test thoroughly before you make decision.

Spring application context aware

I want my spring application context to be retrieved any time anywhere in my application. Luckily, ApplicationContextAware and BeanFactoryPostProcessor helped me.

Spring-beans.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:context="http://www.springframework.org/schema/context"

xmlns:jee="http://www.springframework.org/schema/jee"

xmlns:lang="http://www.springframework.org/schema/lang"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:p="http://www.springframework.org/schema/p"

xmlns:util="http://www.springframework.org/schema/util"

xmlns:aop="http://www.springframework.org/schema/aop"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd

http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

     <context:component-scan base-package="spring.examples"/>

</beans>  

Spring context Wrapper:

package spring.examples;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextWrapper implements ApplicationContextAware,
        BeanFactoryPostProcessor {

    private ApplicationContext appContext;
    private ConfigurableListableBeanFactory factory;

    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory)
            throws BeansException {
        this.factory = factory;
    }

    public void setApplicationContext(ApplicationContext c)
            throws BeansException {
        this.appContext = c;
    }
    public ApplicationContext getAppContext() {
        return appContext;
    }
    public void setAppContext(ApplicationContext appContext) {
        this.appContext = appContext;
    }
    public ConfigurableListableBeanFactory getFactory() {
        return factory;
    }
    public void setFactory(ConfigurableListableBeanFactory factory) {
        this.factory = factory;
    }
}

Test:

package spring.examples;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringContextWrapperTest {
    @Autowired
    SpringContextWrapper springContextWrapper;

    void createSpringContext() {
        AutowireCapableBeanFactory factory = null;
        ApplicationContext appContext = new ClassPathXmlApplicationContext("spring-beans.xml");
        factory = appContext.getAutowireCapableBeanFactory();
        factory.autowireBeanProperties(this,
                AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
    }
    public static void main(String[] args) {
        SpringContextWrapperTest springContextWrapperTest = new SpringContextWrapperTest();

        // Once Spring context is created, It will call the following post back methods in SpringContextWrapper
        // 1. postProcessBeanFactory
        // 2. setApplicationContext
        springContextWrapperTest.createSpringContext();

        // Check if the application context is injected in SpringContextWrapper.
        if (springContextWrapperTest.springContextWrapper.getAppContext() != null) {
            System.out.println("Application context is retreived from spring context wrapper.");
        } else {
            System.out.println("No Application context found.");
        }
    }
}

Output:

Inside setApplicationContext method
Inside postProcessBeanFactory method
Application context is retreived from spring context wrapper.

Now, I can able to retrieve spring application context anywhere in my application.

How to create spring beans dynamically?

Spring beans can be created dynamically using the following steps.

  1. Get the appropriate bean factory from application context.
  2. Get the bean definition registry from bean factory.
  3. Create your bean definition.
  4. Register the bean in the registry. 

Example Code:

import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DynamicBeanTest {
    static ApplicationContext appContext = null;

    private void createDynamicBean() {
        AutowireCapableBeanFactory factory = null;
        appContext = new ClassPathXmlApplicationContext("spring-beans.xml");
        factory = appContext.getAutowireCapableBeanFactory();
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) factory;
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(SampleBean.class);
        beanDefinition.setAutowireCandidate(true);
        registry.registerBeanDefinition("sampleBean", beanDefinition);
        factory.autowireBeanProperties(this,
                AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
    }

    public static void main(String[] args) {
        DynamicBeanTest dynamicBeanTest = new DynamicBeanTest();
        dynamicBeanTest.createDynamicBean();
        SampleBean sampleBean = getBean(SampleBean.class);
        sampleBean.method1();
    }

    @SuppressWarnings("unchecked")
    private static <T> T getBean(Class <? extends T> type) {
        String beanName = type.getSimpleName();
        beanName = beanName.substring(0, 1).toLowerCase()
                + beanName.substring(1);
        return (T) appContext.getBean(beanName);
    }
}

class SampleBean {

    public void method1() {
        System.out.println("Method1");
    }
}

Note: Ensure that spring-beans.xml is present in classpath before executing the above code.

Now you have invented how to define spring beans dynamically.