Wednesday, March 16, 2005

Spring is great; Spring sucks

In general, I have found Spring to be an invaluable framework for my development tasks. It provides fairly elegant APIs for most useful things, and does them well. Occasionally, however, you can get really stuck. I wasted several hours ( possibly days) trying to fix the following problem:
I upgraded to Tomcat 5.0.28 and somehow all my Spring applications stopped working. Specifically, i could not get a datasource to save my life, and this being a database driven web system, I was left in the proverbial mud (if that is indeed a proverb. I'm a software developper, not a sage).
It turns out that there is parameter ( little documented as far as I can tell) called "resourceRef". Now, I haven't really explored exactly what it does, but as far as I can tell, it simply appends "java:comp/env/" to the beginning of the jndi name. This in itself wouldn't have been such a big deal - and I would have probably thought to look there earlier, had the template file which I have been using ( copied from Spring, I believe) had the following text:


I have since removed that comment.
javax.naming.NameNotFoundException: Name jdbc is not bound in this Context
at org.apache.naming.NamingContext.lookup(NamingContext.java:768)
at org.apache.naming.NamingContext.lookup(NamingContext.java:151)
at org.apache.naming.SelectorContext.lookup(SelectorContext.java:136)
at javax.naming.InitialContext.lookup(InitialContext.java:347)
at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:123)
at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:85)
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:121)
at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:71)
at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:85)
at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:124)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1073)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:343)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:260)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:221)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:145)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:285)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:317)
at org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.refresh(AbstractRefreshableWebApplicationContext.java:131)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:224)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:150)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:48)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3827)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4343)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:823)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:807)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:595)
at org.apache.catalina.core.StandardHostDeployer.addChild(StandardHostDeployer.java:903)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.commons.beanutils.MethodUtils.invokeMethod(MethodUtils.java:216)
at org.apache.commons.digester.SetNextRule.end(SetNextRule.java:256)
at org.apache.commons.digester.Rule.end(Rule.java:276)
at org.apache.commons.digester.Digester.endElement(Digester.java:1058)
at org.apache.catalina.util.CatalinaDigester.endElement(CatalinaDigester.java:76)
at org.apache.xerces.parsers.AbstractSAXParser.endElement(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanEndElement(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.apache.commons.digester.Digester.parse(Digester.java:1567)
at org.apache.catalina.core.StandardHostDeployer.install(StandardHostDeployer.java:488)
at org.apache.catalina.core.StandardHost.install(StandardHost.java:863)
at org.apache.catalina.startup.HostConfig.deployDescriptors(HostConfig.java:483)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:427)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:983)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:349)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1091)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:789)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1083)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:478)
at org.apache.catalina.core.StandardService.start(StandardService.java:480)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:2313)
at org.apache.catalina.startup.Catalina.start(Catalina.java:556)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:287)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:425)

Wednesday, March 09, 2005

JSON and Spring

Not that this is necessarily the best place to post, but here it goes:

I can case you are trying to use Spring and JSON and happen to not be able to modify an existing servlet/jsp page to accomplish your goal, there is a work around:

public class NDCJSONSessionFilter extends OncePerRequestFilter {



protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {


HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;

// Find the JSONRPCBridge for this session or create one
// if it doesn't exist. Note the bridge must be named "JSONRPCBridge"
// in the HttpSession for the JSONRPCServlet to find it.
HttpSession session = req.getSession(true);
JSONRPCBridge json_bridge = null;
json_bridge = (JSONRPCBridge) session.getAttribute("JSONRPCBridge");

if(json_bridge == null) {
json_bridge = new JSONRPCBridge();
session.setAttribute("JSONRPCBridge", json_bridge);
}

WebApplicationContext context = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());

IService service = (IService )context.getBean("service");

json_bridge.registerObject("Service", service);



}


}

-- this will allow you use JSON if you happen to be limited by something like by the use of velocity templates, etc.

This probably not useful to anyone else, but just in case. Oh, and does anyone know how I can put code in a blog??

Wednesday, March 02, 2005

Object-Relational mapping

You may have overheard the question before, whispered clandestinely between curious developpers. 'What is the point of using a object-relational tool - why not just use JDBC?' They ask. 'We know the database, and we can create the objects as we need them.' Let me posit some answers.

In order to address this question, we need to understand the full scope of a persistence layer.

1.) Transparent and/or low impact enabling of persistence on objects.
2.) Caching of objects
3.) Querying of objects
4.) Mapping of database to the objects and usually vice-versa

A lot of developers out there end up gradually evolving a home-grown and usual only partially functional persistence layer -- piece by piece, layer by layer.

At my current company, we have gone through 2 or 3 different persistence mechanisms over three years, with each one leaving a long trail of problems. This reflects general trends in software development over the past few years, although our codebase does seem to be a few years behind the industry-wide trends.

In the first stage, the code base was written using what is typically called a Table access mechanism, i.e., with a more or less 1-to-1 correspondence between the database structure and that of the data access objects. The relationships between tables are embodied in the SQL at the code level ( i.e. straight-forward JDBC for data retrieval and recording.)

The second generation (and, really the final generation, the third having never really received full traction in the company) comprises an evolutionary approach to persistence. This involves the following stages:

1.) Creating a set of classes similar to the classes above for loading/saving of individual classes
2.) Creating a single mechanism for loading of certain classes based on a unique ID structure (i.e. each class type has a corresponding ID type which is passed to the loader)
3.) Implementing caching for each loader.
4.) Getting the objects to manage their loading (e.g. have classes load database connections in the model)
5.) Creating a separate system for the saving of data.

Now, each of these evolutions came about as a new requirement came in, and the reason that a complete persistence system was never implemented was because it was never absolutely necessary to have a complete persistence domain at any one time. One can see, however, that in the end, the final result is a limited and rather poorly designed persistence mechanism. This, at least partially, illustrates why it is useful to think about the long-term picture before patching a limited system.

This addresses at least 3 of the properties listed above, but does not address the querying property. This came about later, when we were doing real-time analysis of data. Since we have a complex object model, including who created the data, who currently owned the data, etc -- altogether over 40 interconnected classes, ignoring sub-domain specific classes and data tables -- we often found ourselves performing complex queries across the datasets. The manner in which these queries were written down evolved over time, until they came to be written in a language that had a strange resemblance and HQL. Unfortunately, by the time this came about, we had written at least 3 different libraries for efficiently producing SQL queries, with the final version being only very slightly different from HQL.

The reasons that these changes were not foreseen (or perhaps just ignored) revolve a great deal around the politics of the company and its reluctance to use new technologies. However, at a deeper level it is a common mistake to evolve a design based on a small problem domain and not grasp the whole picture of where the code is heading. In our case, this had a great deal more to do with the quasi-burlesque view of our software as being a different from all other software projects, from the manner in which it was conceived, to its functionality, and finally to it capabilities, but these are topics for another day.

Since this is my first blog, I would appreciate comments ( if anyone happens to read this) with regards to the content: is this too little detail, is it stating the obvious, and does anyone really care? Did this help in the least bit to clarify why O/R layers are actually useful and not just some bizarre metadata-driven form of intellectual masturbation that allows architects to defend their constantly evolving realm?