Showing posts with label Maven. Show all posts
Showing posts with label Maven. Show all posts

Monday, June 30, 2014

Spring sample application, environment dependent properties files, Logback, JUnit and Ehcache

In this article we will create a basic maven Spring application. The application will contain following features:

  1. Depending on an environment variable (call it env), the application will specific parameters, e.g: DB URL, schemas, and so on.
  2. The application must write some custom log messages to a particular log file. Let's say that this file will contain specific log messages to be visualised by admin
  3. The application will use Ehcache to cache calls to specific methods
PS: The source code of this article is on github.

1. Create the project with maven

First, let's create a maven web project. To do so, go to project placement (where you want to create the project) and run following command (you must have maven installed, see the link for instructions):

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp

 It will ask you to enter the groupId, the artifactId, the version and the package for the newly generated project.
After that, go to Eclipse (or your favorite IDE) and import the project. In Eclipse, right click in the project explorer and chose Import -> Import... After that, a dialog will be opened:

Chose "Existing Maven Projects" and click Next>.
Now browse to where you executed the mvn command and select the newly created (by maven) project having for name the value of artifact that you gave:



And click Finish.
You will see the newly imported project under your projects explorer in Eclipse:


Now right click on src/main folder and chose New->Folder and name it "java"
After that, right click on src folder and chose New->Folder and name it "test". And add another folder named "java" under the newly created "test" folder. Now you project should look like this:


Now open the pom.xml file and change it so it looks like the following:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.raissi</groupId>
  <artifactId>test-maven-project</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>test-maven-project Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
   <!-- Just for tests -->
 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.0.1</version>
  <scope>test</scope>
 </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <properties>
  <spring.version>4.0.4.RELEASE</spring.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <build>
  <finalName>spring-cache-tutorial</finalName>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
     <encoding>${project.build.sourceEncoding}</encoding>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.4</version>
    <configuration>
     <archive>
      <manifestEntries>
       <DisableIBMJAXWSEngine>true</DisableIBMJAXWSEngine>
      </manifestEntries>
     </archive>
    </configuration>
   </plugin>

  </plugins>
 </build>
</project>

You need now to run (right click on the project) Maven->Update Project...
Now the project should be ready to add some code to it.

2. Prepare the Spring application

As said in the introduction, this is a Spring application, so first we need to add the Spring dependenciies to our project. In pom.xml file, add following:

<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring.version}</version>
   <exclusions>
    <exclusion>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
    </exclusion>
   </exclusions>
  </dependency>
  <!-- Various Application Context utilities, including EhCache, JavaMail, 
   Quartz, and Freemarker integration Define this if you need any of these integrations -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.7.4</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>1.7.4</version>
  </dependency>
  <!-- JSR 330 -->
  <dependency>
   <groupId>javax.inject</groupId>
   <artifactId>javax.inject</artifactId>
   <version>1</version>
  </dependency>

Now let's create the applicationContext.xml file to configure Spring: Under src/main/webapp/WEB-INF add a file named applicationContext.xml:

Next, open the file and add the following:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task"
 xmlns:cache="http://www.springframework.org/schema/cache"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context-4.0.xsd
 http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    http://www.springframework.org/schema/task
    http://www.springframework.org/schema/task/spring-task-4.0.xsd
    http://www.springframework.org/schema/cache 
    http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

 <!-- Base package for Spring to look for annotated beans -->
 <context:component-scan base-package="com.raissi" />
 <!-- Activates various annotations to be detected in bean classes: Spring's @Required and @Autowired, as well as JSR 
    250's @PostConstruct, @PreDestroy and @Resource (if available) etc...-->
 <context:annotation-config></context:annotation-config>

</beans>

For now, the file contains only the base package to tell Spring where to find our beans. And also the context:annotation-config to tell Spring that we are using annotations to define our resources.

3. Properties file

