Thursday, January 31, 2008

Onward

The rest of the quickstart demo went pretty smoothly. Not surprising, considering that it was largely a matter of typing (or copy and pasting) sample code into real files.

But now the fun begins: problems. The truth is that you don't understand how a technology or combination of technologies works until you've had to fix them when they're supposed to work.

So, first problem: Exception stack trace in my Tomcat log. I am not going to drop the entire stack trace into the blog at this point, but let me describe how I'm handling this. First, I skimmed the stack trace looking for references to code that I know to be mine. In this case there are no "quickstart." files anywhere in the stack trace, so I know it's not my Java code. Now, here are the first few lines of the stack trace:

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.ParserContext.registerBeanComponent(Lorg/springframework/beans/factory/parsing/BeanComponentDefinition;)V
Caused by: java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.ParserContext.registerBeanComponent(Lorg/springframework/beans/factory/parsing/BeanComponentDefinition;)V
at org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser$AopAutoProxyConfigurer.configureAutoProxyCreator(AnnotationDrivenBeanDefinitionParser.java:130)
at org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.parse(AnnotationDrivenBeanDefinitionParser.java:79)

The most obvious problem appears to be an error parsing applicationContext.xml. Probably I have a typo in there somewhere. However, I can't find one after glancing over the file for five minutes and that is almost certainly enough time for now. So I'm going to do something annoying. I typed this one in by hand and probably fat-fingered something. So I'm going to copy-and-paste the file contents from the demo and see if that makes the problem go away. If it doesn't then I'll have to use my brain and nobody wants that.

That doesn't make the problem go away! Crap. Time to do some Google searches on the exception messages.

And Matt Raible comes through again. Dude is rapidly becoming my hero. A thread on the AppFuse mailing list suggests that there might be collisions between various Spring jars. I'm reorganizing my build path, let's see how this works.

...nope. OK, time to review my build path, make sure I have everything I need. Yes, this would have been a good time to have used Maven, I know.

Hey, it turns out that I ignored a bunch of Hibernate jars earlier -- specifically the Hibernate Annotations and Hibernate Entity Manager jars. I'll grab them now. And that doesn't solve the problem either. Same error.

Clean, refresh, delete the deployment, rebuild, redeploy... and it's a whole new suite of errors! But these are more comprehensible. They can go in another post.

Continuing the Struts 2 / Spring / Hibernate Toy

If you are just joining us, I am working my way through this.

I'm working on the provided PersonServiceImpl. Eclipse can't find the @Transactional annotation, which is apparently org.springframework.transaction.annotation.Transactional. Thought I had all the Spring jars -- guess not. http://www.docjar.com/ -- which should be in every developer's bookmark list -- suggests that it is found in spring-dao.jar, which I don't have. It looks like I don't have it because it doesn't exist.

I have a hunch here and it turned out to be right. Rather than relying on Eclipse's Spring enablement guck, I went right to the site to download the latest Spring distribution, and grabbed it. Unpacked it, grabbed spring.jar, dropped it into my project. There, right there, is the org.springframework.transaction.annotation package. I had assumed -- wrongly -- that Eclipse's Spring capability enablement would include spring.jar. Sigh.

Adding jars to the project

It just occurred to me that if you have this feed copied to your livejournal friends list you're going to hate me, because there's no way that I know of to hide crap behind a cut. So, er, sorry. But this is mostly for my future reference, and so being spammy is good from my perspective. And maybe if somebody else needs a hand in the future they might find these posts and get something good out of them.

So, I made a 'quickstart' project in MyEclipse, and added it to the deployments listed under my Tomcat 6 server in the 'servers' tab.

Now it's time to download and add 24 jar files to my project. Although MyEclipse 6 doesn't support Struts 2 yet, I'm going to try and handle the Hibernate jars with MyEclipse's "add Hibernate capabilities" feature, let's see how this goes. I need the Hibernate Core, Hibernate Annotations, and Hibernate Entity Manager features, 16 jars in all.

This guide says I need antlr.jar, asm.jar, asm-attrs.jar, cglib.jar, dom4j.jar, jdbc2_0-stdext.jar, ehcache.jar, hibernate3.jar, xml-apis.jar, and commons-collections.jar from the Hibernate Core. MyEclipse provides all of these in the Hibernate 3.1 Core Libraries module; it also adds commons-logging, jaas, jaxen beta (I have no idea what jaxen is), jta, log4j, and xerces.

The quickstart guide says that Hibernate Annotations should provide ejb3-persistence.jar, jta.jar, and hibernate-annotations.jar. MyEclipse's Hibernate Core gives jta.jar. The other two aren't provided by MyEclipse, so I will need to grab them.

The quickstart guide says that "Hibernate Entity Manager" should provide
hibernate-entitymanager.jar, javassist.jar, and jboss-archive-browsing.jar. MyEclipse's library plugin does not provide any of these, so I'll have to go grab them as well. No problem.

For now I'll add the Hibernate 3.1 Core library to my project, and go out and get the other Hibernate libraries that I need. I'll take the defaults for the Hibernate config file (putting it in src/hibernate.cfg.xml) because I don't have strong opinions about where it belongs.

