Buscar este blog

lunes, 15 de agosto de 2016

JBoss - Virtual Servers - Mod_cluster

In JBoss, a virtual server is like a virtual host in apache (httpd). There is always a default virtual server that handles all requests received by the server. So, for each request this is what can happend:
  1. If the request points to the root context ("/"), you get the default welcome application. In this case the ROOT.war
  2. If the request points to a valid context (i.e, the context of a deployed web app), you get the response of this web app.
  3. If the request points to a invalid context, you get a 404

Besides the default virtual server, you can also define your own, by specifying a group of alias (domains) which will be handled in this virtual server.

For example, I have two web apps, /app1 and /app2 deployed, each one in its own context, among other webapps (/app3, /app4, etc.). The absolute URL would by:
  • app1: https://domain1/app1
  • app2: https://domain2/app2
  • app3: https://domain3/app3
  • app4: https://domain4/apps
There is a apache front-end configured with mod cluster. Each domain is a apache virtual host with forward the requests to the JBoss backends (a server cluster).

Now, I want to reconfigure this situation in order to get the following URLs:
  • app1: https://myCommonDomain/
  • app2: https://myCommonDomain/app2
  • app3: https://domain3/app3
  • app4: https://domain4/apps
So, app1 and app2 will be in the same domain, and app1 will be the root context of this domain. In this way, a user will think that both applications are just the same. In other way, I´m merging two web apps in one.

JBoss configuration

The first step is to create a new virtual server in JBoss, in the web subsystem:
<subsystem xmlns="urn:jboss:domain:web:1.5"
 default-virtual-server="default-host" native="false">
 <configuration>
  <jsp-configuration development="true"
   check-interval="2000" />
 </configuration>
 <connector name="http" protocol="HTTP/1.1" scheme="http"
  socket-binding="http" />
 <connector name="ajp" protocol="AJP/1.3" scheme="http"
  socket-binding="ajp" />
 <virtual-server name="default-host"
  enable-welcome-root="true">
  <alias name="localhost" />
  <alias name="example.com" />
 </virtual-server>
 <virtual-server name="myNewVirtualServer" default-web-module="app1.war">
  <alias name="myCommonDomain"/>
 </virtual-server>
</subsystem>

I created a virtual server named myNewVirtualServer, which default root will be app1.war (this is de runtime name of the deployment). This virtual server will be "listening" in myCommonDomain.

Web apps configuration

Inside app1 and app2, you need to include a jboss-web.xml file. In this file, each application declares its root context and references the virtual server created before.

jboss-web.xml in app1.war:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
 <context-root>/</context-root>
 <virtual-host>myNewVirtualServer</virtual-host>
</jboss-web>

jboss-web.xml in app2.war:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
 <context-root>/app2</context-root>
 <virtual-host>myNewVirtualServer</virtual-host>
</jboss-web>

Web apps configuration

Finally, you need to declare a new apache virtual host for this domain.
<VirtualHost *:443> 
 ServerName myCommonDomain
 (...) 
</VirtualHost>


domingo, 7 de agosto de 2016

JBoss - IllegalArgumentException: warning no match for this type name - Error creating bean with name 'dataSource'

I had a JEE 6 application using JPA in persistence tier, Spring in business tier, and that was being deployed in JBoss. Everything was working fine.

Then, I configured a business cache as explained in one of my previous posts, http://trabajosdesisifo.blogspot.com.es/2016/07/jboss-spring-infinispan-cache.html (well, at that time it was not a distributed cache, but that doesn't matter). Everything was working fine, too.

My application had a web service client, so I configured a web service cache as explained in other of my previous posts, http://trabajosdesisifo.blogspot.com.es/2016/07/web-service-client-spring-cache.html.  And this didn't work.

