JBoss 7 and java.lang.NoClassDefFoundError: com/sun/image/codec/jpeg/JPEGCodec

So you’re tooling along and make a change to your development environment or deploy to a new server, then you suddenly hit on a stacktrace like this:

Caused by: java.lang.NoClassDefFoundError: com/sun/image/codec/jpeg/JPEGCodec
        at com.aspose.pdf.kit.n.a(Unknown Source) [aspose-pdf-kit-4.3.0.jar:]
        at com.aspose.pdf.kit.PdfConverter.doConvert(Unknown Source) [aspose-pdf-kit-4.3.0.jar:]
        at com.ity.ityweb.util.FileUploadController.copyFile(FileUploadController.java:77) [ITYWeb-ejb.jar:]
        at com.ity.ityweb.util.FileUploadController.handleFileUpload(FileUploadController.java:41) [ITYWeb-ejb.jar:]
        ... 67 more
Caused by: java.lang.ClassNotFoundException: com.sun.image.codec.jpeg.JPEGCodec from [Module "deployment.ITYWeb-ear.ear:main" from Service Module Loader]
        at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:190)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:468)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:456)
        at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
        at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:120)
        ... 71 more

 

Changing your runtime environment (to Java 7, for example) is a good way to cause this. The basic explanation is that com.sun.image.codec.jpeg.JPEGCodec and related libraries have long been marked as depreciated, and you should use the ImageIO library instead. Cold comfort if you are using libraries that you have no control over and can’t do the conversion and re-build them. The quick way to resolve this runtime error in JBoss is to put an entry in the <JBoss-install-diectory>/modules/sun/jdk/main/module.xml file:

<module xmlns="urn:jboss:module:1.1" name="sun.jdk">
    <resources>
        <!-- currently jboss modules has not way of importing services from
        classes.jar so we duplicate them here -->
        <resource-root path="service-loader-resources"/>
    </resources>
    <dependencies>
        <system export="true">
            <paths>
                <path name="com/sun/script/javascript"/>
                <path name="com/sun/jndi/dns"/>
                <path name="com/sun/jndi/ldap"/>
                <path name="com/sun/jndi/url"/>
                <path name="com/sun/jndi/url/dns"/>
                <path name="com/sun/security/auth"/>
                <path name="com/sun/security/auth/login"/>
                <path name="com/sun/security/auth/module"/>
                <path name="sun/misc"/>
                <path name="sun/io"/>
                <path name="sun/nio"/>
                <path name="sun/nio/ch"/>
                <path name="sun/security"/>
                <path name="sun/security/krb5"/>
                <path name="sun/util"/>
                <path name="sun/util/calendar"/>
                <path name="sun/util/locale"/>
                <path name="sun/security/provider"/>
                <path name="META-INF/services"/>
                <path name="com/sun/image/codec/jpeg"/>
            </paths>
            <exports>
                <include-set>
                    <path name="META-INF/services"/>
                </include-set>
            </exports>
        </system>
    </dependencies>
</module>

Note the line path name=”com/sun/image/codec/jpeg” added to the <paths> subtree.

Your stacktrace regarding the com/sun/image/codec/jpeg/JPEGCodec error will be resolved and you will go on your merry way in your deployment.

JBoss Error when accessing web pages: Must call associate() before calling activate()

This problem had been blocking my progress for a while when I upgraded to JBoss Developer Studio 6.0.0 GA and using JBoss AS 7.1.1 Final. When accessing your application’s default page, you’ll get a stacktrace such as this

12:18:18,949 ERROR [org.jboss.seam.exception.Exceptions] (http-localhost-127.0.0.1-8080-1) handled and logged exception: javax.servlet.ServletException: Must call associate() before calling activate()
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:606) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:83) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.LoggingFilter.doFilter(LoggingFilter.java:60) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.IdentityFilter.doFilter(IdentityFilter.java:40) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.MultipartFilter.doFilter(MultipartFilter.java:90) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.ExceptionFilter.doFilter(ExceptionFilter.java:64) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.RedirectFilter.doFilter(RedirectFilter.java:45) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.web.HotDeployFilter.doFilter(HotDeployFilter.java:53) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter$FilterChainImpl.doFilter(SeamFilter.java:69) [jboss-seam.jar:2.3.0.CR1]
        at org.jboss.seam.servlet.SeamFilter.doFilter(SeamFilter.java:158) [jboss-seam.jar:2.3.0.CR1]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:397) [jbossweb-7.0.13.Final.jar:]
        at org.jboss.as.jpa.interceptor.WebNonTxEmCloserValve.invoke(WebNonTxEmCloserValve.java:50) [jboss-as-jpa-7.1.1.Final.jar:7.1.1.Final]
        at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:153) [jboss-as-web-7.1.1.Final.jar:7.1.1.Final]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.13.Final.jar:]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.13.Final.jar:]
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.13.Final.jar:]
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.13.Final.jar:]
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.13.Final.jar:]
        at java.lang.Thread.run(Thread.java:722) [rt.jar:1.7.0_04]