Hm. After another page of the wizard, the wizard is now asking me about creating Hibernate SessionFactory classes and stuff. I'd better check the quickstart to see if it's got suggestions as to how I should set that junk up.

After reviewing the quickstart, I'm going to hold off on having a wizard-created SessionFactory. I can always create one later if I need it.

OK, that's done. Now I notice the quickstart has references to spring.jar. I could probably just enable the Spring capabilities within MyEclipse. MyEclipse's Spring 2.0 Core libraries includes a lot of stuff that I may not need. If I were working with a team I would probably hold off for the time being, but since it's just me and I'm confident that I know what's in all these jars, I'll add them all wizardishly too.

By default MyEclipse wants Spring's applicationContext.xml in "src". I know I don't want it there; the quickstart wants it in WebRoot/WEB-INF/ and I am inclined to agree, so off it goes. Also, I'm not going to create a Spring LocalSessionFactory, because the quickstart appears to be doing things differently.
Using the instructions mentioned in the previous post I'm going through and creating myself a toy configuration for this app.

I had thought previously that I could just create a new project in MyEclipse and use MyEclipse's "add capabilities" features to get the stuff I want. But MyEclipse doesn't, as of yet, support Struts 2. So I'm going to stick with doing things the old fashioned way.

First step -- grab Tomcat and install it -- as expected, no problems there. Configure MyEclipse to recognize this Tomcat installation by going to Preferences -- MyEclipse -- Servers -- Tomcat -- Tomcat 6, click "Enable," click the "Browse" button next to "Tomcat home directory," and find the root of my Tomcat installation. The other arguments are automagically populated from there, hit Apply, hit OK. Go down to the 'Servers' tab in MyEclipse, select this Tomcat 6 server, click the exciting green start arrow, Tomcat starts up just fine.

Now start up MySQL -- hm, it won't start -- check properties for the MySQL service, find that it points to a directory that doesn't exist.

I should point out that this is an inherited machine that wasn't wiped before it was handed over. Sigh.

So, grab MySQL, install it -- now everything seems OK, except that I have an old, nonfunctional MySQL service in my service list in addition to the new functional one. I'll live.

Make a note in my lab notebook as to the usernames and passwords for Tomcat and MySQL. Yes, it might be slightly insecure but my memory is even less secure.

These quickstart instructions next provide a handy little SQL script to create a 'Person' table. Open up the MySQL Query Browser, open a new script tab, copy, paste, execute -- no dice. Syntax error. Huh. The syntax looks right to me:

CREATE TABLE 'quickstart'.'Person' (
'id' INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
'firstName' VARCHAR(45) NOT NULL,
'lastName' VARCHAR(45) NOT NULL,
PRIMARY KEY('id')
)
ENGINE = InnoDB;

But the MySQL query browser is complaining that I've got a syntax error near the second line. OK, let's check out the online refs, what is the correct syntax for CREATE TABLE in MySQL 5.0? I should mention that MySQL's online reference is just great, at least for this basic stuff. Detailed syntax, lots of examples.

Poke around for ten minutes to look for answers to this, can't find any. Ten minutes seems like a good enough time-boxing, so I'm just going to use the MySQL query browser's GUI to replicate the same code that is here.

Just for reference, here is the autogenerated SQL that the query browser came up with to create this table after I did the happy clicky:

DROP TABLE IF EXISTS `quickstart`.`person`;
CREATE TABLE `quickstart`.`person` (
`id` int(10) unsigned NOT NULL auto_increment,
`firstName` varchar(45) NOT NULL,
`lastName` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Grand.

Wednesday, January 30, 2008

Good intro -- Struts 2 + Hibernate

Another good link, something to look at from work: Struts + Spring + JPA (Hibernate).

Monday, January 28, 2008

Struts 2 Links

Here is a free online PDF intro to Struts 2. No idea how good it is... yet.

A decent description of the post/redirect/get pattern for web app request management.

No, Seriously

I swear. Struts 2. It's just that today was spent fighting fires and -- God help me -- documenting the 'as-is' state of the app.

Tomorrow: Documenting what I'm doing with Struts 2 and Hibernate.

Saturday, January 26, 2008

Exciting News

As you might have guessed, my productivity isn't quite as high with ColdFusion as it is in a Java web app environment. It took me a couple of days this last week to develop an embarrassingly small number of components and unit tests in CF.

I haven't been whining out loud about ColdFusion quite as much as I've been complaining on this blog, but several related activities, combined with some whining, and my lack of productivity, have led us to general agreement that we're going to be remediating this application's problems using Struts 2, Spring, and probably Hibernate. I'm very happy about this, as you might imagine.

I haven't used Struts 2 on a real app yet, although I have used Struts 1. I'll probably be using this blog in part as a Struts 2 linkdump, and partly as a description / walkthrough as I learn. I'm really looking forward to it.

My first link to dump, courtesy of client coworker: how to set up a Struts 2 project in MyEclipse 6.

Friday, January 25, 2008

Continued CF Whining

At this point I'm simply going to have to assume that all CF data is string data, and deliberately coerce it into behaving like numeric data as needed:

I have a query, and the field "storeId" is defined in the DB as being an integer. I have a method, setStoreId(), which has an argument, defined as being an integer. If I do obj.setStoreId(query.storeId), an expression exception is thrown, because query.storeId is not an integer.

Really?

And if I bludgeon that into submission, then later when I do getStoreId(), which is defined as returning an integer, I get another expression exception, because the value it's returning isn't an integer. Even though its value is the number 110.

Thursday, January 24, 2008

ColdFusion needs unit tests freakin' everywhere

By trade I am a Java developer -- I've done plenty of Java EE work as well as pure Java SE stuff. I'm a consultant in Pittsburgh. I won't mention my employer by name, or name any clients that aren't dead, but I've worked at a fair number of clients in the greater Pittsburgh area.

Back in 2000, at a long-dead website, I did about six months of really painful ColdFusion development. In the fall of this year I was offered the opportunity to do more CF consulting, at a local company, and I took the posting.

So, I've been doing CF again for a few months now. I miss so much stuff from Java development that I won't even list it all, although my previous CF post hits some of them.

Here's a new one: Today I lost half a day to the difference between <cfloop query="foo"> and <cfloop query="#foo#">. The error message I received informed me that I was trying to use a complex object where I needed a simple value. I didn't get a stack trace. I didn't get a line number.

And of course I didn't have a debugger. The only way to find out what's going on in ColdFusion is to layer your code with <cfdump> tags and other output-to-the-page wickedness.

So, anyway, what I'm finding is that the more CF development I do, the more I have to write unit tests for even ordinary model objects -- I need to make sure that freakin' getters and setters are behaving intelligently when faced with even slightly unusual circumstances.

Case in point: I have a method, setShippingCharge(), on a Customer CFC. When I designed Customer I knew that shippingCharge was numeric, and so I wrote it like this:

<cffunction name="setShipping" output="false" returntype="void">
 <cfargument name="shippingCharge" type="Numeric">
 <cfset shippingcharge = "arguments.shippingCharge"/>
 <cfreturn/>
</cffunction>

I didn't write any unit tests for this, because, y'know, it's a setter, and I'm a Java guy.

I ran some code using this method today, populating a Customer object from a database query. Well the
shipping_charge field in the database was nullable, and in this case, it was actually null. ColdFusion complained about this, because of a type mismatch. Null -- or, thanks to the retardation of ColdFusion, the empty string "" -- wasn't numeric!

So instead I had to rewrite the function like this:

<cffunction name="setShipping" output="false" returntype="void">
 <cfargument name="shippingCharge">
 <cfif>
  <cfset shippingcharge = "0"/>
  <cfelseif>
   <cfset shippingcharge = arguments.shippingCharge/>
  <cfelse>
   <cfthrow type="foo.IllegalArgumentException" message="this is supposed to be numeric">
 </cfif>
 <cfreturn/>
</cffunction>



I'm not sure I understand, at this point, why ColdFusion functions even allow you to declare their expected data type. If you've got to go through gyrations like the above for every method, what good is the 'type' attribute on <cfargument>?

I was going to turn this into a rant on dynamic languages but I guess I'll save that, except to say that gosh, a compiler could sure prevent a lot of this pain.

Wednesday, January 23, 2008

JVM web app frameworks

Found myself today in a position to discuss criteria for accepting or rejecting web app frameworks to a client with specific needs. This presentation by Matt Raible is incredibly helpful in providing at least a base to talk from.

I cleverly note this so that I have a link to it next time I need it.

Tuesday, January 22, 2008

ColdFusion Pain

A brief note regarding ColdFusion, which I'm currently doing development on (augh). CF is pretty loosely typed. Unit test (using CFUnit, god help me) certainly need to explicitly do testing on the types that methods return, as well as methods' abilities to handle arguments of varying types.

As a side note, I use CFEclipse, which is the ColdFusion plugin for Eclipse. It really isn't very good. Its ability to do tag completion is woeful; ctrl-space code completion isn't very complete -- and often the "timed" code-completion popup occurs at precisely the wrong moment (namely, when I'm pressing 'return' for some other reason).

Worst, in my opinion, is CFEclipse's inability to find things. When doing Java development in Eclipse, if I highlight a variable name, I expect to see every instance of that variable highlighted in the current file. CFEclipse doesn't do that with ColdFusion files.

I'm also addicted, in Java, to using the right-click menu to find declarations of variables or to find references to methods and/or files. Not available in CFEclipse at all.

I accept that some of this stuff is hard to do for a loosely typed "language" like ColdFusion. But this is playing hell with my productivity. I also accept that CFEclipse is an open-source project and that I could contribute to it if I really wanted to. I'm considering it.

Sunday, January 20, 2008

Hi

I'm trying to capture thoughts about technology and software development as they happen.

Lately I'm playing with the Google Web Toolkit so I want to discuss my progress with that.