By now, we have Spring correctly configured. So let's move to the first requirement of our application.
The goal here is to have multiple properties files, and that our application uses the right one for every environment. 
Let's say you have different servers on different machines, one of these servers is dedicated to DEV teams, another is for TEST teams, and another one is for PRODUCTION. And that you are using Jenkins or another Continuous Integration tool to build and deploy your application.
Evidently, the 3 environments have different values for config parameters like DB URL, username and password etc...
To not be obliged to change these values manually in your properties files, or using maven to change them, we will create three properties files, each one is dedicated to a specific environment:
spring-sample.dev.propertiesspring-sample.test.properties and spring-sample.prod.properties. Please notice that the only difference in their names is the words "dev", "test" and "prod" after "spring-sample".
The idea is to tell Spring to load the appropriate file based on an environment variable that we will call "env".
In a PRODUCTION server, the value "env" must be equal to "prod" so our application loads the spring-sample.prod.properties file. Same thing for DEV and TEST environments.
So you must create a new environment variable in your system having "env" as a name, and either "dev", "test" or "prod" as value. You can change the value later to check that the right file is being loaded.
After creating the variable, you must shutdown Eclipse and start it again so that it can be aware of the newly created variable. Notice, that if you click on "Restart" eclipse, the JVM does not exit, and by that the new variable is not discovered.
After that, let's edit the applicationContext.xml file to add following entry:

<!-- application.properties will contain all our config data: db username, 
  password, etc... -->
 <context:property-placeholder
  location="classpath:spring-sample.${env}.properties"/>
As the documentation says, the "property-placeholder" entry "Activates replacement of ${...} placeholders by registering a PropertySourcesPlaceholderConfigurer within the
 application context". For example, if you want to define a dataSource to access the DB and have its parameters like jdbcUrl been defined in the properties file, you just write the following:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  destroy-method="close">
 <property name="driverClass" value="${db.className}" />
 <property name="jdbcUrl" value="${db.url}" />
 <property name="user" value="${db.username}" />
 <property name="password" value="${db.password}" />     
</bean>
And then in your spring-sample.dev.properties file (we suppose your "env" variable value is equal to "dev"), define values for db.className, db.url etc...
e.g: db.username=raissi.

And that's it!! Very simple, but very useful. By now, depending on your environment type, Spring will load the appropriate properties file.

4. Add Logback

Logback is a logging library for Java. It "is intended as a successor to the popular log4j project". Written by the same author of log4j, Logback offers a faster implementation, "Logback is intended as a successor to the popular log4j project". More reasons why you should use Logback can be found here.
To use Logback, you need firstly to add its dependencies to your pom.xml:

<!-- SLF4J -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.5</version>
  </dependency>

  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>jcl-over-slf4j</artifactId>
   <version>1.7.5</version>
  </dependency>

  <!-- Log Back -->
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.1.1</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-core</artifactId>
   <version>1.1.1</version>
  </dependency>

You may also have noticed, that in Spring dependencies I excluded commons-logging. In fact, Spring uses by default Commons Logging for their logs. And to make it use Logback, you must exclude commons-logging like I did for Spring dependencies
Now, let's configure Logback. It's very simple, all you need is to add (like log4j) an XML file called logback.xml to your application classpath:


P.S: If you were using Log4j and you want just to convert your log4j.properties file automatically, there is an online tool for this.
Before configuring our loggings, let's explain what we want to do:

  1. The console must display all logs (depending on our global log level)
  2. There must be a log file for every level, one for debug, one for error and one for info messages
  3. There must be some particular messages (of any level) that must be written to a special file. Let's say they are special messages intended for admins of the application.
  4. When reaching a particular size, the log file is compressed in a zip archive.