Caused by: java.lang.IllegalStateException: Must call associate() before calling activate()
        at org.jboss.weld.context.AbstractConversationContext.activate(AbstractConversationContext.java:200) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
        at org.jboss.weld.jsf.WeldPhaseListener.activateConversations(WeldPhaseListener.java:108) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
        at org.jboss.weld.jsf.WeldPhaseListener.beforePhase(WeldPhaseListener.java:85) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
        at com.sun.faces.lifecycle.Phase.handleBeforePhase(Phase.java:228) [jsf-impl-2.1.7-jbossorg-2.jar:]
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:99) [jsf-impl-2.1.7-jbossorg-2.jar:]
        at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:116) [jsf-impl-2.1.7-jbossorg-2.jar:]
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
        ... 31 more

 

The “get it fixed and move on” solution for me was to delete the beans.xml file from your META-INF directory in the “EJB” portion of your SEAM project. I haven’t run into any drawbacks yet on my project, as my default beans.xml was empty.

 

Source: https://community.jboss.org/thread/204485

 

Startup Initialization Code in JBoss As

Often, in your application, you wish to have some code executed on startup, for housekeeping, starting services, and otherwise doing ‘wake up’ activities. One possible way to do this is through the ServletContextLoader interface:

public class ContextLoaderListener implements ServletContextListener {

  public void contextInitialized(ServletContextEvent event) {
      .. do initialization stuff here
  }

}

 

Or if you prefer, as of EJB 3.1, instead of putting all your startup code in a central location, you can put startup tasks in independent beans:

 

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Singleton
@Startup
public class OrdersEngine{
int total;

    @PostConstruct
    private void startup() {
        System.out.println("DERRICK: Start initiation!");
    }

    @PreDestroy  
    void atShutdown() {  
             System.out.println("DERRICK: Stopping!");  
    }  
}

 

Note that “Startup” and “PostConstruct” appear in multiple header libraries, so make sure you choose the right ones!

-Derrick

Select Drop Downs in SEAM

Putting a drop-down selector in your SEAM 3.0 application seems much harder than it has to be, partly because of old documentation muddling your internet search for up-to-date documentation. However, once you know how, it’s rather easy.

A drop down looks like this:

 

 

In your Session bean, you declare both your DataModel and corresponding DataModelSelection:

@Stateful
@Name("studentSession")
public class StudentSession implements StudentLocal{

    @Inject    Messages messages;

    @DataModel
    private List<Assignment_Calendar> past_assignments=null;

    @DataModelSelection("past_assignments")
    private Assignment_Calendar selectedPast=null;

    @Factory("past_assignments")
    public List<Assignment_Calendar> getPastassignments(){
        if(past_assignments==null){
           // fetch the "past assignments" object from the database....        
        }    
        return past_assignments;
    }

    ........

    ........

 

You can see that the DataModelSelection “selectedPast” will be the object selected from the past_assignments List. Nothing complicated about it.

in your xhtml file, making sure you’re using this tag library:

 

 xmlns:s="http://jboss.com/products/seam/taglib"

 

insert this drop down into your web page thusly:

 

 <h:selectOneMenu  value="#{selectedPast}" id="pastSelection">
    <s:selectItems var="past" value="#{past_assignments}" label="#{past.name} #{past.type}" itemValue="#{past.assignmentId}" />
 </h:selectOneMenu>

 

The refrerences past.name and past.type and past.assignmentId are references to the Assignment_Calendar object, which is custom to the application. For completeness, here are the corresponding fields in Assignment_Calendar, which in this case happens to be an @Entity object with some JPA annotations:

@Entity
public class Assignment_Calendar implements Serializable { 

    private static final long serialVersionUID = 7086838039190809182L;

    private Integer assignmentId;
    private Integer type;
    private String name;

    @Id @GeneratedValue @Column(name="Assignment_ID")
    public Integer getAssignmentId() {
        return assignmentId;
    }

    public void setAssignmentId(Integer assignmentId) {
        this.assignmentId = assignmentId;
    }

    @Column(name="Type")
    public Integer getType() {
        return type;
    }
    public void setType(Integer type) {
        this.type = type;
    }