When I tried to deploy the application, I got this error:
10:32:35,429 ERROR [org.springframework.web.context.ContextLoader] (ServerService Thread Pool -- 232) Context initialization failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in ServletContext resource [/WEB-INF/config/data-access-config.xml]: Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Post-processing of the FactoryBean's object failed; nested exception is java.lang.IllegalArgumentException: warning no match for this type name: org.tempuri.EntradaSoap [Xlint:invalidAbsoluteTypeName]
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1387) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1128) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1117) [spring-context-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:922) [spring-context-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479) [spring-context-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389) [spring-web-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294) [spring-web-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112) [spring-web-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.apache.catalina.core.StandardContext.contextListenerStart(StandardContext.java:3339) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
 at org.apache.catalina.core.StandardContext.start(StandardContext.java:3777) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
 at org.jboss.as.web.deployment.WebDeploymentService.doStart(WebDeploymentService.java:156) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
 at org.jboss.as.web.deployment.WebDeploymentService.access$000(WebDeploymentService.java:60) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
 at org.jboss.as.web.deployment.WebDeploymentService$1.run(WebDeploymentService.java:93) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
 at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [rt.jar:1.7.0_21]
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334) [rt.jar:1.7.0_21]
 at java.util.concurrent.FutureTask.run(FutureTask.java:166) [rt.jar:1.7.0_21]
 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [rt.jar:1.7.0_21]
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_21]
 at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_21]
 at org.jboss.threads.JBossThread.run(JBossThread.java:122)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Post-processing of the FactoryBean's object failed; nested exception is java.lang.IllegalArgumentException: warning no match for this type name: org.tempuri.EntradaSoap [Xlint:invalidAbsoluteTypeName]
 at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:165) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:102) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1454) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:306) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:323) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 ... 27 more
Caused by: java.lang.IllegalArgumentException: warning no match for this type name: org.tempuri.EntradaSoap [Xlint:invalidAbsoluteTypeName]
 at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:301) [aspectjweaver-1.6.11.jar:1.6.11]
 at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:208) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.aspectj.AspectJExpressionPointcut.getFallbackPointcutExpression(AspectJExpressionPointcut.java:359) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.aspectj.AspectJExpressionPointcut.matches(AspectJExpressionPointcut.java:256) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:208) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:262) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:294) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:118) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:88) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:69) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:359) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322) [spring-aop-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:409) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.postProcessObjectFromFactoryBean(AbstractAutowireCapableBeanFactory.java:1625) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:162) [spring-beans-3.2.3.RELEASE.jar:3.2.3.RELEASE]
 ... 32 more

The first error that I tried to solve was this:
java.lang.IllegalArgumentException: warning no match for this type name: org.tempuri.EntradaSoap [Xlint:invalidAbsoluteTypeName]
Note: org.tempuri.EntradaSoap was the web service interface which had the method that I was trying to add to the cache.
This problem was related with Spring AOP configuration, like my pointcut was wrong. But after a while, I didn't discover anything.


Then I focused on the second error:
Error creating bean with name 'dataSource': Post-processing of the FactoryBean's object failed;
My datasource was defined as follows:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
 <property name="jndiName">
  <value>java:jboss/datasources/myAppDS</value>
 </property>
</bean>
So, it was created by JBoss and the application retrieves it via JNDI.

As I found in this stackoverflow answer, http://stackoverflow.com/questions/12804049/warning-no-match-for-this-type-name-when-deploying-to-jboss-dispite-deploying, When spring aop tried to inspect this class, it was not able to load it.

The solution is to add the a dependency with the module org.jboss.ironjacamar.jdbcadapters in the META-INF/jboss-deployment-structure.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>
  <dependencies>
   <module name="org.infinispan"/>
   <module name="org.jboss.ironjacamar.jdbcadapters"/>   
  </dependencies>
 
 </deployment>
</jboss-deployment-structure>

jueves, 28 de julio de 2016

Web service client - Spring cache

This is the third post about cache. The first one was about how to configure persistence cache with Hibernate, and the second one about how to configure a business cache with Spring, both of then by using Infinispan embbede in JBoss.

The objective of this post is to show how to configure the bussines cache with web services clients generated with maven cxf-codegen-plugin. The only special feature here is that you can not use the cache annotations (@Cacheable, @CacheEvict, etc.) because you have not access to the web service client source code. This code is generated by Maven in the compile phase.

In this example I will use the famous currency converter service: http://www.webservicex.com/CurrencyConvertor.asmx?wsdl

The first step is to configure pom.xml in order to parse the WSDL and to generate all needed classes.
<plugin>
 <groupId>org.apache.cxf</groupId>
 <artifactId>cxf-codegen-plugin</artifactId>
 <version>2.7.18</version>
 <executions>
  <execution>
   <id>generate-sources-wsdl</id>
   <phase>generate-sources</phase>
   <configuration>
    <wsdlOptions>
     <wsdlOption>
      <wsdl>${basedir}/src/main/resources/META-INF/wsdl/CurrencyConvertor.wsdl</wsdl>
      <extraargs>
       <extraarg>-xjc-XhashCode</extraarg>
       <extraarg>-xjc-Xequals</extraarg>
       <extraarg>-xjc-XtoString</extraarg>
      </extraargs>
     </wsdlOption>
    </wsdlOptions>
   </configuration>
   <goals>
    <goal>wsdl2java</goal>
   </goals>
  </execution>
 </executions>
 <dependencies>
  <dependency>
   <groupId>org.jvnet.jaxb2_commons</groupId>
   <artifactId>jaxb2-basics</artifactId>
   <version>1.11.1</version>
  </dependency>
 </dependencies>