from the docs, "Logback delegates the task of writing a logging event to components called appenders". So you need to define an appender for each specific need. for example, to write to the console:

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <encoder>
   <pattern>%d{[yyyy-MM-dd] HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
  </encoder>
 </appender>
This does not do so much. Let's now define an appender that writes info messages to a specific file:


<!-- We use a RollingFileAppender to backup the log files depending in the previously mentioned ZIP archives -->
 <appender name="FILE-INFO"
  class="ch.qos.logback.core.rolling.RollingFileAppender">
  <!-- The file location -->
  <file>${log.basefolder}/${log.info.filename}</file>
 
  <!-- The rolling policy how to rollover files 
    Here I am chosing to to keep up to 3 zip archives, and then delete the oldest one
  -->
  <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
   <fileNamePattern>${log.rolling.folder}/sample-info.%i.log.zip</fileNamePattern>
   <minIndex>1</minIndex>
   <maxIndex>3</maxIndex>
  </rollingPolicy>
 
  <!-- The trigger to rollover a file, here I'm using a size based trigger. When file is up to maxFileSize, a rollover takes place -->
  <triggeringPolicy
   class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
   <maxFileSize>${log.file.maxSize}</maxFileSize>
  </triggeringPolicy>
  
  <!-- The pattern for our messages, just like Log4j -->
  <encoder>
   <pattern>%d{[yyyy-MM-dd] HH:mm:ss.SSS} %-5level %logger{36} - %msg%n</pattern>
  </encoder>
  <!-- The level filter, accept only INFO messages in this appender -->
  <filter class="ch.qos.logback.classic.filter.LevelFilter">
   <level>INFO</level>
   <onMatch>ACCEPT</onMatch>
   <onMismatch>DENY</onMismatch>
  </filter>
 </appender>
You can see in the comments inside the code that we are using an Appender to accept only INFO messages, and to write them to a specific file. We are also using a Rollover policy to backup files into up to 3 zip archives, and then when max size exceeded, delete the oldest one and continue.
One other thing to notice, is the use of placeholders like: ${log.info.filename}. 
To be able to use such placeholders, you neeed to add a property to specify from where to load these placeholder values:

<property resource="spring-sample.${env}.properties" />
And here we are referring to our environment dependent resource file, previously used with Spring. The nice thing here, is that Logback recognizes environment variables, just like Spring does.
Here is an example of my  spring-sample.dev.properties file:

log.basefolder=path-to-a-folder-that-will-contain-our-log-files
log.info.filename=sample-info.log
log.debug.filename=sample-debug.log
log.error.filename=sample-error.log
log.audit.filename=sample-audit.log

#Max size for a log file
log.file.maxSize=5MB
#folder to save in log files when the log size exceeds maxSize
log.rolling.folder=path-to-a-folder-that-will-contain-our-log-files-archives

Same thing should be done for debug and error levels.
Another thing we need with our logs, is to have a custom file for admin messages. To do so, we need to use another filter for our appender other than the LevelFilter previously used with INFO messages.
this time, we will use an EvaluatorFilter that use Markers to decide of the type of messages:

<appender name="AUDIT_FILE" class="ch.qos.logback.core.FileAppender">
  <!-- the filter element -->
  <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
   <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
    <!-- you can use any other value, just make sure, you use the same value in your Java code -->
    <marker>AUDIT_SYS</marker>
   </evaluator>
   <onMismatch>DENY</onMismatch>
   <onMatch>ACCEPT</onMatch>
  </filter>
  
  <file>${log.basefolder}/${log.audit.filename}</file>
  <encoder>
   <pattern>%d{[yyyy-MM-dd] HH:mm:ss.SSS} %level %logger{36} - %msg %n</pattern>
  </encoder>

  <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
   <fileNamePattern>${log.rolling.folder}/sample-audit.%i.log.zip</fileNamePattern>
   <minIndex>1</minIndex>
   <maxIndex>3</maxIndex>
  </rollingPolicy>

  <triggeringPolicy
   class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
   <maxFileSize>${log.file.maxSize}</maxFileSize>
  </triggeringPolicy>
 </appender>
Here the only that changed (compared to previous appender for INFO messages) is the Filter part.

The last thing to do, is tell Logback to use our defined appenders, and also to specify the global log level for our application:

 <!-- Levels by packages and classes: --> 
 <!-- you can define as many as you want loggers. just like in Log4j, 
 and this may be also by class or by package -->
    <logger name="com.sample.services" level="debug"/>
    <logger name="org.springframework.jdbc.core" level="TRACE">
    <appender-ref ref="STDOUT" />
    </logger>
 <logger name="com.sample.Foo" level="info"/>
 
 <root level="debug">
  <appender-ref ref="STDOUT" />
  <appender-ref ref="FILE-INFO" />
  <appender-ref ref="FILE-DEBUG" />
  <appender-ref ref="FILE-ERROR" />
  <appender-ref ref="AUDIT_FILE" />
 </root>
The last thing to do now is to log some messages from our Java code:
package com.raissi;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

public class LogbackDemoClass {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogbackDemoClass.class);
    private static final Marker CACHE_LOG = MarkerFactory.getMarker("AUDIT_SYS");
    
    @Test
    public void testLogs(){
        LOGGER.debug("This message will go to debug file only, and will contain a param: {}", "paramValue");
        LOGGER.debug(CACHE_LOG, "This message will go to debug file and admin log file also, and will contain a param: {}", "paramToAdmin");
    }
}
Few thing are to note here:

  • We use the same value to build the Marker object as the one defined in logback.xml file
  • No need to test if debug is enabled like we should do in other logging libraries
  • We use placeholders to introduce parameters, so that the complete String message is only built if the message is really going to be printed