    @Column(name="Name")
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    ......
    ......

 

Drop downs in SEAM is quite easy, once you know the correct process.

 

Six Monitor Workstation!

One of the nice things about working at In Theory Yes is working with teachers and educators on new and novel ways of classroom and online instruction. Like much of the innovation that goes on here in our Stamford campus, the best projects start with “what if..?”, “why don’t we…?”, and the occasionally depressing “it’s nice, but…”

The usual roundtable discussion about how to improve our software offerings and product ideas resulted in the latter – our ITYPhysics product introduced some new windows in our latest version, and when I eagerly asked a teacher how he liked it, I got the pained “it’s nice, but…” – one thing that we hadn’t paid much attention to was the size of student laptops, which tend to be in the lower end, typically 13 inch screen models. Students were spending a lot of time alt-tabbing to find these windows, and this instructor noted that breaks in concentration in a classroom or library would make the student tempted with distractions, such as talking to friends or looking at other homework assignments due on her desk or table.

So “what if….” we made a giant three monitor workstation that immerses the student by giving him or her lots of real estate to spread out our instruction material? Why not four? Five? Six? So with some stealth I liberated some monitors and dipped into our R&D budget and built the closest thing to a workstation for those blessed with compound eyes (joke attributed to Tom Fawcett):

Six Monitor Workstation

The ITY Immersive Instruction Prototype

 
The machine didn’t take too long to build, I used six 24 inch HP Promo LA2405WG LCD monitors, mounted on an Ergotech Hex 3 over 3 monitor stand, which I was impressed with its sturdy build and hefty construction. The HP monitors were used as it’s our standard office monitor, so I had six of those lying around for this build. For a production machine, I would probably go with a smaller screen to be able to angle the monitors more for better viewing by the student. The HP Promo works very well when viewed straight on, but when tilted or viewed from the side, the color suffers somewhat – but in this prototype still quite usable. I would probably choose a TFT monitor of smaller dimensions when procuring for a school.
 
For the video cards, I went with an nVidia Quadro NVS 450 for the first 4 monitors, and at first tried to drive the other two with the on-board Intel video chip on the Intel i3770k processor and using the video outputs on the Asus Sabertooth Z77 motherboard, with Ubuntu 12 as the operating system. The initial results were promising, but apropos to this cartoon, I wasn’t able to get the 6 monitors working in harmony. Seperately the nVidia and Intel processors worked fine, but put together, the Intel would only go into clone mode and not dual-head. I felt if I could just find the right Xorg option it’d work, but as I was spending a lot of time on configuration files, I punted and bought a Quadro NVS 295 card from eBay for $30 and everything just worked! I would describe the Quadros’ performance as “surprisingly adequate” – the cards can support 6 monitors at 1920×1200 resolution at 24 bit color, even playing Youtube videos with acceptable playback. You can definitely see the limitations of the card when moving large windows quickly across the monitors, so you find yourself turning off the cute special effects your window manager provides. The Quadro cards have shockingly high sticker prices brand new ($400 for the 450, $130 for the 295), but seem to have terrible resale values, you can snap them up on ebay for fractions of those prices.
 
Too bad about the Intel, but not all is lost, I plugged in our office HDTV into the motherboard video ports and now we can throw a 42 inch HDTV in the mix! No idea how the student’s going to see the TV over the monitors, unless we’re going to position it on the side or on the ceiling, or even on the floor for the ‘glass bottom boat’ effect, but then again just because someone has options doesn’t mean you’re obligated to use them all.
 
I’ve yet to plop a student with a full spread of our software for a test drive, due to the Summer break, so I’ve been using myself as a lab rat, using this configuration for my daily work. All I can say is “Hooray!” With all my applications open, I can glance at what I need while doing my development or technical support. I have not used alt-tab to switch between windows, and the monitors taking up my view seems to focus me on my task. Even I am not immune to distractions in the office, so my concentration isn’t broken with the act of hunting for applications by switching between them in a small laptop screen. I’ve noticed a definite increase in productivity, and as a bonus, my office mates can’t always see me hiding behind my monitors so they’ll take their request to someone they can see.
 
So from a teacher’s “it’s nice… but” I’ve found a new standard for productivity and while ITY branded multi-monitor setups probably won’t be rolling off the production lines anytime soon, it’s definitely worth considering in today’s trend toward single screen tablets and laptops.
 

Starting a SEAM project on JBoss 7.1 with MySQL

Recently I started a new JBoss project, and decided that as faithful and stalwart JBoss AS 5.1 was, we all have to keep up with the times. I decided to test out JBoss AS 7.1, a stable and recent release. Likewise, I also installed JBoss Developer Studio 5.0 Beta 2 – what better way to maximize uncertainty with two unknown variables?

Installation was smooth and straightforward with both packages, and I used the project wizard to roll up a Seam project and crossed my fingers.

The created Seam project was ‘almost there’, with some minor cleanup involving libraries, but I quickly found out there’s some additional steps to get persistence working under JBoss AS 7.1 and MySql. You will know you need to do this if you encounter this type of error, among others:

JBAS014775:    New missing/unsatisfied dependencies:
      service jboss.naming.context.java.jboss.datasources.ITYPhysicsDatasource (missing) dependents: [service jboss.persistenceunit."ITYPhysics-ear.ear/ITYPhysics-ejb.jar#ITYPhysics"]
 

Assuming you have the MySql server running and an appropriate database and user configured:

1) First download the latest MySQL connector from http://www.mysql.com/downloads/connector/j/ – the latest for me was 5.1.20.

2) Open the archive, and find  mysql-connector-java-5.1.20-bin.jar within.

