Monday, December 21, 2009

#jsf2next: Bean Validation's Final Frontier

There's a scene in The Hitchhiker's Guide to the Galaxy that goes:

He reached out and pressed an invitingly large red button on a nearby panel. The panel lit up with the words Please do not press this button again.

Clearly this is a joke: it's funny because it's obviously bad user interface design. Yet this is precisely where we are today with Java EE 6.

Don't get me wrong: I think Java EE 6 is an awesome spec, and I think Bean Validation and JSF2 work really well together, but just like Seam's s:validateAll, the best you can do is validate the input after the user interaction:

  1. give them a text box, then afterwards tell them their input was too long

  2. give them a text box, then afterwards tell them number must be less than 100

  3. give them a date picker, then afterwards tell them date of birth must be less than today
In short - we let them press the button, then afterwards tell them they shouldn't have pressed it. Ideally, we should:

  1. give them a text box limited to a certain number of characters

  2. give them a number spinner (or slider) capped at 100

  3. give them a date picker capped to before today
In short - we should apply Bean Validation constraints to the component before the user interaction.

There are a variety of ways to achieve this, and obviously I'm biased. But whichever approach we use, I think it's the next logical step - and the final frontier in establishing validation constraints that apply all the way from the back end to the front end.

Tuesday, December 8, 2009

Newbie's Guide to using JBoss Cache in JBoss AS


The promise of JBoss Cache is that it makes distributed caching really easy. But it can be hard to figure out how to use it, because many of the docs are concerned with installation and configuration. Yet the promise of JBoss AS is that everything is already installed, configured and integrated together, so how do you skip all that and get straight to The Good Stuff?

So, this is the blog I was looking for:

If Today You Are Running JBoss AS And Have...

public class MyBeanOrControllerOrWhatever {

   private final static Map MY_CACHE = new HashMap();

   public Object lookupSomething( Object key ) {

      Object value = MY_CACHE.get( key );

      if ( value == null ) {
         value = lookupUncached();
         MY_CACHE.put( key, value );
      }

      return value;
   }
}
(ie. you are using a static Map for your cache - which is fine but will not distribute across VMs)

...Then Using JBoss Cache You Can Do

public class MyBeanOrControllerOrWhatever {

   public Object lookupSomething( Object key ) {

      CacheManager cacheManager = CacheManagerLocator.getCacheManagerLocator().getCacheManager( null );
      Cache myCache = cacheManager.getCache( "sfsb-cache", true );


      Fqn fqn = Fqn.fromString( MyBeanOrControllerOrWhatever.class.getName() );
      Object value = myCache.get( fqn, key );

      if ( value == null ) {
         value = lookupUncached();
         myCache.put( fqn, key, value );
      }

      return value;
   }
}

Where:
  • fqn is a unique key in the JBoss Cache tree so that you don't conflict with other caches

  • sfsb-cache is a pre-configured JBoss AS cache that replicates asynchronously and never expires

  • CacheManagerLocator is in common/lib/jboss-ha-server-api.jar

  • CacheManager is in server/all/lib/jbosscache-core.jar

You'll also need to edit server/all/deploy/cluster/jboss-cache-manager.sar/META-INF/jboss-cache-manager-jboss-beans.xml to pre-start the cache:

<!-- Start these caches as part of the start of this CacheManager -->
<property name="eagerStartCaches">
   <set>
      <value>sfsb-cache</value>
   </set>
</property>

This should be enough to get you working and get you excited. All this actually is in the JBoss Cache docs, just waaaay at the back in Chapter 11. After a while you should probably look at configuring your own cache rather than hijacking sfsb-cache, as that's not strictly meant for your own stuff. Here's a simple one that's kind of a 'distributed Map with max 300 entries':

   <entry>
      <key>my-cache</key>
      <value>
         <bean name="MyCache" class="org.jboss.cache.config.Configuration">
            <property name="isolationLevel">NONE</property>
            <property name="cacheMode">REPL_ASYNC</property>
            <property name="evictionConfig">
               <bean class="org.jboss.cache.config.EvictionConfig">
                  <property name="wakeupInterval">5000</property>
                  <property name="defaultEvictionRegionConfig">
                     <bean class="org.jboss.cache.config.EvictionRegionConfig">
                        <property name="regionName">/</property>
                        <property name="evictionAlgorithmConfig">
                           <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                              <property name="maxNodes">300</property>
                           </bean>
                        </property>
                     </bean>
                  </property>
               </bean>
            </property>
         </bean>
      </value>
   </entry>


Though note that maxNodes refers to the number of Fqn nodes, rather than the Map entries within one of those nodes. So instead of:

myCache.put( fqn, key, value )


You'll have to start doing:

myCache.getChild( fqn ).addChild( Fqn.fromElements( key )).put( key, value )


(ie. create a full-blown child node based on converting each key to an Fqn)

Improvements welcome!