By now, you should have a very good and convenient Logback configuration. So enjoy logging!!

5. Caching with Ehcache

The last part of this article, is to configure Ehcache to be used with our Spring application. It's mainly used to cache expensive calls that have results which change rarely, or at known rate.
If you google for "Ehcache with Spring example", you will find so many tutorials about this subject. So why am I writing about it again ? It's to address a point rarely considered on these tutorials. 
So let's begin by configuring Ehcache for our application.
First thing to do, is to add Ehcache dependecies to you pom.xml file:

  <!-- Ehcache -->
  <dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache</artifactId>
   <version>2.7.4</version>
  </dependency>

Next you need to configure Spring to use Ehcache for our caching. In fact, Spring can be configured to use multiple cache implementations. See the docs for more details.
So, in applicationContext.xml file, add the following:

<!-- Tell Spring that we going to use cache annotations in our Java code -->
 <cache:annotation-driven/>
 <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"
  p:cache-manager-ref="ehcache" />
 <!-- EhCache library setup -->
 <bean id="ehcache"
  class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
  p:config-location="classpath:ehcache.xml" />
Here we are referring to a file named "ehcache.xml", it's there where to configure Ehcache, about how to define our cache. see this page for details about what you should define there.
Here is my ehcache.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
 <defaultCache eternal="false" maxElementsInMemory="100"
  overflowToDisk="false" />
 <cache name="spring-cache" maxElementsInMemory="1000000" eternal="false"
  overflowToDisk="false" />
</ehcache>
Main things to notice here are the cache named "spring-cache", we will refer to it in our cache annotations later. There also the maxElementsInMemory property that defines to max elements to be contained in this cache. You can define more than one cache.
Now in your Spring beans, you just add annotations to cache method calls, or to evict elements from cache:
Here is a simple example of a service class:

package com.raissi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class PersonServiceImpl implements PersonService{
 
 private static final Logger LOGGER = LoggerFactory.getLogger(PersonServiceImpl.class);
 private static final Marker CACHE_LOG = MarkerFactory.getMarker("AUDIT_SYS");
 
 private List<String> units = new ArrayList<>(Arrays.asList("UNIT01", "UNIT02", "UNIT03"));
 private List<String> persons = new ArrayList<>(Arrays.asList("PERSON01", "PERSON02", "PERSON03"));
 
 
 @Override
 @Cacheable(value = "spring-cache")
 public List<String> getPersonNames(String department){
  LOGGER.info(CACHE_LOG, "Getting Person names of department {}", department);
  return new ArrayList<>(persons);
 }

 @Override
 @Cacheable(value = "spring-cache")
 public List<String> getDepartmentUnitNames(String department) {
  LOGGER.info(CACHE_LOG, "Getting unit names of department {}", department);
  return units;
 }
 
 @Override
 @CacheEvict(value = { "spring-cache" }, key="#root.targetClass.getName() + 'getPersonNames' + #department")
 public void savePersonInDepartment(String person, String department){
  persons.add(person);
 }
 
 @Override
 public void savePersonInDepartmentNoEvict(String person, String department){
  persons.add(person);
 }
}