</plugin>

I have downloaded the wsdl file and have put it inside the project in /resources/META-INF/wsdl directory.

Next, you have to configure de jaxws client, so you could use this client as a managed spring bean. In a context config file (for example, cxf-config.xml) I have this:
<?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:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">


 <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor">
    <property name="prettyLogging" value="true"/>
 </bean>

 <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor">
    <property name="prettyLogging" value="true"/>
 </bean>


 <jaxws:client id="currencyConversorClient"
  name="{http://www.webserviceX.NET}CurrencyConvertorSoap12" 
  serviceClass="net.webservicex.CurrencyConvertorSoap"
  address="http://www.webservicex.com/CurrencyConvertor.asmx">

  <jaxws:outInterceptors>
   <ref bean="loggingOutInterceptor" />
  </jaxws:outInterceptors>

  <jaxws:inInterceptors>
   <ref bean="loggingInInterceptor" />
  </jaxws:inInterceptors>
 </jaxws:client>

</beans>

With this configuration you can use the currencyConversorClient injected in other Spring managed beans, for example:
@Autowired    
private net.webservicex.CurrencyConvertorSoap currencyService;

And finally, you have to configure the cache in a declarative way, by using another context config file, as is explained in the reference documentation.
<!-- cache manager and  annotation-driven configuration omited-->

<!-- cache definitions -->
<cache:advice id="currencyConversorWebServiceCacheAdvice" cache-manager="cacheManager">
 <cache:caching cache="currency">
  <cache:cacheable method="conversionRate"/>
 </cache:caching>
</cache:advice>

<aop:config>
 <aop:advisor advice-ref="currencyConversorWebServiceCacheAdvice" pointcut="execution(* net.webservicex.CurrencyConvertorSoap.*(..))"/>
</aop:config>

In this snippet you declare a cache named "currency" over the conversionRate method in the CurrencyConvertorSoapCurrencyConvertorSoap service. Note that this is actually an interface which was generated by Maven.


Updated 1
Ultimately, a cache is just a hashmap like structure which store keys (the methods parameters) and values (the method return). In order to work properly, the cache keys must override the equals and hashcode methods, because two instances of a class with the same properties may not be equals.

So, when creating the web service class, you need to configure the cxf-codegen-plugin to add equals and hashcode methods to the DTOs. You get this by using the extra args tag, and by importing jaxb2-basics dependencies.

The currency example is very basic and the method params are simple types, which don't require this approximation, but for complex scenarios this would be mandatory.

