The average developer when they hear the word Nacar, they ask what it is and how it works. For the experienced developer in Madrid, Spain, Nacar is the money maker that keeps you employed. Nacar is a Java development tool created by Accenture Spain around 10 years ago. It was originally designed as a tool for developing GUI interfaces and it was retrofitted to be able to support web applications. Pesado is the Swing GUI portion and Ligero is the Web client portion.
10 years ago Nacar was an advancement in development, but unfortunately Accenture didn't keep up with it and integrate open source standards. Today, Nacar is still in use by Accenture, Everis and BBVA Spain. BBVA is a large bank based out of Madrid with a small presence in the states. They were the first to use Nacar and it probably wouldn't exist if it wasn't for them commissioning it.
Even today Accenture touts that this is the best Java development platform, but there is little about it today that is modern. It is actually quite antiquated, has many limitations and requires a small army of people to develop and maintain applications in it. It's a perfect tool if you are billing by the hour because in comparison to open standards like Spring, Struts or just plain J2EE development, it takes 2-3 times as long to develop applications with it.
The Nacar developer has two components at their disposal. The Visual Nacar component which allows users to drag and drop Views/Windows and services and it has a piece that plugs into Rational Application Developer that allows you to use some custom tags that are in the framework. The custom tags are extremely limited and don’t provide much value.
While you have to admit this all sounds good. The learning curve for Nacar is steep. It takes a couple months to be productive at best. The framework is entirely owned and developed by Accenture with some involvement from the BBVA architecture group in Madrid. This doesn't lend itself for anyone outside of Accenture to be able to help in a time of need and it allows Accenture to charge whatever they want for a developer in Nacar since noone else has the expertise. Everis is another company that has accrued some Nacar knowledge, but I doubt there are very far removed from Accenture.
The problems really start to add up for Nacar when you factor in that they are still stuck on JDK 1.4. Everything is pretty much home-rolled. There are absolutely no standard libraries. Even for simple functions, Nacar has its own code. Nacar supports Web services by actually converting a map representation to Soap that supports no particular Web Service standards. It doesn't support namespace alias. It just puts the namespace into every tag making the Soap process slow and unwieldy. Instead of just using JAX-WS or some type of generated client, the developers try to generate all the Soap themselves manually.
In Nacar, there is not really an idea of separation and modular design. All of the apps run on a single server and they all share common code, preventing you from being able to isolate a single app for upgrade. Instead of simple J2EE modules like an ear and a war, it is a manual deployment process to Web Sphere for each app. This is also a holdover from the pre J2EE days.
To top it all off, the end product is really poor. The limitations in the tools leave the user experience lacking. The apps look like apps did 10 years ago. Again this was great back then, but in an Ajax-driven, dynamic development world we are in now, this just doesn't cut it.
Overall of the many projects I have worked on, this was by far the ugliest framework I have ever seen and unfortunately, it continues to be used and maintained. When I worked in it, a fellow coworker came up with a great nickname for it… Nacrap…
Tuesday, August 24, 2010
Friday, April 30, 2010
JQuery...my new favorite Ajax framework
After a several stints with Dojo, Ext and Prototype, I decided to see if JQuery could provide anything better. I figure it's a good thing to be versed in several Ajax frameworks. You never know which one you might use next really. I am definitely weary of Dojo and it's lack of support and constant revamping to remove things that worked previously. I love Ext, but turning around and charging for it was really a bad idea on their part. I think they have the best forums and support and their API is really easy to use.
After looking into JQuery, I was pleasantly surprised to find out how lightweight it was. It is a tiny fraction of the size of the Dojo library and the syntax of use is more concise and direct. Of course, this lack of size is mainly because all of the widgeting control is included in other separate plugins that you can add on, but I do like that. It is simple to download a plugin or write your own. I really avoid writing something custom unless a last resort. There is always one out there ready for use.
So, I put JQuery into my project and I already had a Spring Controller using Content negotiation serving up my JSON and I gave it a whirl. It just a few minutes I was able to get data from the JSON object and then populate anything or do anything I wanted to do with it. I accomplished it in just a few lines of code.
$(document).ready(function(){
$.getJSON("rest/DataService/loanTypes.json",{id: $(this).val(), ajax: 'true'}, function(j){
var options = '';
var loanTypes = j.loanTypeList;
var currOptions = $("select#loanTypes").html(options);
for (var i = 0; i < loanTypes.length; i++) { options += '';
}
$("select#loanTypes").html(options);
});
});
...
At first the $ notation takes a little getting used to, but just think of it as a dojo.byId on steroids. It's a really powerful notation. This is just a snippet from the example, but I just get the JSON from the server and populate a select box manually creating option elements. Ordinarily I would frown upon doing this, but it's just an example. A proper way would be to use a JQuery UI component that would just take the JSON automatically.
I found several plugins that were really useful on the JQuery site. I even used JQuery UI and another plugin to replace alert messages with Ajax Alerts.
Most of the other libraries out there are all very self contained, but JQuery just allows you to download plugins at will. This is very powerful but it has it's drawbacks. You have to make sure that the plugin you download and all it's dependencies all match with the supported versions of each. This can make upgrading JQuery difficult if you use a lot of plugins. I recommend using them cautiously.
You can find a list of all the JQuery plugins here.
http://plugins.jquery.com/
You can download and install JQuery into your project as simply as downloading the js file and including in your HTML page.
Go get it and try it out.
JQuery http://www.jquery.com
If you want support or see other examples of how other users are leveraging the framework, check out the forum.
http://forum.jquery.com/
After looking into JQuery, I was pleasantly surprised to find out how lightweight it was. It is a tiny fraction of the size of the Dojo library and the syntax of use is more concise and direct. Of course, this lack of size is mainly because all of the widgeting control is included in other separate plugins that you can add on, but I do like that. It is simple to download a plugin or write your own. I really avoid writing something custom unless a last resort. There is always one out there ready for use.
So, I put JQuery into my project and I already had a Spring Controller using Content negotiation serving up my JSON and I gave it a whirl. It just a few minutes I was able to get data from the JSON object and then populate anything or do anything I wanted to do with it. I accomplished it in just a few lines of code.
$(document).ready(function(){
$.getJSON("rest/DataService/loanTypes.json",{id: $(this).val(), ajax: 'true'}, function(j){
var options = '';
var loanTypes = j.loanTypeList;
var currOptions = $("select#loanTypes").html(options);
for (var i = 0; i < loanTypes.length; i++) { options += '';
}
$("select#loanTypes").html(options);
});
});
...
At first the $ notation takes a little getting used to, but just think of it as a dojo.byId on steroids. It's a really powerful notation. This is just a snippet from the example, but I just get the JSON from the server and populate a select box manually creating option elements. Ordinarily I would frown upon doing this, but it's just an example. A proper way would be to use a JQuery UI component that would just take the JSON automatically.
I found several plugins that were really useful on the JQuery site. I even used JQuery UI and another plugin to replace alert messages with Ajax Alerts.
Most of the other libraries out there are all very self contained, but JQuery just allows you to download plugins at will. This is very powerful but it has it's drawbacks. You have to make sure that the plugin you download and all it's dependencies all match with the supported versions of each. This can make upgrading JQuery difficult if you use a lot of plugins. I recommend using them cautiously.
You can find a list of all the JQuery plugins here.
http://plugins.jquery.com/
You can download and install JQuery into your project as simply as downloading the js file and including in your HTML page.
Go get it and try it out.
JQuery http://www.jquery.com
If you want support or see other examples of how other users are leveraging the framework, check out the forum.
http://forum.jquery.com/
Wednesday, April 28, 2010
Make your Web Services simple with Apache CXF and Spring
Have you ever just wanted to make Java Web Services so easy that you can whip one out in a few minutes, well here is your shot. Apache CXF and Spring make creating a Web Service almost stupid simple. I'll get right to it.
First you need to configure CXF in your web application by adding a few lines to the web.xml.
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
All this is doing is setting it up so that all requests for /ws/* to get routed to CXF. Now we need to include a new xml file for Spring to load.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd"
default-autowire="byName">
<!-- Load CXF modules from cxf.jar -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="openSessionIn" class="com.domain.spring.cxf.HibernateInInterceptor">
<property name="singleSession" value="false" />
</bean>
<bean id="openSessionOut" class="com.domain.spring.cxf.HibernateOutInterceptor">
<property name="singleSession" value="false" />
</bean>
<!-- Enable message logging using the CXF logging feature -->
<cxf:bus>
<cxf:inInterceptors>
<ref bean="openSessionIn"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="openSessionOut"/>
</cxf:outInterceptors>
<cxf:features>
<cxf:logging />
<cxf:fastinfoset/>
</cxf:features>
</cxf:bus>
<!-- Aegis data binding -->
<bean id="aegisBean"
class="org.apache.cxf.aegis.databinding.AegisDatabinding"
scope="prototype">
<property name="configuration">
<bean class="org.apache.cxf.aegis.type.TypeCreationOptions">
<property name="defaultMinOccurs" value="1"/>
<property name="defaultNillable" value="false"/>
</bean>
</property>
</bean>
<bean id="jaxws-and-aegis-service-factory"
class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
scope="prototype">
<property name="dataBinding" ref="aegisBean"/>
<property name="serviceConfigurations">
<list>
<bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
<bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>
<bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/>
</list>
</property>
</bean>
<!-- Service endpoint -->
<jaxws:endpoint id="applicationWebService"
implementorClass="com.domain.services.impl.ApplicationServiceImpl"
implementor="#applicationService"
address="/applicationService">
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
<entry key="faultStackTraceEnabled" value="true" />
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
<jaxws:serviceFactory>
<ref bean="jaxws-and-aegis-service-factory"/>
</jaxws:serviceFactory>
</jaxws:endpoint>
</beans>
Now, we are ready to write our service class. I typically create service classes to interact with my other backend services, databases, web services, etc. Here is an example.
@WebService(endpointInterface = "com.domain.services.IApplicationService", name = "applicationService", serviceName = "applicationService", portName = "applicationPort", targetNamespace = "http://services.domain.com/")
@Service(name="applicationService")
public class ApplicationServiceImpl extends ServiceImpl implements
IApplicationService {
public String sayHello(){
return "Hello";
}
...
And that is pretty much it. Start up your Web server and hit /ws/applicationService?wsdl and you should get a WSDL for the service automatically generated. You can use SoapUI as a testing tool to test out the services. You can also use SoapUI to generate any type of client that you want for the services, JAX-WS, JAX-RPC, Axis, you name it.
It doesn't get much easier than that does it? I've only scratched the surface of CXF as well. It has many more features.
Spring and CXF make a truly versatile way to create an SOA architecture or an Enterprise Service Bus all on your own as well.
First you need to configure CXF in your web application by adding a few lines to the web.xml.
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
All this is doing is setting it up so that all requests for /ws/* to get routed to CXF. Now we need to include a new xml file for Spring to load.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd"
default-autowire="byName">
<!-- Load CXF modules from cxf.jar -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="openSessionIn" class="com.domain.spring.cxf.HibernateInInterceptor">
<property name="singleSession" value="false" />
</bean>
<bean id="openSessionOut" class="com.domain.spring.cxf.HibernateOutInterceptor">
<property name="singleSession" value="false" />
</bean>
<!-- Enable message logging using the CXF logging feature -->
<cxf:bus>
<cxf:inInterceptors>
<ref bean="openSessionIn"/>
</cxf:inInterceptors>
<cxf:outInterceptors>
<ref bean="openSessionOut"/>
</cxf:outInterceptors>
<cxf:features>
<cxf:logging />
<cxf:fastinfoset/>
</cxf:features>
</cxf:bus>
<!-- Aegis data binding -->
<bean id="aegisBean"
class="org.apache.cxf.aegis.databinding.AegisDatabinding"
scope="prototype">
<property name="configuration">
<bean class="org.apache.cxf.aegis.type.TypeCreationOptions">
<property name="defaultMinOccurs" value="1"/>
<property name="defaultNillable" value="false"/>
</bean>
</property>
</bean>
<bean id="jaxws-and-aegis-service-factory"
class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
scope="prototype">
<property name="dataBinding" ref="aegisBean"/>
<property name="serviceConfigurations">
<list>
<bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
<bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>
<bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/>
</list>
</property>
</bean>
<!-- Service endpoint -->
<jaxws:endpoint id="applicationWebService"
implementorClass="com.domain.services.impl.ApplicationServiceImpl"
implementor="#applicationService"
address="/applicationService">
<jaxws:properties>
<entry key="schema-validation-enabled" value="true" />
<entry key="faultStackTraceEnabled" value="true" />
<entry key="mtom-enabled" value="true"/>
</jaxws:properties>
<jaxws:serviceFactory>
<ref bean="jaxws-and-aegis-service-factory"/>
</jaxws:serviceFactory>
</jaxws:endpoint>
</beans>
Now, we are ready to write our service class. I typically create service classes to interact with my other backend services, databases, web services, etc. Here is an example.
@WebService(endpointInterface = "com.domain.services.IApplicationService", name = "applicationService", serviceName = "applicationService", portName = "applicationPort", targetNamespace = "http://services.domain.com/")
@Service(name="applicationService")
public class ApplicationServiceImpl extends ServiceImpl implements
IApplicationService {
public String sayHello(){
return "Hello";
}
...
And that is pretty much it. Start up your Web server and hit /ws/applicationService?wsdl and you should get a WSDL for the service automatically generated. You can use SoapUI as a testing tool to test out the services. You can also use SoapUI to generate any type of client that you want for the services, JAX-WS, JAX-RPC, Axis, you name it.
It doesn't get much easier than that does it? I've only scratched the surface of CXF as well. It has many more features.
Spring and CXF make a truly versatile way to create an SOA architecture or an Enterprise Service Bus all on your own as well.
Tuesday, April 27, 2010
Caching Java methods with Spring 3
A while back I wrote about caching Java methods using an earlier version of Spring, but now I am pleased to announce that you can cache your methods using annotations with Spring 3 using Ehcache annotations for Spring.
Similar to my other post you can cache a Java method in a Spring bean by simply adding the annotation.
@Cacheable(cacheName="dogCache")
public Collection getDogs() throws Exception {
...
I really put this annotation to work in my latest project because I called many Web Services in my code that return very static data that is managed by our Enterprise Service Bus. There isn't any need to go get the data every time since it hardly ever changes.
Using the annotation, I specify that the cache region I want to use is dogCache from the ehcache.xml file and I have it configured specific to have it handle caching dogs exactly like I want it to, even to overflow to disk if necessary. Now let's look at the configuration necessary.
You will need to drip the jar file for ehcache-annotations in your WEB-INF/lib and then make the following modifications to your applicationContext.xml.
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache-spring.xml</value>
</property>
</bean>
<ehcache:annotation-driven cache-manager="cacheManager" />
And that's it, you are now caching your results. Be very cautious with using this, each and every method that gets cached must be thoroughly thought through. For example, I accidentally placed this annotation on a method that returned domain objects that were already configured to be cached via Hibernate, thus doubling the amount of memory that the server was using for each object loaded.
While a very powerful tool that can increase performance, caching can also decrease it.
Similar to my other post you can cache a Java method in a Spring bean by simply adding the annotation.
@Cacheable(cacheName="dogCache")
public Collection
...
I really put this annotation to work in my latest project because I called many Web Services in my code that return very static data that is managed by our Enterprise Service Bus. There isn't any need to go get the data every time since it hardly ever changes.
Using the annotation, I specify that the cache region I want to use is dogCache from the ehcache.xml file and I have it configured specific to have it handle caching dogs exactly like I want it to, even to overflow to disk if necessary. Now let's look at the configuration necessary.
You will need to drip the jar file for ehcache-annotations in your WEB-INF/lib and then make the following modifications to your applicationContext.xml.
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache-spring.xml</value>
</property>
</bean>
<ehcache:annotation-driven cache-manager="cacheManager" />
And that's it, you are now caching your results. Be very cautious with using this, each and every method that gets cached must be thoroughly thought through. For example, I accidentally placed this annotation on a method that returned domain objects that were already configured to be cached via Hibernate, thus doubling the amount of memory that the server was using for each object loaded.
While a very powerful tool that can increase performance, caching can also decrease it.
Wednesday, December 23, 2009
Spring - Caching Java methods
Caching has become an increasing concern for developers wanting to increase performance but still utilize the flexibility of Web services in their applications. There is no doubt that Web Services are extremely versatile, but the HTTP protocol combined with the marshalling and unmarshalling of SOAP, assuming you aren't using a REST web service, is very slow and doesn't perform very well.
The answer for most of this has come in the form of caching. I have used EHCache, my favorite open source Java caching framework on several projects in the past 3 years and I've used it in many different ways to increase the performance and response times of Ajax applications. Using a cache is not for the inexperienced. You can easily go overboard and degrade performance by utizing too much memory. Deciding what to and what not to cache should be a decision by your entire development team and agreed upon before implementation. I do not recommend letting a single developer run wild with a cache in hand. It's probably just as dangerous as running with scissors.
Spring has many different way to handle integration with caching frameworks, including EHCache. Hibernate also supports many caching implementations and I use those as well. For spring 2.5 and below, there is even a library that specifically gives you the ability to use special tags in your context files to handle caching of data.
In this particular example, we are going to look at how we can use Spring, AOP and EHCache to cache the returned data of a java method, so that you don't actually have to write the code to manage storing the objects in the cache yourself.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:bean="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">
<!-- this only works in Spring 2.5 currently and you need a special third-party jar as well.
<ehcache:config configLocation="classpath:ehcache-CacheService.xml" />
<ehcache:annotations>
<ehcache:caching id="getDistributedCache"
cacheName="distributedCache" />
</ehcache:annotations>
-->
<bean id="cacheServiceCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache-CacheService.xml</value>
</property>
</bean>
<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager">
<ref local="cacheServiceCacheManager"/>
</property>
<property name="cacheName">
<value>org.company.cache.METHOD_CACHE</value>
</property>
</bean>
<bean id="methodCacheInterceptor" class="com.company.services.interceptor.MethodCacheInterceptor">
<property name="cache">
<ref local="methodCache" />
</property>
</bean>
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*methodOne</value>
<value>.*methodTwo</value>
</list>
</property>
</bean>
There is the configuration and now we need a class to actually do the caching work. If you noticed above you can indicate which methods in a Spring Bean get cached by specifying a regular expression matching pattern. So for example, you could have a service class that returns a User (getCachedUser) and in your configuration .*Cached.* to match any spring bean with any method with Cached in it's name.
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
private Cache cache;
/**
* sets cache name to be used
*/
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Checks if required attributes are provided.
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one.");
}
/**
* main method
* caches method result if method is configured for caching
* method results must be serializable
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
logger.debug("looking for method result in cache");
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null) {
//call target/sub-interceptor
logger.debug("calling intercepted method");
result = invocation.proceed();
//cache method result
logger.debug("caching result");
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
return element.getValue();
}
/**
* creates cache key: targetName.methodName.argument0.argument1...
*/
private String getCacheKey(String targetName,
String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName)
.append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i=0; i
sb.append(".")
.append(arguments[i]);
}
}
return sb.toString();
}
}
With this example, you can see how generic you can make you caching. You may also want to tweak the ehcache settings as well. Ehcache is extremely configurable. You have many many options. Notice that above we specified a configuration file of ehcache-CacheService.xml. In this file we'll take care of adding the caches that we want to use. Ehcache support replication via RMI, JMS and JGroups. This example replicates the cache among nodes in the cluster using RMI. Each node is discovered on it's own so this file is the same on every server.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" >
<diskStore path="java.io.tmpdir/CacheService"/>
<cacheManagerEventListenerFactory class="" properties=""/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="hostName=127.0.0.1,
peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, =32"/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic,
multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=1"
propertySeparator=","
/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="port=40001,
remoteObjectPort=40002,
socketTimeoutMillis=120000"
propertySeparator="," />
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
<defaultCache
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</defaultCache>
<cache name="org.company.cache.METHOD_CACHE"
maxElementsInMemory="10000"
maxElementsOnDisk="1000"
eternal="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
<cache name="distributedCache"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="true">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
</ehcache>
The answer for most of this has come in the form of caching. I have used EHCache, my favorite open source Java caching framework on several projects in the past 3 years and I've used it in many different ways to increase the performance and response times of Ajax applications. Using a cache is not for the inexperienced. You can easily go overboard and degrade performance by utizing too much memory. Deciding what to and what not to cache should be a decision by your entire development team and agreed upon before implementation. I do not recommend letting a single developer run wild with a cache in hand. It's probably just as dangerous as running with scissors.
Spring has many different way to handle integration with caching frameworks, including EHCache. Hibernate also supports many caching implementations and I use those as well. For spring 2.5 and below, there is even a library that specifically gives you the ability to use special tags in your context files to handle caching of data.
In this particular example, we are going to look at how we can use Spring, AOP and EHCache to cache the returned data of a java method, so that you don't actually have to write the code to manage storing the objects in the cache yourself.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:bean="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">
<!-- this only works in Spring 2.5 currently and you need a special third-party jar as well.
<ehcache:config configLocation="classpath:ehcache-CacheService.xml" />
<ehcache:annotations>
<ehcache:caching id="getDistributedCache"
cacheName="distributedCache" />
</ehcache:annotations>
-->
<bean id="cacheServiceCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation">
<value>classpath:ehcache-CacheService.xml</value>
</property>
</bean>
<bean id="methodCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager">
<ref local="cacheServiceCacheManager"/>
</property>
<property name="cacheName">
<value>org.company.cache.METHOD_CACHE</value>
</property>
</bean>
<bean id="methodCacheInterceptor" class="com.company.services.interceptor.MethodCacheInterceptor">
<property name="cache">
<ref local="methodCache" />
</property>
</bean>
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor"/>
</property>
<property name="patterns">
<list>
<value>.*methodOne</value>
<value>.*methodTwo</value>
</list>
</property>
</bean>
There is the configuration and now we need a class to actually do the caching work. If you noticed above you can indicate which methods in a Spring Bean get cached by specifying a regular expression matching pattern. So for example, you could have a service class that returns a User (getCachedUser) and in your configuration .*Cached.* to match any spring bean with any method with Cached in it's name.
import java.io.Serializable;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
public class MethodCacheInterceptor implements MethodInterceptor, InitializingBean {
private static final Log logger = LogFactory.getLog(MethodCacheInterceptor.class);
private Cache cache;
/**
* sets cache name to be used
*/
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Checks if required attributes are provided.
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(cache, "A cache is required. Use setCache(Cache) to provide one.");
}
/**
* main method
* caches method result if method is configured for caching
* method results must be serializable
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;
logger.debug("looking for method result in cache");
String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = cache.get(cacheKey);
if (element == null) {
//call target/sub-interceptor
logger.debug("calling intercepted method");
result = invocation.proceed();
//cache method result
logger.debug("caching result");
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
return element.getValue();
}
/**
* creates cache key: targetName.methodName.argument0.argument1...
*/
private String getCacheKey(String targetName,
String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName)
.append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i=0; i
sb.append(".")
.append(arguments[i]);
}
}
return sb.toString();
}
}
With this example, you can see how generic you can make you caching. You may also want to tweak the ehcache settings as well. Ehcache is extremely configurable. You have many many options. Notice that above we specified a configuration file of ehcache-CacheService.xml. In this file we'll take care of adding the caches that we want to use. Ehcache support replication via RMI, JMS and JGroups. This example replicates the cache among nodes in the cluster using RMI. Each node is discovered on it's own so this file is the same on every server.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" >
<diskStore path="java.io.tmpdir/CacheService"/>
<cacheManagerEventListenerFactory class="" properties=""/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="hostName=127.0.0.1,
peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, =32"/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic,
multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=1"
propertySeparator=","
/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="port=40001,
remoteObjectPort=40002,
socketTimeoutMillis=120000"
propertySeparator="," />
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
<defaultCache
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</defaultCache>
<cache name="org.company.cache.METHOD_CACHE"
maxElementsInMemory="10000"
maxElementsOnDisk="1000"
eternal="false"
overflowToDisk="true"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
<cache name="distributedCache"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
overflowToDisk="true">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
</ehcache>
Tuesday, December 22, 2009
Hibernate Validation latest and greatest
I recently decided to upgrade my Hibernate Validator code and discovered that the framework had changed quite a bit in 4.0.2 to support the new Java validation API. I wanted to share some of my code in the hopes that it would help some of you become familiar with how to use this framework.
The framework itself still pretty much works the same way for annotating your Hibernate classes, it's the programmatic validation and the custom validators that are different. Let's look at an example from a Hibernate class. Remember that you are not restricted to using the validation framework with just Hibernate classes. It's just that with Hibernate annotated classes the validation occurs automatically before the database is hit. You don't have to manually validate when using Hibernate ORM.
@NotNull //enforce that this cannot be null
@NotEmpty //enforce it cannot be an empty string
@Column(name="username", nullable=false)
@Index(name="IDX_USER_NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Let's assume that we want to call a validation on this class manually. I added a method to my GenericDAO to accomplish this, but you could do this anywhere you like. I prob should have lazy initialized this, but for the sake of example, I'll just mention what I should have done.
protected GenericDAO(final Class type) {
this.type = type;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
//validation
public Set> validate(final T t) {
return validator.validate(t);
}
You can also use the validator in Spring by using the @Valid annotation. Learn more
Now we can use an instance of the UserDAO to validate our object.
@Autowired
UserDAO userDAO;
User user = new User();
user.setUserName("");
userDAO.validate(user);
This method will return a Collection of ConstraintViolation so that you can process and do what you want with the result. I created a method in a utility to format and return the result. It's not elegant, but it works.
public static String formatViolations(Set violations) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation violation : violations) {
sb.append("----------------------------------------------------\n");
sb.append("Property: " + violation.getPropertyPath() + "\n");
sb.append("Message: " + violation.getMessage() + "\n");
sb.append("Value " + violation.getInvalidValue() + "\n");
sb.append("Entity " + violation.getLeafBean() + "\n");
sb.append("----------------------------------------------------\n");
}
return sb.toString();
}
That's all pretty simple and as you could see, this mechanism could work on any class. Remember there are many different annotations for validation, including but not limited to @Size, @Length, @Email etc...
Aside from usual suspects in the out of the box validation implementation, you also have the ability to create your own. This is where I had to rewrite some of my old code to migrate to the latest validations framework.
Let's look at a validation I created to enforce rules on passwords by a specified strategy. First we have the annotation @Password that we can apply to a password String field of our User object.
@Constraint(validatedBy = PasswordValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface Password {
PasswordStrategy strategy() default PasswordStrategy.NO_STRATEGY;
String message() default "{validator.invalid_password}";
Class[] groups() default {};
Class[] payload() default {};
}
Now we need the Strategy that we can pass to the annotation to specify which strategy we want to enforce in your application. This class will get ahold of a custom ValidationMessages.properties that is at the root of our classpath to process some messages. It looks complicated, but explaining how it works is beyond the scope of this document.
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public enum PasswordStrategy {
NO_STRATEGY(".*", 0, "validator.invalid_password.NO_STRATEGY"),
DIGIT_STRATEGY("\\d[0-9]", 0,"validator.invalid_password.DIGIT_STRATEGY"),
STRONG_STRATEGY("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d.*\\d)[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$", 0, "validator.invalid_password.STRONG_STRATEGY"),
PASSPHRASE_STRATEGY("^(?=.*[a-z])(?=.*[A-Z])(?=.*[\\s])[a-zA-Z0-9\\s]{6,30}$", 0, "validator.invalid_password.PASSPHRASE_STRATEGY"),
USERID_STRATEGY("^[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$", 0, "validator.invalid_userid.USERID_STRATEGY");
@Override
public String toString() {
return getMessage();
}
PasswordStrategy(String pattern, int flags, String message){
this.pattern = pattern;
this.flags =flags;
this.message = message;
}
private final String pattern;
private final int flags;
private final String message;
private Pattern patternRegEx;
public String getMessage(){
String thisResult = ResourceBundle.getBundle("ValidationMessages").getString(message);
return thisResult;
}
public boolean validate(Object value) {
patternRegEx = Pattern.compile(
pattern,
flags );
if ( value == null ) return true;
if ( !( value instanceof String ) ) return false;
String password = (String) value;
Matcher m = patternRegEx.matcher( password );
return m.matches();
}
}
Now we need the Password annotation implemetation.
public class PasswordValidator implements ConstraintValidator {
private PasswordStrategy strategy;
//part of the Validator contract,
//allows to get and use the annotation values
public void initialize(Password parameters) {
strategy = parameters.strategy();
}
public boolean isValid(String value, ConstraintValidatorContext constraintContext) {
return strategy.validate(value);
}
}
Now we can apply our validation to our User class. Note that we chose the STRONG_STRATEGY that will map to the Regular expression in the PasswordStrategy class.
@NotNull
@NotEmpty
@Column(name="password", nullable=false, insertable=false, updatable=false)
@Password(strategy=PasswordStrategy.STRONG_STRATEGY)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Now here is our ValidationMessages.properties
validator.invalid_password={strategy}
validator.invalid_password.NO_STRATEGY=The password is invalid
validator.invalid_password.DIGIT_STRATEGY=Your password must be digits
validator.invalid_password.STRONG_STRATEGY=A valid password is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and two digits. Make your password stronger by using a special character (.!@#$%^&*) or by including separate words
validator.invalid_password.PASSPHRASE_STRATEGY=A valid password passphrase is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and contain multiple words
validator.invalid_userid.USERID_STRATEGY=A Valid user id must be a minimum of 6 characters and no more than 30
javax.validation.constraints.AssertFalse.message=must be false
javax.validation.constraints.AssertTrue.message=must be true
javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Future.message=must be in the future
javax.validation.constraints.Max.message=must be less than or equal to {value}
javax.validation.constraints.Min.message=must be greater than or equal to {value}
javax.validation.constraints.NotNull.message=may not be null
javax.validation.constraints.Null.message=must be null
javax.validation.constraints.Past.message=must be in the past
javax.validation.constraints.Pattern.message=must match "{regexp}"
javax.validation.constraints.Size.message=size must be between {min} and {max}
org.hibernate.validator.constraints.Email.message=not a well-formed email address
org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}
org.hibernate.validator.constraints.NotEmpty.message=may not be empty
org.hibernate.validator.constraints.Range.message=must be between {min} and {max}
This file is a copy of the default one included in hibernate itself. I have customized it a bit. I don't consider localization in this application, but considering multiple language isn't that difficult using this framework.
The new hibernate 4.0.2 validation mechanism is compliant and inline with the Java Validation Framework defined in JSR303. This was a great step in getting on a common validation framework. If you are starting a new project with Hibernate or just want to use a validation mechanism, Hibernate Validator 4.0.2 or greater is the place to start.
The framework itself still pretty much works the same way for annotating your Hibernate classes, it's the programmatic validation and the custom validators that are different. Let's look at an example from a Hibernate class. Remember that you are not restricted to using the validation framework with just Hibernate classes. It's just that with Hibernate annotated classes the validation occurs automatically before the database is hit. You don't have to manually validate when using Hibernate ORM.
@NotNull //enforce that this cannot be null
@NotEmpty //enforce it cannot be an empty string
@Column(name="username", nullable=false)
@Index(name="IDX_USER_NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
Let's assume that we want to call a validation on this class manually. I added a method to my GenericDAO to accomplish this, but you could do this anywhere you like. I prob should have lazy initialized this, but for the sake of example, I'll just mention what I should have done.
protected GenericDAO(final Class
this.type = type;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
//validation
public Set
return validator.validate(t);
}
You can also use the validator in Spring by using the @Valid annotation. Learn more
@RequestMapping(method=RequestMethod.POST) public String create(@Valid User user, BindingResult result) { if (result.hasErrors()) { return "user/createForm"; } //process adding a user here
//do something
return "redirect:/user/" + user.getId(); }Now we can use an instance of the UserDAO to validate our object.
@Autowired
UserDAO userDAO;
User user = new User();
user.setUserName("");
userDAO.validate(user);
This method will return a Collection of ConstraintViolation so that you can process and do what you want with the result. I created a method in a utility to format and return the result. It's not elegant, but it works.
public static String formatViolations(Set
StringBuilder sb = new StringBuilder();
for (ConstraintViolation violation : violations) {
sb.append("----------------------------------------------------\n");
sb.append("Property: " + violation.getPropertyPath() + "\n");
sb.append("Message: " + violation.getMessage() + "\n");
sb.append("Value " + violation.getInvalidValue() + "\n");
sb.append("Entity " + violation.getLeafBean() + "\n");
sb.append("----------------------------------------------------\n");
}
return sb.toString();
}
That's all pretty simple and as you could see, this mechanism could work on any class. Remember there are many different annotations for validation, including but not limited to @Size, @Length, @Email etc...
Aside from usual suspects in the out of the box validation implementation, you also have the ability to create your own. This is where I had to rewrite some of my old code to migrate to the latest validations framework.
Let's look at a validation I created to enforce rules on passwords by a specified strategy. First we have the annotation @Password that we can apply to a password String field of our User object.
@Constraint(validatedBy = PasswordValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface Password {
PasswordStrategy strategy() default PasswordStrategy.NO_STRATEGY;
String message() default "{validator.invalid_password}";
Class[] groups() default {};
Class[] payload() default {};
}
Now we need the Strategy that we can pass to the annotation to specify which strategy we want to enforce in your application. This class will get ahold of a custom ValidationMessages.properties that is at the root of our classpath to process some messages. It looks complicated, but explaining how it works is beyond the scope of this document.
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public enum PasswordStrategy {
NO_STRATEGY(".*", 0, "validator.invalid_password.NO_STRATEGY"),
DIGIT_STRATEGY("\\d[0-9]", 0,"validator.invalid_password.DIGIT_STRATEGY"),
STRONG_STRATEGY("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d.*\\d)[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$", 0, "validator.invalid_password.STRONG_STRATEGY"),
PASSPHRASE_STRATEGY("^(?=.*[a-z])(?=.*[A-Z])(?=.*[\\s])[a-zA-Z0-9\\s]{6,30}$", 0, "validator.invalid_password.PASSPHRASE_STRATEGY"),
USERID_STRATEGY("^[a-zA-Z0-9.!@#$%^&*()\\s]{6,30}$", 0, "validator.invalid_userid.USERID_STRATEGY");
@Override
public String toString() {
return getMessage();
}
PasswordStrategy(String pattern, int flags, String message){
this.pattern = pattern;
this.flags =flags;
this.message = message;
}
private final String pattern;
private final int flags;
private final String message;
private Pattern patternRegEx;
public String getMessage(){
String thisResult = ResourceBundle.getBundle("ValidationMessages").getString(message);
return thisResult;
}
public boolean validate(Object value) {
patternRegEx = Pattern.compile(
pattern,
flags );
if ( value == null ) return true;
if ( !( value instanceof String ) ) return false;
String password = (String) value;
Matcher m = patternRegEx.matcher( password );
return m.matches();
}
}
Now we need the Password annotation implemetation.
public class PasswordValidator implements ConstraintValidator
private PasswordStrategy strategy;
//part of the Validator
//allows to get and use the annotation values
public void initialize(Password parameters) {
strategy = parameters.strategy();
}
public boolean isValid(String value, ConstraintValidatorContext constraintContext) {
return strategy.validate(value);
}
}
Now we can apply our validation to our User class. Note that we chose the STRONG_STRATEGY that will map to the Regular expression in the PasswordStrategy class.
@NotNull
@NotEmpty
@Column(name="password", nullable=false, insertable=false, updatable=false)
@Password(strategy=PasswordStrategy.STRONG_STRATEGY)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Now here is our ValidationMessages.properties
validator.invalid_password={strategy}
validator.invalid_password.NO_STRATEGY=The password is invalid
validator.invalid_password.DIGIT_STRATEGY=Your password must be digits
validator.invalid_password.STRONG_STRATEGY=A valid password is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and two digits. Make your password stronger by using a special character (.!@#$%^&*) or by including separate words
validator.invalid_password.PASSPHRASE_STRATEGY=A valid password passphrase is a minimum of six characters and a maximum of thirty. It should contains at least one lower case letter, one upper case letter and contain multiple words
validator.invalid_userid.USERID_STRATEGY=A Valid user id must be a minimum of 6 characters and no more than 30
javax.validation.constraints.AssertFalse.message=must be false
javax.validation.constraints.AssertTrue.message=must be true
javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Future.message=must be in the future
javax.validation.constraints.Max.message=must be less than or equal to {value}
javax.validation.constraints.Min.message=must be greater than or equal to {value}
javax.validation.constraints.NotNull.message=may not be null
javax.validation.constraints.Null.message=must be null
javax.validation.constraints.Past.message=must be in the past
javax.validation.constraints.Pattern.message=must match "{regexp}"
javax.validation.constraints.Size.message=size must be between {min} and {max}
org.hibernate.validator.constraints.Email.message=not a well-formed email address
org.hibernate.validator.constraints.Length.message=length must be between {min} and {max}
org.hibernate.validator.constraints.NotEmpty.message=may not be empty
org.hibernate.validator.constraints.Range.message=must be between {min} and {max}
This file is a copy of the default one included in hibernate itself. I have customized it a bit. I don't consider localization in this application, but considering multiple language isn't that difficult using this framework.
The new hibernate 4.0.2 validation mechanism is compliant and inline with the Java Validation Framework defined in JSR303. This was a great step in getting on a common validation framework. If you are starting a new project with Hibernate or just want to use a validation mechanism, Hibernate Validator 4.0.2 or greater is the place to start.
Implementing Hudson Server
Hudson is without a doubt the best tool I have used this year in my projects. Continous integration is very important in an Agile development environment, but it most often involves developers committing time and effort into making it happen and it's a consistent effort that never seems to end. Hudson and Cruise Control are two solutions that change all that.
Let me preface this by saying that I started out using Cruise Control for a month and I then I tried Hudson and in just a few hours, I had removed Cruise Control and I never looked back. Cruise Control has it's merits but Hudson has all the same functionality plus much much more. I highly recommend the migration if you are still stuck with Cruise Control.
Hudson is a Java project that you can deploy standalone or in a web container like Tomcat. It supports building multiple types of projects and tasks and it has a few hundred plugins to customize its functionality. Hudson is a great tool out of the box, but once you browse and install a few plugins, it really comes into it's own.
I spent 5 minutes installing Hudson into Tomcat and then I created a new project through the interface. In the configuration, you can tell Hudson where your project is, SVN, CVS, etc. You set how you build your project, whether it is Maven or Ant and then you are off. Hudson has no limits to the configuration options. You can configure it to poll your version control periodically to look for changes, or build every so often or you can build on demand only. You can configure the project to automatically deploy to a container, publish to a repository and archive your builds.
In a project where I used Hudson, I set a series of projects to build periodically by polling SVN and if there was a change, it would trigger a chain of projects to build to create the final project and deploy the enterprise to a container.
Hudson requires a dedicated server. You don't really want to run this on your desktop. My recommendation is take a decent Linux machine and dedicate it to running your builds and give it plenty of disk space to archive your builds.
Hudson also integrates with tools such as Polarion, ViewCVS and WebSVN to give you reports and link back from changes to see the source. It also will aggregate reports from several sources including Junit reports, TestNG reports, Checkstyle reports, Crap4j and more. Again this is where the plugins are key.
Hudson is easy to maintain and upgrade. Just make sure your HUDSON_HOME environment variable is set to a location where you want all the work to happen and make sure that location is redundant. Don't store anything under the hudson directory that gets created when the container starts. To upgrade, just simple replace the hudson directory and war file and start your container and that's it.
Installing a plugin is as simple as going to the plugin page of the Hudson management screen and choosing the plugins you want and download. It will also let you know when there is a new version of an installed plugin.
Hudson will also cluster together multiple machines and allow you to control them from a single instance. You just add machines as slaves by running the slave.jar on the machines you want to cluster and tell the master Hudson instance where these are. When builds run, Hudson will utilize resources on the other machines to accomplish builds as well as the master machine. This functionality isn't as useful if you have projects that depend on each other. I recommend avoiding having projects depending on others if you can but this is often impossible since all projects should inherit from a set of common framework projects in an enterprise to avoid code duplication and promote reuse.
Hudson has a robust email and notification mechanism with keeping up with build statuses. You can configure emails to be sent to a group on success, emails to be sent to developers who are the culprit behind a failing build and you can even configure options such as Twitter and Calendar updates at the end of a build. There is no shortfall of notifications and events in the system.
The server also had a very pleasing interface. It's very professional looking and has quite a few Ajax-enabled components.
This tool definitely has it all and the best part is that a trained monkey could configure it. Also, once you get your projects running in it, which doesn't take long, you can be hands off and coding while Hudson is taking care of your developer and QA builds. You can eve setup deployments to automate your production deployments and set permissions for users to set who can and cannot do certain tasks.
There really isn't a point if asking if Hudson supports a certain configuration because there is always a plugin to accomplish the task at hand if the out of the box configuration won't do it. Hudson will save you time, many and make a better software product in the process. You can't lose. What are you waiting for? Download Hudson today.
http://hudson-ci.org/
Let me preface this by saying that I started out using Cruise Control for a month and I then I tried Hudson and in just a few hours, I had removed Cruise Control and I never looked back. Cruise Control has it's merits but Hudson has all the same functionality plus much much more. I highly recommend the migration if you are still stuck with Cruise Control.
Hudson is a Java project that you can deploy standalone or in a web container like Tomcat. It supports building multiple types of projects and tasks and it has a few hundred plugins to customize its functionality. Hudson is a great tool out of the box, but once you browse and install a few plugins, it really comes into it's own.
I spent 5 minutes installing Hudson into Tomcat and then I created a new project through the interface. In the configuration, you can tell Hudson where your project is, SVN, CVS, etc. You set how you build your project, whether it is Maven or Ant and then you are off. Hudson has no limits to the configuration options. You can configure it to poll your version control periodically to look for changes, or build every so often or you can build on demand only. You can configure the project to automatically deploy to a container, publish to a repository and archive your builds.
In a project where I used Hudson, I set a series of projects to build periodically by polling SVN and if there was a change, it would trigger a chain of projects to build to create the final project and deploy the enterprise to a container.
Hudson requires a dedicated server. You don't really want to run this on your desktop. My recommendation is take a decent Linux machine and dedicate it to running your builds and give it plenty of disk space to archive your builds.
Hudson also integrates with tools such as Polarion, ViewCVS and WebSVN to give you reports and link back from changes to see the source. It also will aggregate reports from several sources including Junit reports, TestNG reports, Checkstyle reports, Crap4j and more. Again this is where the plugins are key.
Hudson is easy to maintain and upgrade. Just make sure your HUDSON_HOME environment variable is set to a location where you want all the work to happen and make sure that location is redundant. Don't store anything under the hudson directory that gets created when the container starts. To upgrade, just simple replace the hudson directory and war file and start your container and that's it.
Installing a plugin is as simple as going to the plugin page of the Hudson management screen and choosing the plugins you want and download. It will also let you know when there is a new version of an installed plugin.
Hudson will also cluster together multiple machines and allow you to control them from a single instance. You just add machines as slaves by running the slave.jar on the machines you want to cluster and tell the master Hudson instance where these are. When builds run, Hudson will utilize resources on the other machines to accomplish builds as well as the master machine. This functionality isn't as useful if you have projects that depend on each other. I recommend avoiding having projects depending on others if you can but this is often impossible since all projects should inherit from a set of common framework projects in an enterprise to avoid code duplication and promote reuse.
Hudson has a robust email and notification mechanism with keeping up with build statuses. You can configure emails to be sent to a group on success, emails to be sent to developers who are the culprit behind a failing build and you can even configure options such as Twitter and Calendar updates at the end of a build. There is no shortfall of notifications and events in the system.
The server also had a very pleasing interface. It's very professional looking and has quite a few Ajax-enabled components.
This tool definitely has it all and the best part is that a trained monkey could configure it. Also, once you get your projects running in it, which doesn't take long, you can be hands off and coding while Hudson is taking care of your developer and QA builds. You can eve setup deployments to automate your production deployments and set permissions for users to set who can and cannot do certain tasks.
There really isn't a point if asking if Hudson supports a certain configuration because there is always a plugin to accomplish the task at hand if the out of the box configuration won't do it. Hudson will save you time, many and make a better software product in the process. You can't lose. What are you waiting for? Download Hudson today.
http://hudson-ci.org/
Subscribe to:
Posts (Atom)