Just very simple. whenever we want to cache a method call, we annotate it with Cacheable, giving the cache name as value.
The only point that may be a little tricky here is the CacheEvict annotation. This is used, to evict an entry from the cache. Let me explain.
In the case of the getPersonNames(String department) method, we are caching its results, for example, when first time, you call getPersonNames("DEPT01") the call is going to invoke the method implementation returning list of persons in department "DEPT01".
Next time the method is called, the result is fetched from the cache, which means, the method implementation won't be invoked.
Now, what if we want to add a new person to this department. In this situation, we have to evict the entry associated with the DEPT01 from the cache. This is done by annotating the method that modifies the content of DEPT01 with CacheEvict. We need to give the annotation the key of the entry to be deleted from the cache. Which gets us to the main point of the part of the article. The cache keys.

Default cache key generator in Spring

When caching a method call (which is similar to putting an object in a map), Spring generates a key for it so it can be got next time a call made to that method. For this, Spring offers a default key generation mechanism. This is done via DefaultKeyGenerator in Spring versions prior to Spring 4. In Spring 4, the default key generator is SimpleKeyGenerator.
If you look at the code of key generation :

@Override
 public Object generate(Object target, Method method, Object... params) {
  if (params.length == 0) {
   return SimpleKey.EMPTY;
  }
  if (params.length == 1) {
   Object param = params[0];
   if (param != null && !param.getClass().isArray()) {
    return param;
   }
  }
  return new SimpleKey(params);
 }
And here, you can notice that only the parameters of the method are considered in key generating.
You can refer to this Jira issue to see a discussion about it.
So, by default, both our cached methods getPersonNames and getDepartmentUnitNames would return same values on their second calls with the same "DEPT01" value as parameters.
And this would be really catastrophic if not considered.
So what to do ?
First solution, would be to add a key to every Cacheable annotation like this:

@Cacheable(value="atlas", key="#root.targetClass + #root.methodName + #department")
This will add the class name and the method name to the generated key. And this really solves the problem.

Custom key generator

Using the mentioned solution to give special key to every method, solves the problem, but this would be a tedious task to add a key to every Cacheable annotation, and it would be a bug generator, in case one forgets to include a parameter in the key.
A better solution for this would be to tell Spring to use our custom key generator instead of the SimpleKeyGenerator.
For this, we need to add a new class implementing the KeyGenerator interface:


package com.raissi.spring.cache;

import java.lang.reflect.Method;

import org.springframework.cache.interceptor.KeyGenerator;

public class CacheKeyGenerator implements KeyGenerator {

 @Override
 public Object generate(final Object target, final Method method,
   final Object... params) {
  StringBuilder key = new StringBuilder(method.getDeclaringClass().getName()).append(method.getName());
  if(params != null){
   for(Object obj: params){
    key.append(obj.toString());
   }
  }
  return key.toString();
 }
}
Now we are including the method and class names in the generated key. The only thing that remains is to tell Spring to use our CacheKeyGenerator instead of SimpleKeyGenerator.
In applicationContext.xml; 
<!-- Change it to reference our KeyGenerator class -->
<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="com.raissi.spring.cache.CacheKeyGenerator" />
Notice that I changed the cache:annotation-driven to include a key-generator attribute.

And that's it!

6. Testing your Spring applications with JUnit

The final part of this article is to show you how to test your beans with JUnit and Spring-test.
First add following dependencies:

<!-- Tests -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${spring.version}</version>
  </dependency>
  <!-- JUnit -->
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.11</version>
   <scope>test</scope>
  </dependency>

  <!-- Mockito -->
  <dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-core</artifactId>
   <version>1.9.5</version>
   <scope>test</scope>
  </dependency>
Next, go to "Java resources, src/test/java" and create a new package:

There we will create a base class: AbstractContextTests, all other test classes will extend it. This class will define context config location of Spring, it ensures that a WebApplicationContext will be loaded for the test:

package com.raissi.spring.test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;

@WebAppConfiguration
@ContextConfiguration(value={"file:src/main/webapp/WEB-INF/applicationContext.xml"})
public class AbstractContextTests {

 @Autowired
 protected WebApplicationContext wac;

}
And now let's create our test classes:
package com.raissi.spring.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.raissi.PersonService;

@RunWith(SpringJUnit4ClassRunner.class)
public class PersonServiceTest extends AbstractContextTests {

 private static final Logger logger = LoggerFactory.getLogger(PersonServiceTest.class);
 private static final Marker CACHE_LOG = MarkerFactory.getMarker("AUDIT_SYS");
 
 @Autowired
 private PersonService personService;
 
 @Test
 public void test(){
  final String dept1 = "DEPT1";
  for(int i=0; i <3; i++){
   logger.info("Calling PersonService for unit names of dept {}, got {}", dept1, personService.getDepartmentUnitNames(dept1));
   logger.info("Calling PersonService for persons names of dept {}, got {}", dept1, personService.getPersonNames(dept1));
  }
  
  logger.debug(CACHE_LOG, "Adding new person {} to dept: {} with evict", "PERS04", dept1);
  personService.savePersonInDepartment("PERS04", dept1);
  logger.debug(CACHE_LOG, "Calling PersonService for persons names of dept {}, got {}", dept1, personService.getPersonNames(dept1));
  
  logger.info("Adding new person {} to dept: {} with no evict", "PERS05", dept1);
  personService.savePersonInDepartmentNoEvict("PERS05", dept1);
  logger.info("Calling PersonService for persons names of dept {}, got {}", dept1, personService.getPersonNames(dept1));
  
  
  logger.debug(CACHE_LOG, "Adding new person {} to dept: {} with evict", "PERS06", dept1);
  personService.savePersonInDepartment("PERS06", dept1);
  logger.debug(CACHE_LOG, "Calling PersonService for persons names of dept {}, got {}", dept1, personService.getPersonNames(dept1));
 }
}

And that's it, you just right click on the class and chose Run As -> JUnit Test.

I hope this will be of some help.

Thursday, July 18, 2013

JSF 2, Spring, Spring Security, Hibernate/JPA, Jasypt, application sample available at Github

If you have followed my previous articles series about JSF 2 and different frameworks integration, then you can find the complete application source code on Github here.
Please feel free to post your comments, suggestions and ameliorations

Saturday, May 25, 2013

JSF 2, Maven, Tomcat 7 and Eclipse integration

This is the first article of a series of articles that I will dedicate to the JSF technology. In this series, I will implement a complete web application that shows how to integrate many frameworks: Primefaces, Prettyfaces, JasperReports, Freemarker, Hibernate etc...
But now we will begin by setting up a simple JSF2 project using Eclipse and Maven. Please notice that I suppose you already have Java 7, Eclipse Juno and Tomcat 7 installed on your machine.
Once you have all of these prepared, here are the steps to create a simple JSF 2, Maven enabled project:
  1) Install the m2eclipse plugin, you can find the path to install site here. (You can also use the Eclipse Marketplace from the Help menu).

  2) Once you finished the m2eclipse installation and restarted Eclipse, you can create new maven enabled projects: File->New->Other->Maven->Maven Project.


  3) Next you need to specify the project location. I let it with default values:


4) Now you need to choose an Archetype for your project which is "in short, Archetype is a Maven project templating toolkit" (introduction to Archetypes here). You should select the "maven-archetype-webapp" entry:



  5) Next you need to specify the Archetype parameters: Group Id and Artifact Id (here a link to maven naming conventions) and then press finish:



  6) Once the project created, we need to add some facets to it. Right click the project. Choose Properties. Then select "Project Faces". By default there is no facets for this project, so you should select: "Convert to faceted form...":



  7) Once converted, Eclipse show you a list of available facets. By default the Java facet should be selected. You need to verify the version to be used and select if not selected the 1.7 version. Also, select the Dynamic Web Module 3.0 facet, click Apply and then Ok:



  8) After adding these facets, Eclipse will automatically add some elements to your projects, like the "Java Resources" part. Also there is a folder named WebContent that is created, you should delete it.

  9) Now we need to add the Apache Tomcat 7 plugin to our project. So in the pom.xml file, ensure your build part looks like this:

    jsfsample2
    
  
      maven-compiler-plugin
      
          
          1.7
          1.7
      
      2.3.2
  
  
   org.apache.tomcat.maven
   tomcat7-maven-plugin
   2.0
  
 
 src/main/webapp/WEB-INF/classes
  