3) Create the directory $JBOSS_HOME/modules/com/mysql/main

4) Copy mysql-connector-java-5.1.20-bin.jar to $JBOSS_HOME/modules/com/mysql/main

5) In $JBOSS_HOME/modules/com/mysql/main, create the file module.xml:

<?xml version="1.0" encoding="UTF-8"?>

<module xmlns="urn:jboss:module:1.1" name="com.mysql">
    <resources>
        <resource-root path="mysql-connector-java-5.1.20-bin.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
        <module name="javax.transaction.api"/>
        <module name="javax.servlet.api" optional="true"/>
    </dependencies>
</module>

 

6) Edit the file $JBOSS_HOME/standalone/configuration/standalone.xml file. Look for the tag  <subsystem xmlns=”urn:jboss:domain:datasources:1.0″>, and make modifications:

<subsystem xmlns="urn:jboss:domain:datasources:1.0">
   <datasources>
     <datasource jta="true" jndi-name="java:jboss/datasources/ITYPhysicsDatasource" pool-name="ITYPhysicsDatasource" enabled="true" use-java-context="true">
        <connection-url>jdbc:mysql://localhost:3306/ITYPhysics</connection-url>
         <driver>mysql</driver>
           <security>
             <user-name>physicsuser</user-name>
             <password>mypassword123</password>
           </security>
     </datasource>
     <drivers>
        <driver name="mysql" module="com.mysql">
        <xa-datasource-class>mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
     </driver>
   </drivers>
  </datasources>
</subsystem>

 

7) The Seam creation wizard will have generated a persistence.xml file for you in your ejb module under META-INF. Now is a good time to check to see if it is correct:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Persistence deployment descriptor for dev profile -->
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

   <persistence-unit name="ITYPhysics">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:/jboss/datasources/ITYPhysicsDatasource</jta-data-source>
      <!-- The <jar-file> element is necessary if you put the persistence.xml in the WAR and the classes in the JAR -->
      <!--
      <jar-file>../../vehicles.jar</jar-file>
      -->
      <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
         <property name="hibernate.show_sql" value="true"/>
         <property name="hibernate.format_sql" value="true"/>
         <property name="hibernate.default_schema" value="ITYPhysics"/>
         <property name="jboss.entity.manager.factory.jndi.name" value="java:/ITYPhysicsEntityManagerFactory"/>
      </properties>
   </persistence-unit>

</persistence>

 

Credit: This JBoss entry helped me solve my issues with integrating MySQL: https://community.jboss.org/message/628209

The JBoss 7.1 experience is quite different than getting started in 5.1, but should not thwart the efforts of any determined developer.

-Derrick

 

Using ReentrantReadWriteLock for Database Caches

Some things never change, even in your database. There are always tables holding geographical data, historical transactions, and mundane status codes that won’t ever be modified for the lifetime of the application. Typically, the data is read from the table at startup and stored in-memory:

public class States{
 private List<StatesTbl>  stateList = null;
 public States(){
      stateList = database.fetchStatesList();  //Fetch a list of the US States
 }

 public List<StatesTbl> getStates(){          //getter for states list
    return stateList;
 }
}

 

Other tables are either far too big or complex to cache, so a database fetch is done for each lookup.

How about tables in between? Things like product codes, business addresses, and telephone numbers don’t change often, so it’s efficient to store them in memory in application startup. When they do change, you want to refresh the memory cache without restarting the application, yet not have the overhead of checking the database each time to see if it’s been updated.

One way to do this is to take advantage of Java’s ReentrantReadWriteLocks.

static private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();  
static private Lock readlock = readWriteLock.readLock();  
static private Lock writelock = readWriteLock.writeLock();

 

Reentrant locks are similar in concept to the Java “synchronize” locks, but with these useful refinements:

  1. Read locks can occur concurrently, instead of one at a time.
  2. Write locks wait until read locks have been released.

So ReentrantReadWriteLocks fit our needs very nicely: controlling access to an object that is read far more often than it’s written to.

An implementation of our modified data cache:

public List<CompanyProductCodesTbl>  getCodes(){
    readlock.lock();
     try{
       return company_codes_list;
     }finally{
       readlock.unlock();
     }
}
public void refreshCodes(){
   writelock.lock();
   try{
      company_codes_list=database.getCompanyCodes(); 
    }finally{
      writelock.unlock();
    }
}

Wrapping the locks around try/finally blocks ensures that locks will always be released in the event that an exception occurs.

How does “refreshCodes()” get executed? In this case it is a manual process. When the database is refreshed for that particular table, care is needed to execute refreshCodes(). Since updates are rare, there could be an administrative console or a data process that triggers this function at the same event the Product Codes table is updated.

This saves an enormous amount of overhead in making sure your seldom changed data is always current in your running application.

Java: Using A HashMap instead of if/else chains or switch

Here in In Theory Yes, one of the functions we do is to generate complex reports and documents. Not surprisingly, any organization of size is going to need to generate a wide variety of documents, often a lot of them at once. The easiest way is to make templates of form letters, and filling in tags or placeholders, and run them through a report generator.

For example, a form letter template may read:

Dear {alumni_name}, please accept this personal welcome from {current_university_president}!

And run through the generator, comes to your e-mail or post  as:

Dear Joe Smith, please accept this personal welcome from Tom Jones!

For junk mail, a “mail merge” function found in Word or similar species against a simple database would be sufficient. But say you want to do this from your own Java application?

One way you could accomplish this is by having a function with a giant if/else/then chain:

String processTag(String inputTag){
  if("{alumni_tag}".equals(inputTag)){
       //...find and return name of alumni
  }else if("{current_university_president}".equals(inputTag)){
       //..find and return name of president
  }else if......
}

 

Or maybe if you were ahead of the curve and are using Java 7:

 
switch(inputTag){
  case: "{alumni_name}" returnVal="...name of alumni"; break;
  case: "{current_president_name" returnVal="..name of president"; break;
  ...
  ...
  default: returnVal="Error! tag "+inputTag+" not found!";
}
return returnVal;

Both examples suffer from a linear search performance penalty. To find a certain tag, all the previous tags need to be evaluated in the chain before the correct one is found. What if a commonly used tag for a certain document was at the very end of several thousand if/else evaluations? Too bad! Go make your users get some coffee while the job runs!

If you were to say that HashMaps and the like in Java are very good at retrieving random, unsorted tags in far faster than linear time and could be used in fetching functions to process tags, you would be correct:

HashMap<String, ReturnTagValue> tagValues = new  HashMap<String, ReturnTagValue>();

The generic signature ReturnTagValue can be any function that implements a user defined ReturnTagValue interface:

public interface ReturnTagValue{
  public String findVal(ClientObject client);
}
 

 

ReturnTagValue contains a single function, findVal(). It could certainly be embellished with other functions but in this case the single function was sufficient. The ClientObject is an object that contains all the reference data needed to determine the correct value for the tag. In the actual implementation, it was a Hibernate object for a given client.

Building the previously initalized tagValues HashMap:

tagValues.put("{alumni_name}", new ReturnTagValue(){
      @Override
       public String findVal(ClientObject client){
         return client.getAlumniName();
       }
     }
 
tagValues.put("{current_university_president}", new ReturnTagValue(){
      @Override
       public String findVal(ClientObject client){
         return client.getCurrentUniversityPresident();
       }
     }
 

 

If a function implements the ReturnTagValue interface, it can be directly referenced:

tagValues.put("{current_university_president}",new GetCurrentPresident());

 

In our report generator, we put it all together:

return tagValues.get("{current_university_president}").findVal(myClient);

On our document generator, this resulted in a nice speed increase, as well as making it easier to add complex tags by regulating them to their own functions as needed.

A nice bonus with this scheme is the ability to get a list of all currently defined tags for meta-introspective purposes such as listing them for a template builder or for documentation:

List<String> listOfTags = new ArrayList<String>(tagValues.keySet());
Collections.sort(listOfTags); //Nicely alphabetize it
return listOfTags;

 

A nice technique to boost performance in the otherwise mundane task of generating complex and voluminous documents!