Rainy days and Java always get me down


I've Moved My Blog

It's currently located at http://www.urlinone.com/blog

I should say "I'm moving my blog." It's a pretty painful process.

Pebble has blown up on me, and it's been many months since I've been able to blog reliably. I've lost posts. And now I've got to figure out how to migrate my past blog posts from Pebble to my new destination without all the URLs changing, lest external links become 404 Not Founds.

Why does everything in the 21st century have to be a three-day project???

Groovy is one of the hot new things in the Java world. It's a powerful scripting language that integrates both ways, i.e. you can call Groovy scripts from within your Java code, and you can access your Java objects from within your Groovy scripts. In fact, Groovy classes become plain old Java bytecode, so there is ultimately no real distinction between the two. In the Groovy language, you have many powerful constructs including closures.

Here are the steps I took to add a little groovability to my AppFuse 1.5 app. I realize that AppFuse 1.6 is out, but I'm in the midst of a 1.5 development, and I suspect that these steps won't change much, if at all, between 1.5 and 1.6.

  1. Download Groovy from here. Treat yourself to the freshest version available.
  2. Unzip it. FWIW, I unzip all downloaded software to c:\sw.
  3. Create a directory in your AppFuse app for the Groovy distribution. I created c:\projects\optioninsight\lib\groovy-1.0-beta-7.
  4. Copy groovy-1.0-beta-7.jar to your new directory.
  5. Create a lib directory below the new groovy directory.
  6. Copy asm-1.4.3.jar and asm-util-1.4.3.jar from C:\sw\groovy-1.0-beta-7\lib to your lib\groovy-1.0-beta-7\lib directory.
  7. In the top-level lib directory off the root of your appfuse application (in my case, that's c:\projects\optioninsight\lib), edit lib.properties.
  8. At the bottom of lib.properties (or anywhere else you like within the file) add the following
    #
    # Groovy - http://groovy.codehaus.org/
    #
    groovy.version       = 1.0-beta-7
    groovy.dir=${lib.dir}/groovy-${groovy.version}
    groovy.jar=${groovy.dir}/groovy.jar
    
  9. Edit build.xml in the root of your AppFuse application (c:\projects\optioninsight, in my case).
  10. Locate the line </war> in build.xml. Just before that line add the following:
                <lib dir="${groovy.dir}" includes="*.jar"/>
                <lib dir="${groovy.dir}/lib">
                    <include name="asm*.jar"/>
                </lib>
    
    This will add the jars required for Groovy to your war file during deployment.
  11. Now edit properties.xml, in the same directory as build.xml.
  12. Locate web.compile.classpath within that file. Between the <path> and </path> statements add the following:
        <fileset dir="${groovy.dir}" includes="*.jar"/>
    
    This adds the Groovy jar to the classpath for compiling the web module. I haven't done this, but I would imagine you have to add the same line to the other classpaths, if you want to compile Groovy into the dao or service layers. In fact, the way I read properties.xml, anything included into dao.compile.classpath will be included in service.compile.classpath, so you could include this line in dao.compile.classpath and have Groovy available in all three AppFuse layers.
  13. At this point, you should be able to access Groovy within your web layer. As a test, I added a bit of Groovy code to StartupListener.java to make sure it was available. Within the contextInitialized() method, I placed the following:
            // Groovy Shell - call groovy expressions from Java code
            Binding binding = new Binding();
            binding.setVariable("foo", new Integer(3));
            GroovyShell shell = new GroovyShell(binding);
    
            Object value = null;
    		try {
    			value = shell.evaluate("println 'Hello World!'; x = 123; return foo * 10");
    		} catch (CompilationFailedException e1) {
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		} catch (IOException e1) {
    			// TODO Auto-generated catch block
    			e1.printStackTrace();
    		}
    		if (log.isDebugEnabled()) {
    			log.debug("Groovy return value: " + value);
    			log.debug("x: " + binding.getVariable("x"));
    		}
            
            // GroovyClassLoader - load an external Groovy script file
            XmlWebApplicationContext ctx =
                (XmlWebApplicationContext) context
                    .getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            if (ctx == null) {
                // if the context is null, it's likely because the listeners
                // aren't initialized in the same order they're specified in
                // web.xml.  This happens in Tomcat 5.
                ctx = new XmlWebApplicationContext();
                // get the config locations from context parameters
                String configLocations =
                        context.getInitParameter(ContextLoader.CONFIG_LOCATION_PARAM);
                String[] files = configLocations.split(",");
                for (int i=0; i < files.length; i++) {
                    files[i] = files[i].trim();
                }
                ctx.setConfigLocations(files);
                ctx.setServletContext(context);
                ctx.refresh();
            }
            
            LookupManager mgr =
                (LookupManager) ctx.getBean("lookupManager");
            ClassLoader parent = mgr.getClass().getClassLoader();
            
            GroovyClassLoader loader = new GroovyClassLoader(parent);
            Class groovyClass = null;
    		try {
    			log.debug("Loader: " + loader);
    			groovyClass = loader.parseClass(new File("/sw/tomcat-5.0.19/webapps/optioninsight/scripts/HelloWorld.groovy"));
    		} catch (CompilationFailedException e2) {
    			// TODO Auto-generated catch block
    			e2.printStackTrace();
    		} catch (IOException e2) {
    			// TODO Auto-generated catch block
    			e2.printStackTrace();
    		}
    		//         lets call some method on an instance
            GroovyObject groovyObject = null;
    		try {
    			groovyObject = (GroovyObject) groovyClass.newInstance();
    		} catch (InstantiationException e3) {
    			// TODO Auto-generated catch block
    			e3.printStackTrace();
    		} catch (IllegalAccessException e3) {
    			// TODO Auto-generated catch block
    			e3.printStackTrace();
    		}
    		Object[] args = {};
            groovyObject.invokeMethod("run", args);
    
    Now, there's quite a bit of ugliness there. Almost of this is related to the fact that I had to find a class to get a ClassLoader from. I ended up copying a bunch of code from setupContext() at the bottom of StartupListener.java. In retrospect, I probably could have found a better place to run my Groovy test, but availability at startup was what I was after.

    One of the trickiest parts of this whole process was figuring out where the classloader was looking for the script file. As far as I can tell, the path is relative to the root of the hard drive, which is not what I expected. I would have thought that it would be based on the root of the web app or Tomcat, but neither seemed to be the case. Keep in mind that it all depends on what classloader you get, and that is dependent upon what class you get it from. I'm still working on understanding this bit of arcane magic.

  14. The contents of HelloWorld.groovy can be whatever you want, of course, but this is what I had to get you started:
    println "Nice cheese, Gromit!"
    println "----------------------- scripts directory ---------------------------"
    
    I found it helpful, when I was hunting for my script file, to put it in numerous locations with some identifying text, so I knew which one was running.

This entry doesn't actually do anything useful, but it should make Groovy available within your AppFuse environment, so you can start to take advantage of it. Groovy seems like a very powerful tool that can greatly enhance the power of AppFuse. If nothing else, it's a way to make changes without having to repeatedly recompile, redeploy, and restart Tomcat. Once you have the logic down, you can pull the Groovy code into Java, although I'm not sure that's even necessary. It would be nice to hear some Groovy best practices from someone with real world experience.

Yeah baby, yeah!

I'm surprised Bill Maher allowed them to air this week's installment of HBO's Real Time with Bill Maher. Former CIA Director James Woolsey made Bill and his two anti-Bush guests look like the emotional, uninformed, knee-jerk liberals they are.

Mr. Woolsey was quite frank in his criticism of President Bush's administration where fitting, but he also, with his Bob Newhart-esque demeanor, shoved all their rhetoric, propaganda, and hype back down their throats. He made it clear that it was not a mistake, a lie, or inappropriate to link Saddam Hussein with Al Qaeda.

It was a pleasure to watch them squirm as he shot down the mantra they've been chanting for months. Too bad none of it sunk into their thick skulls. They'll continue to believe what they want to, because they aren't interested in the truth. Never let facts get in the way of a good smear campaign, right, Bill?

And just to really drive home the point that Bill Maher is the most irrational, hyperactive whiner on cable television, Dr. Bernadine Healy of the National Red Cross [apologies for not getting her title] made Bill look silly and childish with his nonsensical arguments and inaccurate views on vaccinations and medicine. It was like watching a teacher gently correct the rantings of a tantruming student.

Bill can be tough to take for any period of time. He is so righteous, so judgemental, so intolerant, and so wrong that it's hard to watch. This episode was much better, though.

Great show, Bill! You keep this up, and I'll be a regular viewer.

Maybe you should consider renaming the show to "'Yeah, But...' with Bill Maher."