Updated 2
Under some circumstances (I don't know exactly when) you need to add a dependency with org.jboss.ironjacamar.jdbcadapters module in the jboss-deployment-structure.xml file. Otherwise you will get a very confusing error.

Check this post: http://trabajosdesisifo.blogspot.com.es/2016/08/jboss-illegalargumentexception-warning.html

miércoles, 27 de julio de 2016

Jboss - Spring - Infinispan cache

In a previous post I explained how to configure Hibernate Second Level Cache with Infinispan in a cluster enviroment, check here. Now I´ll show you how to configure a business cache with Spring, also with Infinispan in a JBoss Cluster enviroment.

When I talk about bussines cache I mean the service tier in a multi-tier JEE application. Usually one service (or use case) involves multiple accesses to database, even to multiple databases and remote services, so this kind of cache is complementary to Hibernate cache.

Spring Framework provides a cache abstraction, an infraestructure arround some spring-managed beans which delegates the cache management over a plugable cache container. In this case, this container will be Infinispan.


JBoss configuration

You need to use, at least, the HA profile (High Availability).

Similar to Hibernate, the cache container will be provided by JBoss. In this example I created a myCustomCache container, configured over UDP in order to share information over the cluster nodes.
/profile=ha/subsystem=infinispan/cache-container=myCustomCache:add(start=EAGER)
/profile=ha/subsystem=infinispan/cache-container=myCustomCache/transport=TRANSPORT:add(lock-timeout="60000", stack="udp", cluster="myCustomCache-cluster")


Application configuration

Spring config

Spring will use the cache container created previously to store and recover the cached objects. This container will be obtained via JNDI and injected in a bean of SpringEmbeddedCacheManager.
But this procedure has a problem, you can not define your own cache regions. When spring asks for a new cache region, if it yet not exists , the cache manager creates it with default values. In a cluster enviroment, as I tested, this is a local cache, so it will not be shared amongst the nodes.

In order to create custom regions, and declare them as distributables, you need to create your own extension of the SpringEmbeddedCacheManager. This class will read an internal infinispan configuration file, parse it and "add" the declared caches to the cache manager.

This is the CustomSpringEmbeddedCacheManager class:
public class CustomSpringEmbeddedCacheManager extends SpringEmbeddedCacheManager implements InitializingBean {
    private static Logger LOGGER = LoggerFactory.getLogger(CustomSpringEmbeddedCacheManager.class);

    private Resource infinispanXMLFile;

 public void setInfinispanXMLFile(final Resource infinispanXMLFile) {
        this.infinispanXMLFile = infinispanXMLFile;
    }
 
 
    public CustomSpringEmbeddedCacheManager(final EmbeddedCacheManager nativeCacheManager) {
        super(nativeCacheManager);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
     final EmbeddedCacheManager cacheManager = getNativeCacheManager();
   
        final ParserRegistry parserRegistry = new ParserRegistry(Thread.currentThread().getContextClassLoader());
        final ConfigurationBuilderHolder configurationBuilderHolder = parserRegistry.parse(infinispanXMLFile.getInputStream());
        final Map<String, ConfigurationBuilder> namedConfigs = configurationBuilderHolder.getNamedConfigurationBuilders();
      
        for (final Map.Entry<String, ConfigurationBuilder> entry : namedConfigs.entrySet()) {
            if (cacheManager.cacheExists(entry.getKey())) {
                LOGGER.trace("Eliminando cache {}", entry.getKey());
                cacheManager.removeCache(entry.getKey());
            }

            LOGGER.trace("Definiendo nueva cache {}", entry.getKey());
            cacheManager.defineConfiguration(entry.getKey(), entry.getValue().build());
        }
    } 
}

Now, in your application context, you need to obtain de Infinispan cache manager and configure the spring cache manager.
<jee:jndi-lookup id="nativeCacheManager" expected-type="org.infinispan.manager.CacheContainer" jndi-name="java:jboss/infinispan/container/myCustomCache"/>
          
<bean id="cacheManager"  class="es.cixtec.arq.jboss.CustomSpringEmbeddedCacheManager">
  <constructor-arg>
   <ref bean="nativeCacheManager"/>
  </constructor-arg>
  <property name="infinispanXMLFile" value="classpath:infinispan-business.xml"/>
 </bean>

<cache:annotation-driven />

Note that you need to use a internal infinispan configuration file, in this case infinispan-business.xml, located in the classpath (maven resources directory):
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
 xmlns="urn:infinispan:config:5.2">

 <namedCache name="someBusiness">
  <eviction strategy="LRU" maxEntries="10000" />
  <expiration lifespan="60000" maxIdle="60000" />
  <clustering mode="replication">
   <sync replTimeout="10000" />
  </clustering>
 </namedCache>

</infinispan>

Dependency config

You need to import the following dependencys:
<dependencies>

 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.2.17.RELEASE</version>
 </dependency>
  
 <dependency>
  <groupId>org.infinispan</groupId>
  <artifactId>infinispan-core</artifactId>
  <version>5.2.15.Final</version>
  <scope>provided</scope>
 </dependency>
 
</dependencies>

Documentation

Available doc:

domingo, 24 de julio de 2016

Jboss - Hibernate - Infinispan cache

This is the basic configuration in order to use Hibernate Second Level Cache (2LC) inside a JBoss Server in a cluster enviroment. The cache provider will be Infinispan, wich is shipped with the server.
Second Level Cache (2LC)
A Second Level Cache (2LC) is a data store which holds persistent information relating to application state. Examples of 2LC consumers are session state replication, Single Sign On (SSO), and Java Persistence API (JPA). JBoss Enterprise Application Platform 6 uses Infinispan to manage its 2LC.
There is also a First Level Cache which is always enabled, but it works at the session level. The Hiberante Session, usually, is created when a transaction is opened and it is bounded to it. If during the life span of the transaction you get the same object two times from database, only in the first one Hibernate hits the database, the second time, the object is recovered from session.
The limitation with First Level Cache is that it only apply during the transaction, and only for the client who started it.

JBoss configuration

You need to use, at least, the HA profile (High Availability) .

Inside infinispan subsystem there should be an hibernate cache container, but it must be configured with the start attribute to EAGER.
/profile=ha/subsystem=infinispan/cache-container=hibernate:write-attribute(name=start, value=EAGER)

The result would be something like this:
<cache-container name="hibernate" start="EAGER" default-cache="local-query" module="org.jboss.as.jpa.hibernate:4">
    <transport lock-timeout="60000"/>
    <local-cache name="local-query">
        <transaction mode="NONE"/>
        <eviction strategy="LRU" max-entries="10000"/>
        <expiration max-idle="100000"/>
    </local-cache>
    <invalidation-cache name="entity" mode="SYNC">
        <transaction mode="NON_XA"/>
        <eviction strategy="LRU" max-entries="10000"/>
        <expiration max-idle="100000"/>
    </invalidation-cache>
    <replicated-cache name="timestamps" mode="ASYNC">
        <transaction mode="NONE"/>
        <eviction strategy="NONE"/>
    </replicated-cache>
</cache-container>

As you can see, there are three declared caches: local-query, entity and timestamps, each one with its own type attending to the clusterization mode. Infinispan allows the following clustering modes:
  • Local mode. Better performance, but no cluster aware.
  • Replicated mode. All the nodes in the cluster have their own local cache copy. When one node modify its local cache, it sends a message (sychronous or asynchronous) to the other nodes so they can update their local copy too.
  • Invalidation mode. No data is shared. When a node modifies some data in its own local cache copy, it sends a message to the other nodes so they can clear this object from their local copy.
  • Replication mode. Each cached object exists only in one (or in a small number) of the nodes in the cluster. Instead of replicate an object in all the nodes, they are informed about who owns it.

When the number of nodes increase (bigger than 10) the best option is the replication mode.

Application configuration

Hibernate config

Configure hibernate session factory. With the hibernateProperties you can enable 2LC and force hibernate to use the JBoss cache container obtained via JNDI.
<bean class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" id="sessionFactory" lazy-init="false">
 (...)
 <property name="hibernateProperties">
 <props>
   (...)
   
     <!-- Cache -->
     <prop key="hibernate.cache.use_second_level_cache">true</prop>
     <prop key="hibernate.cache.use_query_cache">true</prop>            
     <prop key="hibernate.cache.infinispan.statistics">true</prop>   
     <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.JndiInfinispanRegionFactory</prop>
     <prop key="hibernate.cache.infinispan.cachemanager">java:jboss/infinispan/container/hibernate</prop> 
                               
 </props>
 </property>  
</bean>

Hibernate will use two of them, plus two other news cache wich will be configured the application startup. As you can see in org.hibernate.cache.infinispan.InfinispanRegionFactory, these caches are:
  • entity
  • collection
  • timestamps
  • query
The default values of thesea caches are well documented in this post: Using Infinispan as JPA/Hibernate Second Level Cache Provider

Hibernate will not share entities, collections or querys among the nodes of the cluster in order to reduce the traffic. For example, each node will have an entity instaced cached in its local store, and when this instance is changed by one of them, the others will be informed to evicts it.

Dependency config

You need to import the following dependencys:
<dependencyManagement>
 <dependencies>
  <dependency>
   <groupId>org.jboss.bom.eap</groupId>
   <artifactId>jboss-javaee-6.0-with-hibernate</artifactId>
   <version>6.2.0.GA</version>
   <type>pom</type>
   <scope>import</scope>
  </dependency>
 </dependencies>
</dependencyManagement>  


<dependencies>
 (...)
 
 <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <scope>provided</scope>
 </dependency>

 <dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-infinispan</artifactId>
  <scope>provided</scope>
 </dependency>
</dependencies>

Note that I'm declaring hibernate dependencies as provided, because I will use the jars shipped inside JBoss, so the WAR does not need to have them in its lib directory.


You have to declare thease dependencies in the META-INF/jboss-deployment-structure.xml file, so JBoss will add them to the classpath during the application boot:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
 <deployment>
  <dependencies>
   <module name="org.hibernate" />
   <module name="org.javassist" />
   <module name="org.infinispan" services="import"/>   
   <module name="org.jboss.ironjacamar.jdbcadapters"/>
  </dependencies>

 </deployment>
</jboss-deployment-structure>

Documentation

Available doc:
This doc belongs to wildfly, but it can also be applied to EAP 6: