Buscar este blog

Mostrando entradas con la etiqueta spring. Mostrar todas las entradas
Mostrando entradas con la etiqueta spring. Mostrar todas las entradas

domingo, 30 de octubre de 2016

JBoss - Spring URL UTF-8 encoding

If you need to pass special characters in the request parameters, the browser will encode them by using the specified encoding. This is the case, for example, when using the JQuery serialize method to compose a URL with the elements inside a form.

In my case, the problem arose when I was trying to load a jqGrid base table. The user fills some fields inside a form and when she presses the search button, a jqGrid is created and a request is sent to a Spring MVC Controller.
$("#myGrid").jqGrid({
      url: '/myapp/mypage/search?+ $('#myForm').serialize(),
      loadonce:true,
      mtype: 'GET',
      datatype: "json",
      (...)
});

When some field inside "myForm" has special characters, for example 'áéíóú', the URL generated will be as follows:
http://localhost:8080/myapp/mypage/search?descripcion=%C3%A1%C3%A9%C3%AD%C3%B3%C3%BA&_search=false&nd=1477817915534&rows=20&page=1&sidx=&sord=asc
These values are the result of converting the input text to UTF-8. Inside the controller you would see ANSI characters but the application is expecting UTF-8.

The solution is to configure URI encoding in JBoss EAP 6.2 connector using the following standalone.xml configuration:
<system-properties>
     <property name="org.apache.catalina.connector.URI_ENCODING" value="UTF-8"/>
     <property name="org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING" value="true"/>
</system-properties>

In domain mode you can configure these properties at server level or server group level.
There are more useful JBoss properties I collected in a previous post: http://trabajosdesisifo.blogspot.com.es/2015/12/jboss-useful-system-properties.html

Anyway, if you are working with UTF-8, don't forget the the Spring CharacterEncodingFilter.

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>

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:

viernes, 26 de febrero de 2016

HTML Login Form - Disable autocomplete and password remember

Most users use the autocomplete a password remember options of their browsers. In this way, if you try to login in a site where you previously were logged, the browser autocompletes the login, and then suggests the password.
But recently, in one critical web app, we have a requirement to disable this behaviour. What seemed a simple task, resulted in a small head ache.

We start with this basic login page.
<body>
 <div th:if="${param.error}">
  Invalid username and password.
 </div>
 
 <div th:if="${param.logout}">
  You have been logged out.
 </div>
 
 <form th:action="@{/login}" method="post">
  <div><label> User Name : <input type="text" name="username"/> </label></div>
  <div><label> Password: <input type="password" name="password"/> </label></div>
  <div><input type="submit" value="Sign In"/></div>
 </form>
</body>

In this page, the browser will ask you if you want to remember the password. Any way, the next time you visit this page, the browser will autocomplete the login field.

Step 1, disable autocomplete

If you only need to disable autocomplete (but you can afford the browser ask for password remember) there is a HTML attribute which should work... autocomplete="off"
<body>
 <div th:if="${param.error}">
  Invalid username and password.
 </div>
 
 <div th:if="${param.logout}">
  You have been logged out.
 </div>
 
 <form th:action="@{/login}" method="post" autocomplete="off">
  <div><label> User Name : <input type="text" name="username"/> </label></div>
  <div><label> Password: <input type="password" name="password"/> </label></div>
  <div><input type="submit" value="Sign In"/></div>
 </form>
</body>

Step 2, disable password remember

If you want to force the browser to not ask for password remember, this is a bit more complex. After a lot of  test, the solution I found was to create a hidden form with hidden fields. The user fills a fake form, and when she presses the submit button, these fields and the submit event are transferred to the true login form.
<body>
 <script type="text/javascript">         
  var copyFields = function() {
   document.getElementById('username').value = document.getElementById('usernameFake').value;
   document.getElementById('password').value = document.getElementById('passwordFake').value; 
  }
  
  var submitRealForm = function() {
   copyFields();
   document.getElementById('realLoginForm').submit();
   return false;
  }
 </script>
 
 <div th:if="${param.error}">
  Invalid username and password.
 </div>
 
 <div th:if="${param.logout}">
  You have been logged out.
 </div>
 
  
 <form id="realLoginForm" th:action="@{/login}" method="post" autocomplete="off" style="display:none">
  <input id="username" type="hidden" name="username"/>
  <input id="password" type="hidden" name="password"/>       
 </form>
  
 
 <div class="simulateForm">
  <div><label> User Name : <input id="usernameFake" type="text" /> </label></div>
  <div><label> Password: <input id="passwordFake" type="password"/> </label></div>
  <div> <button type="button" onclick="submitRealForm()">Click Me!</button></div>
 </div>
</body>


Step 3, caputre enter in password

When the user are inside an HTML form and she presses enter key, the browser send a click event to the first button in this form.
With the solution I made, when user press enter, nothing happens. To solve this problem, with javascript you can listen this event and trigger the submit manually.
<body>
 <script type="text/javascript">         
  var copyFields = function() {
   document.getElementById('username').value = document.getElementById('usernameFake').value;
   document.getElementById('password').value = document.getElementById('passwordFake').value; 
  }
  
  var submitRealForm = function() {
   copyFields();
   document.getElementById('realLoginForm').submit();
   return false;
  }
  
  var submitFormOnEnter = function(event) {
   if (event.keyCode == 13) {
    submitRealForm();
   }
  }
 </script>
 
 <div th:if="${param.error}">
  Invalid username and password.
 </div>
 
 <div th:if="${param.logout}">
  You have been logged out.
 </div>
 
  
 <form id="realLoginForm" th:action="@{/login}" method="post" autocomplete="off" style="display:none">
  <input id="username" type="hidden" name="username"/>
  <input id="password" type="hidden" name="password"/>       
 </form>
  
 
 <div class="simulateForm">
  <div><label> User Name : <input id="usernameFake" type="text" onkeydown="submitFormOnEnter(event)"/> </label></div>
  <div><label> Password: <input id="passwordFake" type="password" onkeydown="submitFormOnEnter(event)"/> </label></div>
  <div> <button type="button" onclick="submitRealForm()">Click Me!</button></div>
 </div>
</body>

Note.
This example is based on the Spring Security getting started (https://spring.io/guides/gs/securing-web/). Their source code is in https://github.com/spring-guides/gs-securing-web, I just played with de login.html page.

domingo, 7 de febrero de 2016

Spring Controller and Future Tasks

Suppose you have a web application in which the user can ask for reports. The application collects some data and then generate a PDF (or CSV, or whatever) and returns it to him.

Depending on what kind of report and what kind of filter data, the time the app requires to build the report may vary. So, for the same report, the user can get the file immediately, or may need to wait some seconds to get it.

Now, you dont want to force the user to wait for generated the report when it takes much time. If the application detects that the report is lasting much, instead the file, it returns a message telling him that his file will be sent by other means (for example, by sending an email later).

This behaviour can be achieved with Asynchronous tasks, and Futures.

We have a Controller which receives the user request and then calls a service. The service constructs the report, returns it to the controller, and finally the controller returns it to the user. This is the basic flow of much Spring MVC applications.

In this example I have a Service called InvoiceReportService which returns a InvoiceReport.
public class InvoiceReport {
 private byte[] content;

 public byte[] getContent() {
  return content;
 }

 public void setContent(byte[] content) {
  this.content = content;
 }
}

The service will be executed asynchronously so, instead returns a InvoiceReport object it will return a Future<InvoiceReport>:
public interface InvoiceReportService {

 Future<InvoiceReport> generateReport();

}

This is the service class. It just reads a PDF file from the classpath and returns it. I added a random delay to simulate some heavy tasks running. Note that the service method is annotated with @Async.
@Service
public class InvoiceReportServiceImpl implements InvoiceReportService {
 
 @Override
 @Async
 public Future<InvoiceReport> generateReport() {
  Resource resource = new ClassPathResource("spring-boot-reference.pdf");    
  byte[] bytes = null;
  try {
   bytes = IOUtils.toByteArray(resource.getInputStream());
  } catch (IOException e) {
   //Nothing     
  }
  
  simulateDelay();
  
  InvoiceReport report = new InvoiceReport();
  report.setContent(bytes);
  return new AsyncResult<InvoiceReport>(report);
 }

 
 private void simulateDelay() {
  int secondsSleep = new Random().nextInt(2);
  try {
   System.out.println("Going to sleep " + secondsSleep + " seconds");
   TimeUnit.SECONDS.sleep(secondsSleep);
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  System.out.println("Awake!!");
 }
}

Working with futures you can specify a maximum time you want to wait for the result. If the result is not ready in this time, a TimeOutException is raised, but the underlying task is not interrupted.
So, the controller could be something like this:
@Controller
public class InvoiceController {
 
 @Autowired
 private InvoiceReportService invoiceReportSerive;
 
 @RequestMapping(value="/report", method=RequestMethod.POST)
 public HttpEntity<byte[]> getReport(HttpServletResponse response) throws IOException {
  Future<InvoiceReport> reportFutre = invoiceReportSerive.generateReport();
  
  InvoiceReport invoiceReport = null;
  try {
   invoiceReport = reportFutre.get(1, TimeUnit.SECONDS);
   System.out.println("In time");  
   return responseNow(invoiceReport);
  }
  catch(TimeoutException e) {
   System.out.println("Too late");  
   response.sendRedirect("/laterPlease");
   return null;
  }
  catch(Exception e) {
   //Handle exception
   return null;
  }
 }

 
 private HttpEntity<byte[]> responseNow(InvoiceReport invoiceReport) { 
     HttpHeaders header = new HttpHeaders();
     header.setContentType(new MediaType("application", "pdf"));
     header.set("Content-Disposition", "attachment; filename= \"report.pdf\"");
     header.setContentLength(invoiceReport.getContent().length);

     return new HttpEntity<byte[]>(invoiceReport.getContent(), header); 
 }
}

The controller waits, as much, as one second for the report. If the service does not respond in that time, then shows the user a info page. Otherwise, the controller returns the file.

But this has one problem. How does the user get the report when it is not completed in the specified timeout? We need other task which wait for report as long as necessary, because the service will be running even when the controller returns the info page.

To solve this I created a second service which is the one who will wait until the first one ends.
@Service
public class WaitUntilFinishService<T> {
 
 @Async
 public void waitUntilFinish (Future<T> future, AfterWaitComand<T> afterWaitCommand){
  try {
   T result = future.get();
   afterWaitCommand.docommand(result);
  } catch (InterruptedException | ExecutionException e) {
   System.out.println("The task was interrupted");
   e.printStackTrace();
  }
 }
}
This service receives the Future result and just waits for it. It also receives a call back service, so when the task is completed, it calls this call back service in order to complete the primary action.

This callback service implements a common interface, so you can wire any custom implementation you need. In this case, for example, the callback could retrieve the file and send it to the user by mail.
public interface AfterWaitComand<T> {
 void docommand(T result);
}

@Service
public class SendMailAfterWaitCommand implements AfterWaitComand<InvoiceReport> {

 @Override
 public void docommand(InvoiceReport result) {
  System.out.println("Sending invoice result by mail");
  System.out.println(result);  
 }

}

Whit thease improvements, the controller will be as follows:
@Controller
public class InvoiceController {
 
 @Autowired
 private InvoiceReportService invoiceReportSerive;
 
 @Autowired
 private WaitUntilFinishService<InvoiceReport> waitUntilFinishService;
 
 @Autowired
 private AfterWaitComand<InvoiceReport> sendMailAfterWaitCommand;
 
 
 
 @RequestMapping(value="/report", method=RequestMethod.POST)
 public HttpEntity<byte[]> getReport(HttpServletResponse response) throws IOException {
  Future<InvoiceReport> reportFutre = invoiceReportSerive.generateReport();
  
  InvoiceReport invoiceReport = null;
  try {
   invoiceReport = reportFutre.get(1, TimeUnit.SECONDS);
   System.out.println("In time");  
   return responseNow(invoiceReport);
  }
  catch(TimeoutException e) {
   System.out.println("Too late");
   waitUntilFinishService.waitUntilFinish(reportFutre, sendMailAfterWaitCommand);   
   response.sendRedirect("/laterPlease");
   return null;
  }
  catch(Exception e) {
   //Handle exception
   return null;
  }
 }

 
 private HttpEntity<byte[]> responseNow(InvoiceReport invoiceReport) { 
     HttpHeaders header = new HttpHeaders();
     header.setContentType(new MediaType("application", "pdf"));
     header.set("Content-Disposition", "attachment; filename= \"report.pdf\"");
     header.setContentLength(invoiceReport.getContent().length);

     return new HttpEntity<byte[]>(invoiceReport.getContent(), header); 
 }
}


You can download the source from my github: https://github.com/evazquezma/JEE6/tree/master/async

domingo, 8 de noviembre de 2015

Externalize log4j configuration file in webapp with spring config property

In a previous post (Externalize log4j configuration file in webapp) I explained how to externalize log4j.xml file by using org.springframework.util.Log4jConfigurer inside web.xml. This solution has a drawback, and it's that you need to use a system property wich points to the external file.

In most cases it is more suitable to have a property inside a config file, just like the rest o properties used in the app. This is accomplished with org.springframework.beans.factory.config.MethodInvokingFactoryBean. This class is able to invoke a method of another class, i.e, Log4jConfigurer and you can do it after your config file would be loaded.

The complete solution would be as follow:
<context:property-placeholder properties-ref="properties" order="1" />
<bean id="properties"  class="org.springframework.beans.factory.config.PropertiesFactoryBean">
 <property name="locations">
  <list>
   <value>classpath:conf.properties</value>
  </list>
 </property>
</bean>



<bean id="log4jInitializer" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="properties">
 <property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
 <property name="targetMethod" value="initLogging" />
 <property name="arguments">
  <list>
   <value>${customProperty.log4j.location:classpath:log4j.xml}</value>   
  </list>
 </property>
</bean>

sábado, 1 de agosto de 2015

Externalize log4j configuration file in webapp

Note: This solution was updated using spring in http://trabajosdesisifo.blogspot.com.es/2015/11/externalize-log4j-configuration-file-in.html


All web application needs a Log. With the information written in a log file you can audit your application, check its health and trace problems.

Log4j is just one framework you can use to generate logs. You configure log behaviour by using a configuration file: log4j.xml or log4j.properties.
Usually this files are stored inside your app, i.e packed inside your war. This implies that you will have the same logging options in all of the environments: development, test, preproduction and production.

This could be a simple example of a log4j.xml file:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- ************************************************ -->
    <!-- ****************** APPENDERS ******************* -->
    <!-- ************************************************ -->
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="[%d] (%F\:%L) %p %t %c %m%n" />
  </layout>
 </appender>

 <appender name="LOG" class="org.apache.log4j.DailyRollingFileAppender">
  <param name="File" value="/var/log/jboss/myApp.log" />
  <param name="DatePattern" value="'.'yyyy-MM-dd"/>
  <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p][%d{ISO8601}][%-4L] %F>%m%n" />
        </layout>
 </appender>
    
    <!-- ************************************************ -->
    <!-- ****************** LOGGERS ********************* -->
    <!-- ************************************************ -->
    <logger name="es.sisifo.package1">
        <level value="DEBUG" />        
    </logger>
    
 <logger name="es.sisifo.package2">
        <level value="DEBUG" />     
    </logger>
 
    <logger name="org.springframework">
        <level value="DEBUG" />  
    </logger>

 <logger name="es.sisifo">
  <level value="INFO" />
 </logger>

 <root>
  <priority value="WARN" />
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="LOG" />
 </root>
</log4j:configuration>

Here you have two packages of your app plus all spring classes in DEBUG level, the rest of your app in INFO and the rest of the classes in WARN.
A file called myApp.log is generated in /var/log/jboss directory.


You can use environment properties inside configuration files in order to externalize some parameters, for example, the path in which files are written. Also, you can use system properties passed to the JVM in boot time.
The previous log4j.xml config file would be this:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <!-- ************************************************ -->
    <!-- ****************** APPENDERS ******************* -->
    <!-- ************************************************ -->
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="[%d] (%F\:%L) %p %t %c %m%n" />
  </layout>
 </appender>

 <appender name="LOG" class="org.apache.log4j.DailyRollingFileAppender">
  <param name="File" value="${jboss.server.log.dir}/jboss/myApp.log" />
  <param name="DatePattern" value="'.'yyyy-MM-dd"/>
  <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="[%-5p][%d{ISO8601}][%-4L] %F>%m%n" />
        </layout>
 </appender>
    
    <!-- ************************************************ -->
    <!-- ****************** LOGGERS ********************* -->
    <!-- ************************************************ -->
    <logger name="es.sisifo.package1">
        <level value="DEBUG" />        
    </logger>
    
 <logger name="es.sisifo.package2">
        <level value="DEBUG" />     
    </logger>
 
    <logger name="org.springframework">
        <level value="DEBUG" />  
    </logger>

 <logger name="es.sisifo">
  <level value="INFO" />
 </logger>

 <root>
  <priority value="${system.myApp.defaultLogLevel}" />
  <appender-ref ref="CONSOLE" />
  <appender-ref ref="LOG" />
 </root>
</log4j:configuration>

Note that "jboss.server.log.dir" is a well-known JBoss property and "system.myApp.defaultLogLevel" would be a custom system property.

But imagine you have your app in production and some strange behaviour suddenly  appears in a apache-commons class. You would like to add a new logger tag and put this package in DEBUG in order to obtain more information. In this scenario, you would need to deploy a new version of your app with a modified log4j.xml.
The solution is to externalize the whole configuration file, and to force your app to read it in boot time. In this way you simple need to change the file and restart the app..


To externalize configuration file you have Log4jConfigListener. This class allow you to specify a external location of this file and load it when the app starts. In your web.xml you must set a context param called log4jConfigLocation that points to this file. This param could be expressed in multiples ways:
  • classpath:/... in order to indicate a custom path to the file inside your classpath.
  • file:... in order to indicate a external path to the file
  • ${} in order to indicate a system property whose value contains the path to the external file
For example:
<!-- Configuración de log -->
 <context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>${system.myApp.log4jConfigLocation.web:classpath:/internal_log4j.xml}</param-value>
 </context-param>

 <context-param>
  <param-name>log4jExposeWebAppRoot</param-name>
  <param-value>false</param-value>
 </context-param>

 <listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
 </listener>

Note that ${} accepts a default value expressed after the ":". In this example, the file would be allocated in the path expressed by "system.myApp.log4jConfigLocation.web" system property and, if this property does not exists, the default value would be "classpath:/internal_log4j.xml" (inside classpath).
In this example, de value of system.myApp.log4jConfigLocation.web would be "file:/home/jboss/myAppConfig/log4j-custom.xml" (note de "file:" prefix).

In JBoss you can either set system properties through web admin console (localhost:9990) in standalone or domain modes (in server-groups). In domain mode, the best way is to set the property in the server-group, keeping in mind that all servers share the same path.

When app starts you should see this log:
[Server:server-full-ha-1] 12:42:12,235 INFO  [org.springframework.beans.factory.config.PropertiesFactoryBean] (ServerService Thread Pool -- 107) Loading properties file from URL [file:/home/jboss/myAppConfig/log4j-custom.xml]

viernes, 26 de junio de 2015

Apache Camel DSL - Spring - Inject bean in message body

What I want to do is to set one bean directly in the message Body inside a Camel Route. This could be very useful, for example, if you need to have a default response message in your route, when an error occurs or under some business circumstances.

There are two ways of doing this: Using Spring Expression Language or usign Simple component.

Camel allows you integrate multiples languages in your routes, for example, Groovy Javascript, or Spring. More precisely, you can use SpEL (Spring Expression Language) so you can work with beans as in java class. In this way you can create a new instance of a bean (in this case it's not a bean, just a simple POJO) and put it in the body.

The other way is declaring a bean as usual and ref it from inside a simple expression.


This is my class:
import java.io.Serializable;

public class User implements Serializable{
    private static final long serialVersionUID = -8687467303034052162L;
    private String nombre;
    private String nif;

    public User() {
    }

    public User(final String nombre, final String nif) {
        this.nombre = nombre;
        this.nif = nif;
    }

    public String getNombre() {
        return nombre;
    }
    public void setNombre(final String nombre) {
        this.nombre = nombre;
    }
    public String getNif() {
        return nif;
    }
    public void setNif(final String nif) {
        this.nif = nif;
    }

    @Override
    public String toString() {
        return "User [nombre=" + nombre + ", nif=" + nif + "]";
    }
}

Then I create an instance of this class in Spring XML config file:
<bean id="alice" class="es.pruebas.modelo.User">
 <constructor-arg name="nombre" value="Alice"/>
 <constructor-arg name="nif" value="00000001R"/>
</bean>

And finally, this is my route in wich the body is set by using the two ways presented:
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
 <route id="myRoute">
  <from uri="timer://foo?fixedRate=true&amp;period=5000"/>     

  <setBody>
   <simple>ref:alice</simple> 
  </setBody>
  <log message="The body should be Alice: ${body}" loggingLevel="INFO"/>         

  <setBody>
   <spel>#{new es.pruebas.modelo.User('Bob', '12345678Z')}</spel>
  </setBody>
  <log message="The body should be Bob: ${body}" loggingLevel="INFO"/>
 </route>
</camelContex>

The outcome of this execution will be:
2015-06-26 16:39:04 INFO  myRoute:96 - New execution starts
2015-06-26 16:39:04 INFO  myRoute:96 - The body should be Alice: User [nombre=Alice, nif=00000001R]
2015-06-26 16:39:04 INFO  myRoute:96 - The body should be Bob: User [nombre=Bob, nif=12345678Z]

miércoles, 24 de junio de 2015

Integrate JAX-WS in Spring Context

Creating a JAX-WS client with javax.xml.ws.Service is very simple, but if you are working with spring, then you would like to integrate it in Spring Context. In this way, you can get your client by using @Autowired.

The solution is to create a Factory Class responsible for instanciate the client.

Other common requirement is to read the service endpoing from a config file, so it could vary from enviroment.

Factory code:
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import es.sisifo.test.ws.webservices.UsuarioWebService;
import es.sisifo.test.ws.webservicesimpl.UsuarioService;



public class UsuariosWebServiceFactoryBean implements FactoryBean<UsuarioWebService> {

 private final String namespaceUri;
 private final String serviceName;
 private final String wsdlLocation;
 private final String serviceEndPoint;


 public UsuariosWebServiceFactoryBean(final String namespaceUri, final String serviceName, final String wsdlLocation, final String serviceEndPoint) {
  this.namespaceUri = namespaceUri;
  this.serviceName = serviceName;
  this.wsdlLocation = wsdlLocation;
  this.serviceEndPoint = serviceEndPoint;
 }


 @Override
 public UsuarioWebService getObject() throws Exception {
  final QName serviceQName = new QName(namespaceUri, serviceName);
  final Resource serviceWSDLResource =  new ClassPathResource(wsdlLocation);

  final UsuarioService service = new UsuarioService(serviceWSDLResource.getURL(), serviceQName);
  final UsuarioWebService port = service.getUsuarioWebServiceImplPort();

  final BindingProvider bindingProvider = (BindingProvider)port;
  bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, serviceEndPoint);

  return port;
 }


 @Override
 public Class<?> getObjectType() {
     return UsuarioWebService.class;
 }


 @Override
 public boolean isSingleton() {
  return true;
 }
}

To use this class, you only need to declare this factory as a normal bean, for example, in the application-context.xml:
<bean id="usuarioService" class="es.sisifo.test.usuarios.webservice.UsuariosWebServiceFactoryBean">   
 <constructor-arg name="namespaceUri"      value="${webService.usuarios.namespaceUri}" />
 <constructor-arg name="serviceName"       value="${webService.usuarios.serviceName}" />
 <constructor-arg name="wsdlLocation"      value="${webService.usuarios.wsdlDocumentLocation}" />
 <constructor-arg name="serviceEndPoint"   value="${webService.usuario.serviceEndPoint}" />
</bean>

And this would be the properties file:
webService.usuarios.namespaceUri     = http://webservicesimpl.ws.testc.es/
webService.usuarios.serviceName      = usuarioService
webService.usuarios.wsdlLocation     = /wsdl/UsuarioService.wsdl
webService.usuarios.serviceEndPoint  = http://localhost:8080/test-ws/servizos/UsuarioService

Note that the WSDL file is in the application classpath, i.e, in main/resources folder, inside wsdl directory.
You can check how to create all necesary classes for the client in this post: http://trabajosdesisifo.blogspot.com.es/2015/04/eclipse-maven-configure-wsdl2java.html 

domingo, 21 de junio de 2015

Spring MVC Portlet Interceptor

How to create a interceptor in Spring MVC Portlet

<bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
 <property name="portletModeMap">
  <map>
   <entry key="view" value-ref="oneController" />
  </map>
 </property> 
 
 <property name="interceptors">
  <list>
   <ref bean="pruebaCustomInterceptor" />
  </list>
 </property>
</bean>
    
<bean id="pruebaCustomInterceptor" class="es.pruebas.portlet.cve.controller.CustomInterceptor"/>

The bean "oneController" is just a MVC Portlet controller, a class annotated with @Controller.

public class CustomInterceptor extends org.springframework.web.portlet.handler.HandlerInterceptorAdapter {

   @Override
    protected boolean preHandle(final PortletRequest request, final PortletResponse response, final Object handler) throws Exception {      
        return super.preHandle(request, response, handler);
    }
}

domingo, 24 de mayo de 2015

JBoss + Camel JMS + Spring Example

In this post I´ll show you how to configure Apache Camel JMS to read messages from a Queue in JBoss.

Versions:
  • Apache Camel 2.12.0
  • Spring 3.2.4.RELEASE
  • JBoss EAP 6.2
  • HornetQ
The server side and producer configuration can be found in helloworld-jms, in https://github.com/jboss-developer/jboss-eap-quickstarts/tree/6.2.x.
Basically, there is a queue named "testQueue" and a simple app that send messages to it. In helloworld example there is a producer and a consumer, but in this case the consumer can be omited because it will be a camel route.

Camel project config

Pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>es.cixtec.arq.pruebas</groupId>
  <artifactId>camel-dynamic-route</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
    
 <properties>
  <camel.version>2.12.0</camel.version>
  <version.jboss.as> 7.3.0.Final-redhat-14</version.jboss.as>     
  <spring.version>3.2.4.RELEASE</spring.version>
  <slf4j.version>1.6.6</slf4j.version>  
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>
 
 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.3</version>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
    </configuration>
   </plugin>


   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
   </plugin>


   <plugin>
    <groupId>org.apache.camel</groupId>
    <artifactId>camel-maven-plugin</artifactId>
    <version>${camel.version}</version>
   </plugin>  

  </plugins>
  
 </build>
 
 <dependencies>
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-core</artifactId>
   <version>${camel.version}</version>   
  </dependency>
  
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-jms</artifactId>
   <version>${camel.version}</version>   
  </dependency>
  
  <dependency>
   <groupId>org.jboss.as</groupId>
   <artifactId>jboss-as-jms-client-bom</artifactId>
   <version>${version.jboss.as}</version>
   <type>pom</type>
  </dependency>
        
  <dependency>
   <groupId>org.apache.camel</groupId>
   <artifactId>camel-spring</artifactId>
   <version>${camel.version}</version>   
  </dependency>
  
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>${slf4j.version}</version>
  </dependency>
  
 </dependencies>
 
</project>

Spring + camel config:
<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" 
 xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd     
     http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

 <!-- ********************************************* -->
 <!-- ********** JMS CONFIG *********************** -->
 <!-- ********************************************* -->
 <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
  <property name="environment">
   <props>
    <prop key="java.naming.factory.initial">org.jboss.naming.remote.client.InitialContextFactory</prop>
    <prop key="java.naming.provider.url">remote://localhost:4447</prop>
    <prop key="java.naming.security.principal">quickstartUser</prop>
    <prop key="java.naming.security.credentials">quickstartPwd1!</prop>    
   </props>
  </property>
 </bean>
 
 <bean id="jmsQueueConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
  <property name="jndiTemplate" ref="jndiTemplate"/>  
  <property name="jndiName" value="jms/RemoteConnectionFactory"/>  
 </bean>

 <bean name="jms" class="org.apache.camel.component.jms.JmsComponent">
  <property name="connectionFactory" ref="jmsQueueConnectionFactory" />
 </bean>

 <!-- ********************************************* -->


 <!-- ****************************** -->
 <!-- ****** RUTA ****************** -->
 <!-- ****************************** -->
 <camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring">
  <route id="mainGestionRoute">
   <from uri="jms:queue:testQueue?username=quickstartUser&amp;password=quickstartPwd1!" />

   <log message="JMSCorrelationID: ${header.JMSCorrelationID}"/>
   <log message="JMSDeliveryMode: ${header.JMSDeliveryMode}"/>
   <log message="JMSDestination: ${header.JMSDestination}"/>
   <log message="JMSExpiration: ${header.JMSExpiration}"/>
   <log message="JMSMessageID: ${header.JMSMessageID}"/>
   <log message="JMSPriority: ${header.JMSPriority}"/>
   <log message="JMSRedelivered: ${header.JMSRedelivered}"/>
   <log message="JMSReplyTo: ${header.JMSReplyTo}"/>
   <log message="JMSMessageID: ${header.JMSMessageID}"/>
   <log message="JMSTimestamp: ${header.JMSTimestamp}"/>
   <log message="JMSType: ${header.JMSType}"/>
   <log message="JMSXGroupID: ${header.JMSXGroupID}"/>
   
   <to uri="log:receivedMessage?level=INFO" />
  </route>
 </camelContext>

</beans>

This route just prints the message and JMS headers received.

domingo, 8 de marzo de 2015

Export SpringBean as Servlet Context Attribute

Suppouse you have a bean callede "oneSpringBean" in your Root Application Context and you want to read it from a Servlet.

There are two solutions.
Simple way, when there is not context mess:

In the servlet you get the bean using WebApplicationContextUtils.getWebApplicationContext(getServletContext()).getBean("oneSpringBean")


Complex way, when you have custom context scopes:


<bean class="org.springframework.web.context.support.ServletContextAttributeExporter">
 <property name="attributes">
  <map>
   <entry key="myAttribute" value-ref="oneSpringBean" />
  </map>
 </property>
</bean>

In the servlet you get the bean using getServletContext().getAttribute("myAttribute")

sábado, 30 de agosto de 2014

Spring MVC Validation - javax.validation

Algunas consideracciones al respecto:

Nota:
        Hay que tener puesto  <mvc:annotation-driven /> para que funcione el @Valid.

Item 1 - javax.validation

Añadiendo anotaciones de javax.validation a un objeto y recibiéndolo como un @ModelAttribute en el controller, al poner @Valid se lanza automáticamente la validación y se rellena el objeto BindingResult.

 @RequestMapping(value="/nuevo", params="accion=crear", method=RequestMethod.POST)  
      public ModelAndView formNuevoCrear(@Valid @ModelAttribute("usuarioForm") UsuarioForm usuarioForm, BindingResult result, RedirectAttributes redirectAttributes) {  
           //Anotando el model attribute con @Valid se fuerza a que se lanze su validación automática antes de entrar en el método.  
           //En caso de detectarse errores se añaden al objeto errors.  
           logger.debug("Acción de crear usuario");  
           if (result.hasErrors()) {  
                ModelAndView mav = new ModelAndView();  
                mav.addObject("tiposUsuario", tipoUsuarioService.getAll());  
                mav.addObject("roles", roleService.getAll());  
                mav.addObject("usuarioForm", usuarioForm);  
                mav.setViewName("app/usuarios/nuevoUsuario");  
                return mav;  
           }  
           Usuario usuario = usuarioForm.getUsuario();  
           usuario = usuarioService.save(usuario);  
           //Mostrar un aviso en el listado indicando que el usuario se creó correctamente  
           redirectAttributes.addFlashAttribute("usuarioCreado", true);   
           ModelAndView mav = new ModelAndView();            
           mav.setViewName("redirect:/usuarios");  
           return mav;  
      }  

En este caso se recibe un Objeto de tipo UsuarioForm que se validará automáticamente.

Item 2 - Validaciones anidadas

Si lo que se recibe de la request es un objeto Form (Bean o POJO adaptador para no usar directamente el objeto de negocio) que, a su vez, contiene un objeto de negocio con anotaciones de validación, hay que anotar con @Valid esa propiedad. De este modo se lanzan las validaciones anidadas.

 public class UsuarioForm {  
      @Valid  
      private Usuario usuario = new Usuario();  
      public Usuario getUsuario() {  
           return usuario;  
      }  
      public void setUsuario(Usuario usuario) {  
           this.usuario = usuario;  
      }       
 }  

 @Entity  
 @Table(name="USUARIO")  
 public class Usuario {  
      @Id @GeneratedValue  
      private Long id;  
      @NotEmpty  
      private String nombre;  
 ...  
 }  


Item 3 - Crear un validador de spring

Se puede usar el objeto de validación propio de spring mvc org.springframework.validation.Validator.
En este caso hay varias opciones para lanzarlo:
  1. Se puede registrar dentro del métod intBinder del controller, con lo que aplicaría a todos los métodos del mismo. Esta suele ser una opción no deseada, porque obliga a tener un validador para cada tipo de objeto recibido como parámetro.
  2. Se puede registrar dentro de un método propio, también con el initBinder. En este caso sólo aplicará a aquellos métodos del controller que tengan un parámetro como el especificado.
     /**  
           * Se restringe el initBinder para que únicamente aplique a los métodos del controller que tenga  
           * un atributo de ese nombre  
           * @param binder  
           */  
          @InitBinder("usuarioForm")  
          protected void initBinderUsuarioForm(WebDataBinder binder) {  
               //Registro el/los validadores manuales aplicables a usuarioForm  
               binder.setValidator(new UsuarioFormValidator());  
          }  
    
  3. Se puede no declarar en ningún lado y ejecutarlo manualmente al entrar en el controller.


Item 4 - Validaciones combinadas

Se pueden combinar validaciones de spring con las de javax, pero dentro de objeto validador de spring.
La idea es invocar manualmente las validaciones javax, y realizar luego el resto de validaciones de negocio más complejas.


 public class UsuarioFormValidator implements Validator {  
      /**  
       * Logger  
       */  
      private final Logger logger = LoggerFactory.getLogger(getClass());  
      @Override  
      public boolean supports(Class<?> clazz) {  
           return UsuarioForm.class.equals(clazz);  
      }  
      @Override  
      public void validate(Object target, Errors errors) {  
           logger.debug("validando usuarioForm");  
           UsuarioForm usuarioForm = (UsuarioForm) target;            
           Usuario usuario = usuarioForm.getUsuario();  
           realizarValidacionesJavax(usuarioForm, errors);            
           realizarValidacionesComplejasNegocio(usuario, errors);            
           logger.debug("UsuarioForm valdiado con {} errores detectados", errors.getErrorCount());  
      }  
      /**  
       * Validaciones generales de javax validation  
       * @param usuarioForm  
       * @param errors  
       */  
      private void realizarValidacionesJavax(UsuarioForm usuarioForm, Errors errors) {  
           ValidatorFactory factory = Validation.buildDefaultValidatorFactory();  
           javax.validation.Validator validator = factory.getValidator();  
        Set<ConstraintViolation<UsuarioForm>> constraintViolations = validator.validate(usuarioForm);        
        for (ConstraintViolation<UsuarioForm> violation : constraintViolations) {  
                errors.rejectValue(violation.getPropertyPath().toString(),  
                          violation.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName(),  
                          violation.getConstraintDescriptor().getAttributes().values().toArray(),  
                          violation.getMessage());  
           }  
      }  
      /**  
       * Validaciones más específicas relacionadas con el negocio  
       * @param usuario  
       * @param errors  
       */  
      private void realizarValidacionesComplejasNegocio(Usuario usuario, Errors errors) {  
           if (usuario.getRoles() != null && usuario.getRoles().size() > 2) {  
                errors.rejectValue("usuario.roles", "error.ususarios.maxRoles", "Sólo se permiten dos roles por usuario");  
           }            
      }  
 }  

Item 5 - De ConstraintViolation a Errors

Al hacer las validaciones de javax manuales, hay que hacer una transformación entre las constraint violation de javax y el objeto errors de spring.
Para ello se recorren los errores detectados y se extrare la información necesaria para meter un rejectValue.

Item 6 - Prevalencia

Si se tiene un @Valid en el controller y un validador registrado de spring, sólo se ejecutarán las validaciones de spring. Las de javax no se ejecutarán.

Item 7 - Internacionalización

Para internacionalizar los mensajes de spring se necesita un messageSource que referencie a los ficheros properties con los códigos de error.

Para internacionalizar los mensajes de javax.validation se necesita un messageInterpolator. El funcionamiento por defecto es buscar en el classpath un fichero ValidationMessages.properties con todos los textos, pero esto presenta el problema de cómo reconocer los cambios de idioma.
Para solucionarlo, spring proporciona una clase LocalValidatorFactoryBean a la que se le puede setear un messageSource, que es el que contendrá todos los códigos de error para los diferentes idiomas.

La configuración resultante será la siguiente (también se ponen los beans para detectar los cambios de idioma y setearlos):

      <!-- ***************************** -->  
      <!-- Mensajes y etiquetas de spring-->  
      <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
        <property name="cacheSeconds" value="60"/>            
           <property name="basenames">  
         <list>                  
          <value>classpath:bundles/errores</value>           
         </list>  
        </property>       
           <property name="defaultEncoding" value="UTF-8" />  
      </bean>  
      <!-- Para la internacionalización de los mensajes de javax.validation se emplea un inter -->  
      <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">  
        <property name="validationMessageSource" ref="messageSource"/>  
       </bean>  
      <!-- ***************************** -->  
      <!-- ***************************** -->  
      <!-- Locale resolver e interceptor -->  
      <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">  
           <property name="defaultLocale" value="gl" />  
      </bean>  
      <mvc:interceptors>  
           <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">  
                <property name="paramName" value="idioma" />  
           </bean>  
      </mvc:interceptors>  
      <!-- ***************************** -->  

También hay que tener en cuenta que, en vez de crear el validator a mano, ahora hay que recibir el inyectado por spring en las clases de validación.


 @Component("usuarioFormValidator")  
 public class UsuarioFormValidator implements Validator {  
      /**  
       * Logger  
       */  
      private final Logger logger = LoggerFactory.getLogger(getClass());  
       @Autowired  
       private javax.validation.Validator validator;  
      ...  
 }  

lunes, 6 de enero de 2014

Spring insight

Instalación

Pasos para desplegar una aplicación web con spring insight:
  1. Descargar sel servidor devoloper mode desde la página principal http://www.gopivotal.com/products/pivotal-tc-server
  2. Descomprimir el archivo en el disco duro.
  3. Abrir una ventana de comandados como administrador:
    • Inicio
    • Todos los programas
    • Accesorios
    • Símbolo del sistema. Botón derecho y ejecutar como administrador
  4. Entrar desde el CMD en el directorio del server, y crear una nueva instancia.
    • tcruntime-instance.bat create myInstanceName -t insight
    • Como se ve en la imagen, al crear la instancia realmente se está creando un nuevo Tomcat preconfigurado para spring-insight. Esto es así porque el server tc viene con una serie de templates, siendo una de ellas la de insight

domingo, 4 de agosto de 2013

Aplicar filtrado Maven en applicationContext

En casi todas las aplicaciones web, a la hora de plantearse el desarrollo es necesario tener en cuenta los distintos entornos en los que se desplegará (Test, Preproducción y Producción, entre otros). Habrá ciertas variables que dependerán de estos entornos, como rutas o algunos parámetros de configuración que habrá que ir actualizando a medida que se hacen los pases.

Una forma sencilla de hacer esto es emplear los filter de maven. Su uso más extendido es el aplicado a los resources de la aplicación, pero también se pueden emplear en el webapp de las aplicaciones web.

En este ejemplo se va a cambiar un parámetro dentro del applicationContext.xml de Spring. Esto también se puede hacer empleando el Expresion Language de Spring y el conf.properties, aunque aquí se hará con filters Maven.

Necesitamos:
  • Uno o mas profiles donde indicar el fichero que contendrá las variables
  • Añadir la sección de filters indicando qué fichero emplear
  • Plugin de resources
  • Configurar war plugin para filtrar el webapp
  • Emplear la variable en el applicationContext

En el pom.xml declaramos los profiles, en los que se especifica en qué ruta está el fichero que contiene las variables a sustituir:


<profiles>
    <!-- Development environment -->
    <profile>
       <id>development</id>
       <activation>
          <activeByDefault>true</activeByDefault>
       </activation>
       <properties>
           <filters.ficheroProperties>src/dev/filters/desarrollo- env.properties</filters.ficheroProperties>
       </properties>
    </profile>
</profiles>


Esto simplemente declara la variable filters.ficheroProperties con el src/dev/filters/desarrollo-env.properties, cuando se emplee el profile devolpment (en este caso es por defecto). Aquí se añadiría un profile por cada entorno y a la hora de generar los wars habría que usar el parámetro -P referenciando el que correspondiese.

Este fichero será un properties corriente:

mavenProperty.jdbc = jdbc/miDatasource


Dentro del apartado de build, se declaran el fichero que contiene las variables de filtrado. Es aquí donde se usa la variable declarada en el profile, a fin de que cambie en cada entorno.


<!-- Se le indica de qué ficheros se obtienen los valores de las variables que se reemplazan -->
<filters>
      <filter>${filters.ficheroProperties}</filter>
</filters>



En el apartado de plugins, se añade el maven-resources-plugin:

<!-- Plugin para gestionar los resources y el filtrado -->
<plugin>    
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-resources-plugin</artifactId>
       <version>2.4.3</version>
       <configuration>
            <encoding>UTF-8</encoding>
      </configuration>
</plugin>


También en el apartado de plugins, se añade configuración extra al maven-war-plugin:


<plugin>    
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-war-plugin</artifactId>
     <version>2.3</version>
        <configuration>
           <!-- Configurar filtrado de ficheros dentro del webapp -->
           <webResources>
             <resource>
              <filtering>true</filtering>
              <directory>src/main/webapp/WEB-INF</directory>
              <includes>
                 <include>**/*.xml</include>
              </includes>
            </resource>
          </webResources>
      </configuration>
</plugin>


En este último plugin se indica que se van a filtrar los recursos dentro de WEB-INF cuya extensión sea .xml. De este modo, a la hora de componer el war se van a leer todos los ficheros xml de este directorio buscando variables Maven para reemplazar.


Por último, basta con emplear las variables declaradas en el fichero de properties dentro del applicationContext.xml

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="${mavenProperty.jdbc}" />
</bean>