And now, just run it:

Right click the project -> Run -> Run Configurations -> Right click Maven Build -> New. For the base directory, click Browse Workspace and select our project from the list. In the goals input, give: tomcat7:run and click Apply and Close. Please use tomcat7:run instead of tomcat7:run-war (shown in picture). The latter Goal will require restarting Tomcat even when changing xhtml files.



By now everything is prepared, we only need to add some code and maven config to add JSF2 dependencies.


Implementation


For this first article of the JSF series, we will implement two simple pages: one as a login page and the other as the user home page that will display user profile.

1) Add JSF Maven dependencies

There are two dependencies to include in the pom.xml file (Project Object Model file under the project) for a JSF 2 application to run under Tomcat 7. We need also to configure the "maven-compiler-plugin" to use Java 7, so our pom.xml file should be like this:



  4.0.0
  com.raissi
  jsfsample1
  war
  0.0.1-SNAPSHOT
  jsfsample1 Maven Webapp
  http://maven.apache.org
  
    
      junit
      junit
      3.8.1
      test
    
    
    
  com.sun.faces
  jsf-api
  2.1.7
 
 
  com.sun.faces
  jsf-impl
  2.1.7
 
    
  
  
    jsfsample1
    
  
      maven-compiler-plugin
      
          1.7
          1.7
      
      2.3.2
  
 

src/main/webapp/WEB-INF/classes
  



Notice, that with every change in the pom.xml file, you need to right click the project -> Maven -> Update Project.

2) JSF implementation

Now that everything is in place, we should begin the implementation of our application. The first thing to do is to configure our deployment descriptor (web.xml file, under: src/main/webapp/WEB-INF). We need to declare the Faces Servlet (the servlet that will handle all user requests to jsf pages). So here is the result:


  First JSF Sample
  
  
    javax.faces.PROJECT_STAGE
    Development
  
  
  
    Faces Servlet
    javax.faces.webapp.FacesServlet
  
  
    Faces Servlet
    *.jsf
  
  
    index.jsp
  
  
  
  


Now, you add a faces-config.xml file, which will be just empty for now:

  


Now we can begin to implement our pages and Java classes. As we said previously, our first example will be a simple app that allows user login and profile display. So we need a User class that will represent a user. This is called a DTO: Data Transfer Object; it will handle data transferred between the layers of our application.  We also need a ManagedBean in which we will define our actions that will be called from xhtml pages (The View in the MVC2 Design Pattern). ManagedBeans also will contain data to be displayed in the View.
So here is the User class:
package com.raissi.entities;

import java.io.Serializable;

public class User implements Serializable{

 private static final long serialVersionUID = 3571343460175211199L;
 
 private String login;
 private String password;
 private String firstName;
 private String lastName;
 
 public User(String login, String password, String firstName, String lastName) {
  super();
  this.login = login;
  this.password = password;
  this.firstName = firstName;
  this.lastName = lastName;
 }
 
 public String getLogin() {
  return login;
 }
 public void setLogin(String login) {
  this.login = login;
 }
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public String getFirstName() {
  return firstName;
 }
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 public String getLastName() {
  return lastName;
 }
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 
}


You can see that it is a simple class to represent user entities. It will be used to transfer data between different layers (business, persistence, presentation) in Model part of the application. Remember that JSF is a MVC2 technology.
And here the UserManagedBean:
package com.raissi.managedbeans;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

import com.raissi.entities.User;

