(with apologies to Monty Python)
A customer enters a software shop.
Mr. Praline: 'Ello, I wish to register a complaint.
The owner does not respond.
Mr. Praline: 'Ello, Miss?
Owner: What do you mean "miss"?
Mr. Praline: I'm sorry, I have a cold. I wish to make a complaint!
Owner: We're closin' for lunch.
Mr. Praline: Never mind that, my lad. I wish to complain about this programming language what I purchased not half an hour ago from this very boutique.
Owner: Oh yes, the, uh, the Danish C++...What's,uh...What's wrong with it?
Mr. Praline: I'll tell you what's wrong with it, my lad. 'E's crap, that's what's wrong with it!
Owner: No, no, 'e's uh,...it's resting.
Mr. Praline: Look, matey, I know a crap language when I see one, and I'm looking at one right now.
Owner: No no it's not crap, it's, it's restin'! Remarkable language, the Danish C++, idn'it, ay? Beautiful syntax!
Mr. Praline: The syntax don't enter into it. It's stone crap.
Owner: Nononono, no, no! 'E's resting!
Mr. Praline: All right then, if it's restin', I'll wake him up! (shouting at the cage) 'Ello, Mister Plymorphic Language! I've got a lovely fresh computer for you if you show...
owner hits the cage
Owner: There, he moved!
Mr. Praline: No, he didn't, that was you hitting the cage!
Owner: I never!!
Mr. Praline: Yes, you did!
Owner: I never, never did anything...
Mr. Praline: (yelling and hitting the cage repeatedly) 'ELLO POLYMORPH!!!!! Testing! Testing! Testing! Testing! This is your nine o'clock alarm call!
Takes language out of the cage and thumps its head on the counter. Throws it up in the air and watches it plummet to the floor.
Mr. Praline: Now that's what I call a crap language.
Owner: No, no.....No, 'e's stunned!
Mr. Praline: STUNNED?!?
Owner: Yeah! You stunned him, just as he was wakin' up! Danish C++'s stun easily, major.
Mr. Praline: Um...now look...now look, mate, I've definitely 'ad enough of this. That language is definitely deceased, and when I purchased it not 'alf an hour ago, you assured me that its total lack of movement was due to it bein' tired and shagged out following a prolonged standards meeting.
Owner: Well, it's...it's, ah...probably pining for a common runtime.
Mr. Praline: PININ' for a COMMON RUNTIME?!?!?!? What kind of talk is that?, look, why did he fall flat on his back the moment I got 'im home?
Owner: The Danish C++ prefers keepin' on it's back! Remarkable language, id'nit, squire? Lovely syntax!
Mr. Praline: Look, I took the liberty of examining that language when I got it home, and I discovered the only reason that it had been sitting on its perch in the first place was that it had been NAILED there.
pause
Owner: Well, o'course it was nailed there! If I hadn't nailed that language down, it would have nuzzled up to those computers, broke 'em apart with its beak, and VOOM! Feeweeweewee!
Mr. Praline: "VOOM"?!? Mate, this language wouldn't "voom" if you put four million volts through it! 'E's bleedin' demised!
Owner: No no! 'E's pining!
Mr. Praline: 'E's not pinin'! 'E's passed on! This language is no more! He has ceased to be! 'E's expired and gone to meet 'is maker! 'E's a stiff! Bereft of life, 'e rests in peace! If you hadn't nailed 'im to the perch 'e'd be pushing up the daisies! 'Is processes are now 'istory! 'E's off the map! 'E's kicked the bucket, 'e's shuffled off 'is mortal coil, run down the curtain and joined the bleedin' choir invisibile!! THIS IS AN EX-LANGUAGE!!
pause
Owner: Well, I'd better replace it, then. (he takes a quick peek behind the counter) Sorry squire, I've had a look 'round the back of the shop, and uh, we're right out of languages.
Mr. Praline: I see. I see, I get the picture.
Owner: I got Java.
pause
Mr. Praline: Pray, does it support pointers?
Owner: Nnnnot really.
Mr. Praline: WELL IT'S HARDLY A BLOODY REPLACEMENT, IS IT?!!???!!?
Owner: N-no, I guess not. (gets ashamed, looks at his feet)
Mr. Praline: Well.
pause
Owner: (quietly) D'you.... d'you want to come back to my place an' dereference some pointers?
Mr. Praline: (looks around) Yeah, all right, sure.
Tuesday, March 20, 2007
Thursday, March 15, 2007
The ever-leaky abstractions of Microsoft
I was using Windows XP inside Parallels today and got a message from Windows that I had never seen before:

How on earth could you design an operating system that could detect there wasn't enough virtual memory, expand the virtual memory pool automatically, but would deny applications requests for virtual memory at the same time?

How on earth could you design an operating system that could detect there wasn't enough virtual memory, expand the virtual memory pool automatically, but would deny applications requests for virtual memory at the same time?
Thursday, March 01, 2007
C++ Unit testing frameworks: do they all suck?
Well, I've been doing some work with C++ lately and I quickly ran into the need for a unit testing framework. I found an excellent article reviewing the state of C++ unit testing frameworks. My first surprise was how many of them there are. This article alone reviewed six of them. Even if those are all of the 'serious contenders', that's about 3 or 4 frameworks too many.
I was also surprised at how primitive the C++ unit testing frameworks feel. I realized after a while that the lack of dynamic/reflective behavior in C++ is part (or even all) of the issue. Since reflection is not built in, many of the grungy tasks that unit testing frameworks in Java automate for us can't be automated in a C++ unit testing framework. What a shame. We're left using frameworks that feel awkard and leave a lot to be desired.
Then I got to thinking: a number of the C++ wizards around here talk about the power of C++ template programming. It seems to me it ought to be possible to clean up and automate what would normally be done in Java with reflection using templates in C++. I wonder why this hasn't been done. I don't know templates well enough to do it myself, but I hope some wiz at C++ templates does this sometime soon.
For now, I'll keep trudging along with CppUnit, and maybe try out CxxTest.
I was also surprised at how primitive the C++ unit testing frameworks feel. I realized after a while that the lack of dynamic/reflective behavior in C++ is part (or even all) of the issue. Since reflection is not built in, many of the grungy tasks that unit testing frameworks in Java automate for us can't be automated in a C++ unit testing framework. What a shame. We're left using frameworks that feel awkard and leave a lot to be desired.
Then I got to thinking: a number of the C++ wizards around here talk about the power of C++ template programming. It seems to me it ought to be possible to clean up and automate what would normally be done in Java with reflection using templates in C++. I wonder why this hasn't been done. I don't know templates well enough to do it myself, but I hope some wiz at C++ templates does this sometime soon.
For now, I'll keep trudging along with CppUnit, and maybe try out CxxTest.
Thursday, February 01, 2007
Adding a JNI Library template to Xcode
I've been doing some work with Xcode, Apple's IDE, recently. I wanted to use it for doing Java JNI work, since I'm doing some of that, but found the normal tutorial assumes you are building an entire application with Xcode. Well, Xcode pales in comparison to IDEA for working with Java.
So I wondered if I could use IDEA for working with the Java code and Xcode for managing the JNI bindings and the C++ code that goes with it. After a bit of playing around (primarily pulling bits and pieces from Apple's mini-tutorial on JNI with Xcode), I figured out how to build a JNI library that I can use easily from IDEA.
That got me to thinking about the four or five steps I would have to do each time I created a new one of these projects (which I expect to be doing for a while). I vaguely remembered a friend talking about customizing Xcode by creating new templates. So I set out to create one for building JNI libraries.
I googled around a bit and found out that Xcode keeps its templates in:
Looking there, I see a folder called "Project Templates", and sure enough inside are all the templates for Xcode projects.

There are several that look promising, but what's the difference between a "BSD Dynamic Library", a "C++ Dynamic Library", and a "C++ Standard Dynamic Library"? Something based on the "C++ Standard Dynamic Library" seems right.
So, I create a test project and make a few changes to produce a JNI library:
Now, save the project, make sure it builds (which it does) and create a simple Java HelloWorld with a native method that calls our Test class.
Having verified that this sort of project can be used to produce a JNI Library, it's time to create the custom project template. First I copy the "C++ Standard Dynamic Library template" to "JNI C++ Standard Dynamic Library". Now I need to find the right places to insert the additional items that were changed (and how to remove the Carbon framework). Looking at the template directory, I see a number of files.
But most of these are just boilerplate for source files in the project ('.pch', '.h', '.cp'). I think I can safely ignore these files at least for now (though I made note of them; if there prove to be certain common files in my JNI libraries moving forward, I can come back and try to tweak the project template to have different boilerplates).
The key 'file' appears to be CppShared.xcodeproj. Right-clicking on it shows it is really a 'package' and I take a look inside.

When I do so, I see two files: "TemplateInfo.plist" and "project.pbxproj". The '.plist' file has some interesting items about template files and lists of files upon which to perform macro expansion. Interestingly, the list of files matches the list of boilerplate files almost exactly. For now, I'm leaving these alone (again making note in case I want to change the set of boilerplate files in the project). There is also a Description item, which shows up in the "New Project..." window in Xcode. After tweaking that to make it better describe this new kind of project I'm creating, I move on to the "project.pbxproj" file.
The "project.pbxproj" file is semi-text and semi-binary. So it's time to be careful and make a backup before experimenting. The first section of interest is this:
I ditch the Carbon framework reference (since I'm doing JNI) by deleting the line that references Carbon. If I want to use Carbon in a JNI library, I can always add it later. It turns out there are a bunch of other references to Carbon in the template, and I have to remove those as well.
Now, I see the line that makes reference to "lib«PROJECTNAME».dylib". I want the library to end with '.jnilib' since that is required by Mac OS X. So I patch this:
Now, I want the header search paths to include the location of the Java headers, and the executable extension to be '.jnilib'. Searching the test project's '.pbxproj' file, I find them in the Debug and Release build sections. However, there are two pairs of these, one pair for the library target and one pair for the overall project. I need to change the ones for the overall project:
I was encouraged when creating a new project didn't fail. Looking at the default setup, everything seems to be in order (no Carbon framework, all the boilerplate files are there, etc.). A few minutes to add in the JNI binding function and change the Java program to use the new JNI library and I have a successful test.
I'm sure there are many other customizations I could make if I only knew about them. I also wish there were a better tool than a text editor for editing these files (hopefully there is and someone will point it out to me).
So I wondered if I could use IDEA for working with the Java code and Xcode for managing the JNI bindings and the C++ code that goes with it. After a bit of playing around (primarily pulling bits and pieces from Apple's mini-tutorial on JNI with Xcode), I figured out how to build a JNI library that I can use easily from IDEA.
That got me to thinking about the four or five steps I would have to do each time I created a new one of these projects (which I expect to be doing for a while). I vaguely remembered a friend talking about customizing Xcode by creating new templates. So I set out to create one for building JNI libraries.
I googled around a bit and found out that Xcode keeps its templates in:
/Library/Application Support/Apple/Developer Tools
Looking there, I see a folder called "Project Templates", and sure enough inside are all the templates for Xcode projects.

There are several that look promising, but what's the difference between a "BSD Dynamic Library", a "C++ Dynamic Library", and a "C++ Standard Dynamic Library"? Something based on the "C++ Standard Dynamic Library" seems right.
So, I create a test project and make a few changes to produce a JNI library:
- Change "Executable Extension" to be "jnilib"
- Change "Header Search Paths" to include "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers"
- Delete the Carbon framework (why was it there in the first place?)
Now, save the project, make sure it builds (which it does) and create a simple Java HelloWorld with a native method that calls our Test class.
Having verified that this sort of project can be used to produce a JNI Library, it's time to create the custom project template. First I copy the "C++ Standard Dynamic Library template" to "JNI C++ Standard Dynamic Library". Now I need to find the right places to insert the additional items that were changed (and how to remove the Carbon framework). Looking at the template directory, I see a number of files.
But most of these are just boilerplate for source files in the project ('.pch', '.h', '.cp'). I think I can safely ignore these files at least for now (though I made note of them; if there prove to be certain common files in my JNI libraries moving forward, I can come back and try to tweak the project template to have different boilerplates).The key 'file' appears to be CppShared.xcodeproj. Right-clicking on it shows it is really a 'package' and I take a look inside.

When I do so, I see two files: "TemplateInfo.plist" and "project.pbxproj". The '.plist' file has some interesting items about template files and lists of files upon which to perform macro expansion. Interestingly, the list of files matches the list of boilerplate files almost exactly. For now, I'm leaving these alone (again making note in case I want to change the set of boilerplate files in the project). There is also a Description item, which shows up in the "New Project..." window in Xcode. After tweaking that to make it better describe this new kind of project I'm creating, I move on to the "project.pbxproj" file.
The "project.pbxproj" file is semi-text and semi-binary. So it's time to be careful and make a backup before experimenting. The first section of interest is this:
/* Begin PBXFileReference section */
08FB77AAFE841565C02AAC07 /* Carbon.framework */ = {isa = ...
32BAE0B70371A74B00C91783 /* «PROJECTNAME»_Prefix.pch */ = {isa = ...
50149BD909E781A5002DEE6A /* «PROJECTNAME».h */ = {isa = ...
5073E0C409E734A800EC74B6 /* «PROJECTNAME».cp */ = {isa = ...
5073E0C609E734A800EC74B6 /* «PROJECTNAME»Proj.xcconfig */ = {isa = ...
5073E0C709E734A800EC74B6 /* «PROJECTNAME»Target.xcconfig */ = {isa = ...
50B2938909F016FC00694E55 /* «PROJECTNAME»Priv.h */ = {isa = ...
D2AAC09D05546B4700DB518D /* lib«PROJECTNAME».dylib */ = {isa =
PBXFileReference; explicitFileType = "compiled.mach-o.dylib";
includeInIndex = 0; path = "lib«PROJECTNAME».dylib"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
I ditch the Carbon framework reference (since I'm doing JNI) by deleting the line that references Carbon. If I want to use Carbon in a JNI library, I can always add it later. It turns out there are a bunch of other references to Carbon in the template, and I have to remove those as well.
Now, I see the line that makes reference to "lib«PROJECTNAME».dylib". I want the library to end with '.jnilib' since that is required by Mac OS X. So I patch this:
D2AAC09D05546B4700DB518D /* lib«PROJECTNAME».jnilib */ = {isa =
PBXFileReference; explicitFileType = "compiled.mach-o.dylib";
includeInIndex = 0; path = "lib«PROJECTNAME».jnilib"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
Now, I want the header search paths to include the location of the Java headers, and the executable extension to be '.jnilib'. Searching the test project's '.pbxproj' file, I find them in the Debug and Release build sections. However, there are two pairs of these, one pair for the library target and one pair for the overall project. I need to change the ones for the overall project:
1DEB916508733D950010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5073E0C609E734A800EC74B6 /* «PROJECTNAME»Proj.xcconfig */;
buildSettings = {
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
EXECUTABLE_EXTENSION = jnilib;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_OPTIMIZATION_LEVEL = 0;
HEADER_SEARCH_PATHS = "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers";
ZERO_LINK = YES;
};
name = Debug;
};
1DEB916608733D950010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 5073E0C609E734A800EC74B6 /* «PROJECTNAME»Proj.xcconfig */;
buildSettings = {
ARCHS = (
ppc,
i386,
);
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
EXECUTABLE_EXTENSION = jnilib;
HEADER_SEARCH_PATHS = "$(SDK)/System/Library/Frameworks/JavaVM.framework/Headers";
PRESERVE_DEAD_CODE_INITS_AND_TERMS = YES;
SEPARATE_STRIP = YES;
};
name = Release;
};
I was encouraged when creating a new project didn't fail. Looking at the default setup, everything seems to be in order (no Carbon framework, all the boilerplate files are there, etc.). A few minutes to add in the JNI binding function and change the Java program to use the new JNI library and I have a successful test.
I'm sure there are many other customizations I could make if I only knew about them. I also wish there were a better tool than a text editor for editing these files (hopefully there is and someone will point it out to me).
Sunday, January 21, 2007
Learning Cocoa
If you have any interest in the Cocoa frameworks for Mac OS X, you might be interested a blog entry about learning how to program Cocoa. It's on my other blog, Walking the Fringe, because it's more exploratory than the material I like to put on this blog.
Friday, January 19, 2007
Configuring logging in your Spring configuration file
I detest multiple configuration files. Generally, I can't avoid having them, but whenever I can find a way to reduce the number of configuration files, the better.
So, when I need to tailor the logging levels in my application, I have a choice: edit the default logging file (possibly effecting other applications if that copy of the JDK is shared as is typical), or create a separate logging configuration file and point logging to it (in the case of the JDK's logging, this means setting the java.util.logging.config.file property).
When using Spring, I'd really like to put my custom logging levels in my Spring configuration file, so I don't have yet another file that has to travel with my application. There doesn't appear to be any way built into Spring to do this, but by adding a tiny Java class to the application (one that is reusable for each application), we gain the ability to do this.
The idea is to create a bean at the top of your Spring configuration file that will set your custom logging levels. For example:
The LoggingConfigurer class is trivial:
Now, when Spring is initialized, very early on in the process, it encounters the entry for our loggingConfigurer bean. Since the bean has lazy initialization turned off, it is immediately created and configured. That sets our logging levels to what we want.
Admittedly, this isn't perfect. For example, these few lines of logging show up when launching a Spring and Hibernate application (prior to our bean turning the logging level to WARNING):
But this is a great improvement over the huge amounts of output from Spring and (especially) Hibernate that show up otherwise.
I'm sure a similar approach can be used with log4j. If anyone adapts this to log4j I'd love to know how that works.
So, when I need to tailor the logging levels in my application, I have a choice: edit the default logging file (possibly effecting other applications if that copy of the JDK is shared as is typical), or create a separate logging configuration file and point logging to it (in the case of the JDK's logging, this means setting the java.util.logging.config.file property).
When using Spring, I'd really like to put my custom logging levels in my Spring configuration file, so I don't have yet another file that has to travel with my application. There doesn't appear to be any way built into Spring to do this, but by adding a tiny Java class to the application (one that is reusable for each application), we gain the ability to do this.
The idea is to create a bean at the top of your Spring configuration file that will set your custom logging levels. For example:
<bean id="loggingConfigurer"
class="com.acme.logging.LoggingConfigurer"
lazy-init="false">
<constructor-arg>
<props>
<prop key="org.springframework">WARNING</prop>
<prop key="org.hibernate">WARNING</prop>
</props>
</constructor-arg>
</bean>
The LoggingConfigurer class is trivial:
package com.acme.logging;
import java.util.Iterator;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Configures logging by being constructed.
* Especially useful when created as a bean from a Spring configuration file.
*/
public class LoggingConfigurer {
public LoggingConfigurer(Properties props) {
Iterator i = props.keySet().iterator();
while (i.hasNext()) {
String loggerName = (String) i.next();
String levelName = props.getProperty(loggerName);
try {
Level level = Level.parse(levelName);
Logger l = Logger.getLogger(loggerName);
l.setLevel(level);
}
catch ( IllegalArgumentException e ) {
System.err.println( "WARNING: Unable to parse '"
+ levelName + "' as a java.util.Level for logger "
+ loggerName + "; ignoring..." );
}
}
}
}
Now, when Spring is initialized, very early on in the process, it encounters the entry for our loggingConfigurer bean. Since the bean has lazy initialization turned off, it is immediately created and configured. That sets our logging levels to what we want.
Admittedly, this isn't perfect. For example, these few lines of logging show up when launching a Spring and Hibernate application (prior to our bean turning the logging level to WARNING):
Jan 19, 2007 3:44:41 PM org.springframework.core.CollectionFactory
INFO: JDK 1.4+ collections available
Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [baseContext.xml]
Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [unitTestingContext.xml]
Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractRefreshableApplicationContext refreshBeanFactory
INFO: Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=2490106]: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [loggingConfigurer,hibernateStatsService,mbeanServer,exporter,persistence,setPersistenceManager,customEditorConfigurer,mySessionFactory]; root of BeanFactory hierarchy
Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext refresh
INFO: 8 beans defined in application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=2490106]
Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext initMessageSource
INFO: Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@a9a32c]
Jan 19, 2007 3:44:41 PM org.springframework.context.support.AbstractApplicationContext initApplicationEventMulticaster
INFO: Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@2153fe]
Jan 19, 2007 3:44:41 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in factory [org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans [loggingConfigurer,hibernateStatsService,mbeanServer,exporter,persistence,setPersistenceManager,customEditorConfigurer,mySessionFactory]; root of BeanFactory hierarchy]
But this is a great improvement over the huge amounts of output from Spring and (especially) Hibernate that show up otherwise.
I'm sure a similar approach can be used with log4j. If anyone adapts this to log4j I'd love to know how that works.
Wednesday, January 17, 2007
Simplified Database Management in Spring-based Hibernate Unit Tests
I've been working with Hibernate recently, and needed a way to implement good unit tests. The goal of these unit tests is to make sure the Java code works in conjunction with the Hibernate mapping files. I found an excellent article on using HSQLDB to do unit testing of Hibernate classes on The Server Side.
Unfortunately, one of the drawbacks of the approach described is that it requires you to write some custom JDBC code to clear out your tables after each unit test. From the article:
Another possible option for handling this scenario if you are using Hibernate 3 is to use it's bulk update feature. I haven't used this feature yet, but apparently one wrinkle is that bulk deletes don't seem to cause cascade deletes. This requires that you remember to update your code each time you add a new table to those used by your application, or end up with problems in your unit tests due to data left over from the last test method.
But in my opinion there's a better way: leverage the SchemaExport utility of Hibernate to automaticallly wipe out and recreate all your tables for you. The basic idea is that the setUp method of your unit test calls the SchemaExport utility to wipe out the existing tables and recreate them. This leaves you with the proper tables and a clean slate upon which to create exactly the objects needed to run your test. This simplifies your unit testing and avoids having to maintain both the mapping files and the bulk delete code in tandem.
To accomplish this, you need to make sure your Hibernate mapping files specify enough information for Hibernate's SchemaExport utility to generate and run database-specific DDL to recreate your tables, indexes, foreign keys, etc. For example, you might have a mapping file for a SimplePerson class that looks like this:
You need to make sure you have information about the SQL types of your mapped columns so Hibernate knows how to generate the tables, like this:
The sql-type attributes are what tell Hibernate how to generate DDL for your table. Once you have this, you can invoke the SchemaExport utility on your in-memory HSQLDB database and it will wipe out the old tables and their contents and recreate them empty.
Now our only catch is invoking SchemaExport when using the Spring framework. If you're using Spring outside of any other container, you might use a LocalSessionFactoryBean to do your Hibernate mapping work, like this:
This is great, except for one thing: you need access to the Hibernate Configuration object in order to invoke SchemaExport and this is not easily available (from what I can find). There is nice workaround for this problem, though. If you use the ampersand ('&') character in front of the name of a bean, you can get at the actual Spring object instead of the bean it creates. For example, the LocalSessionFactoryBean has a public getConfiguration method will get us the Hibernate Configuration object we want. So we can implement a setUp method that contains code like this:
Now, every test method in your unit test will have a clean database with all the right tables, indexes, etc.
One side benefit of this is that there's a good chance you will make the tests run faster than when connecting to an external database. But, most importantly, by avoiding dependencies on an external database you improve the reliability of your Hibernate mapping unit tests.
Unfortunately, one of the drawbacks of the approach described is that it requires you to write some custom JDBC code to clear out your tables after each unit test. From the article:
statement.executeUpdate("delete from Batting");
statement.executeUpdate("delete from Fielding");
statement.executeUpdate("delete from Pitching");
statement.executeUpdate("delete from Player");
connection.commit();
Another possible option for handling this scenario if you are using Hibernate 3 is to use it's bulk update feature. I haven't used this feature yet, but apparently one wrinkle is that bulk deletes don't seem to cause cascade deletes. This requires that you remember to update your code each time you add a new table to those used by your application, or end up with problems in your unit tests due to data left over from the last test method.
But in my opinion there's a better way: leverage the SchemaExport utility of Hibernate to automaticallly wipe out and recreate all your tables for you. The basic idea is that the setUp method of your unit test calls the SchemaExport utility to wipe out the existing tables and recreate them. This leaves you with the proper tables and a clean slate upon which to create exactly the objects needed to run your test. This simplifies your unit testing and avoids having to maintain both the mapping files and the bulk delete code in tandem.
To accomplish this, you need to make sure your Hibernate mapping files specify enough information for Hibernate's SchemaExport utility to generate and run database-specific DDL to recreate your tables, indexes, foreign keys, etc. For example, you might have a mapping file for a SimplePerson class that looks like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.acme.project">
<class name="SimplePerson" table="PERSONS">
<id name="id" type="int">
<column name="CODE" not-null="true"/>
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" not-null="true"/>
<property name="lastName" column="LAST_NAME" not-null="true"/>
<property name="contactEmail" column="CONTACT_EMAIL_ADDRESS"/>
<property name="disabled" type="boolean">
<column name="DISABLE" default="0" not-null="true"/>
</property>
</class>
</hibernate-mapping>
You need to make sure you have information about the SQL types of your mapped columns so Hibernate knows how to generate the tables, like this:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD/EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.acme.project">
<class name="SimplePerson" table="PERSONS">
<id name="id" type="int">
<column name="CODE" sql-type="integer" not-null="true"/>
<generator class="native"/>
</id>
<property name="firstName" column="FIRST_NAME" not-null="true"/>
<property name="lastName" column="LAST_NAME" not-null="true"/>
<property name="contactEmail" column="CONTACT_EMAIL_ADDRESS"/>
<property name="disabled" type="boolean">
<column name="DISABLE" sql-type="int" default="0" not-null="true"/>
</property>
</class>
</hibernate-mapping>
The sql-type attributes are what tell Hibernate how to generate DDL for your table. Once you have this, you can invoke the SchemaExport utility on your in-memory HSQLDB database and it will wipe out the old tables and their contents and recreate them empty.
Now our only catch is invoking SchemaExport when using the Spring framework. If you're using Spring outside of any other container, you might use a LocalSessionFactoryBean to do your Hibernate mapping work, like this:
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>com/acme/project/SimplePerson.hbm.xml</value>
<!-- ... -->
</list>
</property>
<property name="configLocation" value="hibernateConfig.xml"/>
</bean>
This is great, except for one thing: you need access to the Hibernate Configuration object in order to invoke SchemaExport and this is not easily available (from what I can find). There is nice workaround for this problem, though. If you use the ampersand ('&') character in front of the name of a bean, you can get at the actual Spring object instead of the bean it creates. For example, the LocalSessionFactoryBean has a public getConfiguration method will get us the Hibernate Configuration object we want. So we can implement a setUp method that contains code like this:
LocalSessionFactoryBean l = (LocalSessionFactoryBean) factory.getBean("&mySessionFactory");
Configuration cfg = l.getConfiguration();
SchemaExport schemaExport = new SchemaExport(cfg);
schemaExport.create(false, true);
Now, every test method in your unit test will have a clean database with all the right tables, indexes, etc.
One side benefit of this is that there's a good chance you will make the tests run faster than when connecting to an external database. But, most importantly, by avoiding dependencies on an external database you improve the reliability of your Hibernate mapping unit tests.
Wednesday, January 10, 2007
Hibernate: Simple Classes Aren't So Simple
I was fighting with Hibernate the other day, trying to figure out why it was giving me a query error. After much too long, I decided to implement unit tests for my measly two classes, which I thought were too simple to really need unit tests.
Almost immediately, I found bugs in one of the two persistent classes I was working with that were confusing Hibernate and leading it to generate the error message that was confusing me.
I checked the Hibernate best practices page and my lesson isn't listed. It may sound obvious, but here it is:
While your classes may be simple, Hibernate is not. Always implement a unit test to verify that the simple parts of your persistent objects are mapped correctly by Hibernate before moving on to more complex issues like inheritance, relationships, containment, etc.
Almost immediately, I found bugs in one of the two persistent classes I was working with that were confusing Hibernate and leading it to generate the error message that was confusing me.
I checked the Hibernate best practices page and my lesson isn't listed. It may sound obvious, but here it is:
While your classes may be simple, Hibernate is not. Always implement a unit test to verify that the simple parts of your persistent objects are mapped correctly by Hibernate before moving on to more complex issues like inheritance, relationships, containment, etc.
Monday, September 11, 2006
Open Source Router
As part of a project to move my noisy Power Mac G4 to the basement (see my blog entery Silence is Golden), I recently bought a LinkSys WRT45GL. This is a 4-port ethernet and WiFi (802.11g) router which is hooked up to my DSL line and provides a firewall and port forwarding for the G4. I know that Kevin Heifner got this same router and immediately installed Thibor's HyperWRT version of the firmware. I don't use Vonage, and Skype has been working fine for me, so I didn't go so far as to replace the stock firmware (yet).
This router has the distinction of being built on top of a Linux kernel (interestingly, it was only after someone figured out that the firmware was based upon Linux that the firmware and technical details were released: the wikipedia for the WRT45GL).
I don't know if I'll ever replace the stock firmware with one of the open source versions, but it's nice to have the option. I doubt I'll try to do anything about it unless I run into a problem caused by the current firmware that isn't addressed by a stock updated from Cisco and is solved by some open source version with very high stability. I don't mind spending time setting these sorts of things up, but I do not want to maintain them on a regular basis.
By the way, I was amazed at the level of detail about the WRT45GL available from wikipedia. There's another blog topic for some other time.
This router has the distinction of being built on top of a Linux kernel (interestingly, it was only after someone figured out that the firmware was based upon Linux that the firmware and technical details were released: the wikipedia for the WRT45GL).
I don't know if I'll ever replace the stock firmware with one of the open source versions, but it's nice to have the option. I doubt I'll try to do anything about it unless I run into a problem caused by the current firmware that isn't addressed by a stock updated from Cisco and is solved by some open source version with very high stability. I don't mind spending time setting these sorts of things up, but I do not want to maintain them on a regular basis.
By the way, I was amazed at the level of detail about the WRT45GL available from wikipedia. There's another blog topic for some other time.
Sunday, September 10, 2006
Silence is Golden
I recently got a new 20" iMac (yes, recently enough to be saddened that they just came out with a 24" model). Since I got the new machine, I've been working to make a place in the basement where I could move the six year old Power Mac G4). The G4 is the mail server for the family domain and will eventually host a family web site.
I finally got that project completed today. It involved running ethernet cables to the attic and basement (solid masonry homes substantially decrease the range of WiFi), putting a shield along the stringers for the basement stairs (to prevent dust, crud, and dropped objects from falling onto the computer), and getting power to the location.
The actual move of the computer was anti-climactic, which is a good thing. Shut it down, disconnect the wires, move the UPS, move the computer, monitor and keyboard, and hook everything back up. A small hiccup with the outlet having a loose wire was quickly solved and everything 'just worked'. Of course, this was after about two months of on-and-off work to get networking cables run and tested, get the dirt shield created, etc. I ran into a problem with attenuation of the Ethernet signal that required me to install a 5-port ethernet switch in the basement. Just goes to show you that 'cat-5' wiring isn't always really 'cat-5' (whether it was not up to spec to begin with, or whether I crimped something and created a problem will probably never be known).
With the G4's (notorioiusly loud) cooling fans no longer generating dBs, the study where the iMac is located is now soothingly quiet. Tonight, while I was fighting with the PC the kids use to play computer games, it was noisy again (the PCs fans are even louder than the G4s). But after I shut that off, I sat in front of the iMac and enjoyed the quiet. In fact, it was so quiet I could hear the sound of a cricket outside the study window trying to foster a next generation before the end of summer.
As for the girl's PC, configuring it to use a WiFi card and to use the printer attached to the iMac was a blunt reminder of just how crappy PC hardware and software really is. But that's a story for another day.
Now, I have to figure out how to convince my wife to let me buy a 24" iMac and give the 20" iMac to the girls. Then we'd be PC-free once again.
I finally got that project completed today. It involved running ethernet cables to the attic and basement (solid masonry homes substantially decrease the range of WiFi), putting a shield along the stringers for the basement stairs (to prevent dust, crud, and dropped objects from falling onto the computer), and getting power to the location.
The actual move of the computer was anti-climactic, which is a good thing. Shut it down, disconnect the wires, move the UPS, move the computer, monitor and keyboard, and hook everything back up. A small hiccup with the outlet having a loose wire was quickly solved and everything 'just worked'. Of course, this was after about two months of on-and-off work to get networking cables run and tested, get the dirt shield created, etc. I ran into a problem with attenuation of the Ethernet signal that required me to install a 5-port ethernet switch in the basement. Just goes to show you that 'cat-5' wiring isn't always really 'cat-5' (whether it was not up to spec to begin with, or whether I crimped something and created a problem will probably never be known).
With the G4's (notorioiusly loud) cooling fans no longer generating dBs, the study where the iMac is located is now soothingly quiet. Tonight, while I was fighting with the PC the kids use to play computer games, it was noisy again (the PCs fans are even louder than the G4s). But after I shut that off, I sat in front of the iMac and enjoyed the quiet. In fact, it was so quiet I could hear the sound of a cricket outside the study window trying to foster a next generation before the end of summer.
As for the girl's PC, configuring it to use a WiFi card and to use the printer attached to the iMac was a blunt reminder of just how crappy PC hardware and software really is. But that's a story for another day.
Now, I have to figure out how to convince my wife to let me buy a 24" iMac and give the 20" iMac to the girls. Then we'd be PC-free once again.
Subscribe to:
Posts (Atom)