@ManagedBean(name="userManagedBean")
@SessionScoped
public class UserManagedBean implements Serializable{

 private static final long serialVersionUID = 152717293232892353L;
 private static final String HOME_PAGE="home?faces-redirect=true";
 private User user;
 private String userLogin;
 private String password;
 
 public String login(){
  if(userLogin != null && password != null){
   return someLoginMethod();
  }
  return "login?faces-redirect=true";
 }
 
 private String someLoginMethod(){
  if(userLogin.equals("tom") && password.equals("tom")){
   user = new User(userLogin, password, "Tom", "Hanks");
   return HOME_PAGE;
  }else if(userLogin.equals("forrest") && password.equals("forrest")){
   user = new User(userLogin, password, "Forrest", "Gump");
   return HOME_PAGE;
  }
  return "login?faces-redirect=true";
 }

 public User getUser() {
  return user;
 }

 public void setUser(User user) {
  this.user = user;
 }

 public String getUserLogin() {
  return userLogin;
 }

 public void setUserLogin(String userLogin) {
  this.userLogin = userLogin;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 } 
}

The UserManagedBean contains two String properties (userLogin and password). They will be filled with user input in the login page. This class contains also a User property, it will be constructed based on what credentials are submitted when the login() method (action) is called. Basically, this method will make a call to a business class that will fetch user from a database or another persistent data via the DAO layer. For now, we just use an internal method that only accept two login values, tom and forrest.
With JSF 2 also, we can just specify the name of the view to which we want to redirect user as a return of our actions. No need to define our mappings in faces-config.
Now we provide the xhtml pages that are responsible for the View part in our application. Notice that JSP technology is replaced with Facelets in JSF (especially JSF 2).
The login.xhtml page: 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html 
xmlns:f="http://java.sun.com/jsf/core" 
xmlns:h="http://java.sun.com/jsf/html" 
xmlns="http://www.w3.org/1999/xhtml">
 
    
        JSF Maven Eclipse integration
        
    
    
     

Please enter your credentials: tom/tom or forrest/forrest

</html>
In this page we define two inputs, one for the login and one for the password. We also define a button and the corresponding action to be called when it's pressed. In the UserManagedBean#login() method we decided that user will be redirected to the home view if login is successful. For now, this page will just display a welcome message to the user, displaying his full name:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html 
xmlns:f="http://java.sun.com/jsf/core" 
xmlns:h="http://java.sun.com/jsf/html" 
xmlns="http://www.w3.org/1999/xhtml">
 
    
        JSF Maven Eclipse integration - Your home page
    
    
    
     Welcome home dear 
    
    
     Dear guest, you are not allowed to see this content
    
    
</html>
In the home page (home.xhtml), notice that we used the rendered attribute of panelGrid. This attribute is used to display (render) components based on particular conditions. These conditions here are whether the user property in UserManagedBean is empty or not. In other word, do we have a loggedin user or not.
Finally, you should have noticed that in our web.xml descriptor we defined an index.jsp page as welcome file. This will be a simple page, that just redirect user to login page. Here is the index.jsp page to be placed under: src/main/webapp
<% response.sendRedirect("pages/login.jsf"); %>
Notice the .jsf extension used to call the login view. In fact, we declared the Faces Servlet (in web.xml) to handle only requests to .jsf views. If a user calls a page with .xhtml extension, then, JSF components won't be rendered.
The final structure of our eclipse project is as in the picture:



If you want to load JS/CSS/Images with JSF components (as we did in login.xhtml page with the style.css file), then you should place those resources under: main/webapp/resources directory.
Now you want to test the application. Right click the project -> Run As -> (Select the name you gave to the Run Configuration explained when adding the project to Tomcat 7: step 9 in configuration part).
Finally, if you followed this tutorial literally you can call the login page via this URL: http://localhost:8080/jsfsample1/pages/login.jsf

Conclusion

In this introductory article, we saw how to create a JSF2 maven based project with Eclipse, and how to run it using a Run Configuration with Tomcat7.
In next part of this JSF series, we will integrate Primefaces and Prettyfaces libraries to our application.