Sunday, May 3, 2009

I've Moved

To be exact, I've moved here

Monday, December 22, 2008

Mapping a class multiple times in hibernate

I'm currently working on a project where we are converting the persistence layer of a legacy system from an object database to Hibernate. Since you can save any object graph with an object database, this causes some interesting mapping scenarios. In a couple of instances, I found it very useful to use the 'entity-name' attribute, which allows you to map a single class more than once.

For example, consider the following:

class MyMapWrapper {
Map theMap;
}

class A {
MyMapWrapper wrapper;
}

class B {
MyMapWrapper wrapper;
}

How do you map MyMapWrapper? The generic types determine the way that the wrapped map should be mapped. The nice way is to unwrap the map, but when working with legacy code this can cause a lot of ripple effects that you may not want to get into. Using entity-name, the solution becomes quite simple:

<class name="MyMapWrapper" entity-name="StringToStringMapWrapper">
<map name="theMap">
<key column="id">
<map-key column="strkey" type="string">
<element column="strvalue" type="string">
</map>
</class>


<class name="MyMapWrapper" entity-name="StringToIntegerMapWrapper">
<map name="theMap">
<key column="id">
<map-key column="strkey" type="string">
<element column="intvalue" type="integer">
</map>
</class>

<class name="A">
<many-to-one entity-name="StringToStringMapWrapper"/>
</class>

<class name="B">
<many-to-one entity-name="StringToIntegerMapWrapper"/>
</class>

There are one or two caveats to this though:
1. You need to know the entity-name if you want to just persist an instance of MyMapWrapper, since the class alone does not make it unique anymore.
2. You can't use entity-name in an any mapping. I don't like any, but unfortunately it is also often required for converting legacy stuff.

Disclaimer: I did not specifically test the above mappings, it just illustrates the point, but we did solve many issues this way.

Monday, August 18, 2008

Manually injecting a wicket SpringBean

You may need to do this occasionally, for example if you want to access a service from your Application or Session object.

You do it like this: InjectorHolder.getInjector().inject(object);

Saturday, July 5, 2008

My simplest log4j.properties

I don't mind log4j, but I don't like configuring it. Why doesn't it just log everything on info by default? Instead, you HAVE to configure it. And because I've always wanted it to just work, I've never bothered to learn its intricacies (of which there are many). So, here is my super simple log4j.properties, for others that just want it to log stuff:

log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender
log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STDOUT.layout.ConversionPattern=%d %p [%t] %C{1} - %m\n

log4j.category.com.myapp=INFO, STDOUT
log4j.category.org.hibernate=ERROR, STDOUT
log4j.category.org=INFO, STDOUT
log4j.category.com=INFO, STDOUT

Tuesday, May 20, 2008

Wicket components not being created when visible = false

Came across an interesting one today... if you have a component in Wicket with visible=false when the page is rendered the first time, the markup for that component is not sent to the browser at all. The end result is that you cannot use Ajax to make the component visible at a later stage. Turns out there is an easy way around this though, just call setOutputMarkupPlaceHolderTag(true) on the component, and a placeholder tag is sent ot the browser, which can later be replaced with Ajax.

Saturday, March 29, 2008

ZK Example Application with: Maven, Spring, Hibernate, Acegi

I've been working with ZK for a while now, and like its ease of use and low learning curve. However, I had a pretty hard time using it with some of my other favourite frameworks, specifically: Maven, Spring, and Acegi. Also, I found some of the issues around navigation and the initial setup a bit confusing.

Something that would have helped me a lot would have been an example that includes a full Spring / Acegi setup, packaged with Maven so it works 'out of the box', including a couple of screens that actually does your regular search / crud operations. So, I've decided to extract such an example from a project I've been working on. This is just my tiny contribution to the ZK community.

This example app includes all of the above, as well as using Hibernate for persistence, though that is all wrapped in Spring and accessed via services. It's far from perfect, but I think it's a good start. I would greatly appreciate any feedback or suggestions if anyone uses this.

Download:
Download the project as a ZIP file from here.

Usage:
When you launch, you will be presented with a login screen. The Acegi setup is such that a hard-coded user 'admin' with password 'password' can log in only until an actual user exists in the database. The system has screens for administering users, roles and privileges.

Initial setup:
Firstly, you will need Maven, Java, and a database. The example application uses PostgreSQL. To use a different DB setup, edit the following files:
  1. zkskeleton-persistence/pom.xml (The driver from Maven)
  2. zkskeleton-datasource-context.xml (Connection when deployed)
  3. zkskeleton-domain-test-context.xml (Connection for integration tests)
The example includes integration tests so a DB is required to run test cases. To get it going, create a database, user and schema called 'zkskeleton'. The user's password must also be 'zkskeleton'. In postgresql the following should work:
CREATE USER zkskeleton WITH PASSWORD 'zkskeleton' createdb nosuperuser;
CREATE DATABASE zkskeleton WITH OWNER = zkskeleton;
\c zkskeleton
CREATE SCHEMA zkskeleton AUTHORIZATION zkskeleton
To get it running:
  1. Unpack it somewhere
  2. mvn clean install
  3. Deploy the WAR from zkskeleton-web/target/zkskeleton.war
To set it up in your IDE:
I use idea, but eclipse is pretty similiar
  1. mvn idea:clean idea:idea -DdownloadSources=true
  2. Open the project file
Customizing:
The first thing you'll probably want to change is the name, to do this refactor all the packages from 'zkskeleton' to something appropriate. You will also need to change the project directories and xml file names. Then do a global search and replace from 'zkskeleton' to your application name. You will probably also want to change the DB setup, this can be done as explained above.

Architecture:
Of course there are lots of clever ways to do this, but my architecture looks something like this:
  1. Domain layer depending on nothing, containing the domain entities
  2. Persistence layer depending on Domain, containing Hibernate crud stuff
  3. Service Shared layer depending on Domain (bad thing, but I can live with it for purely web based) containing service interfaces and DTO's
  4. Service depending on Service Shared containing service implementations
  5. UI layer depending on Service Shared containing ZK classes, controllers and extensions
  6. Web layer depending on Service and UI, containing the ZK markup files, webapp setup, and Acegi and service wiring.
Each of the above layers exist in its own Maven project, which is in turn wrapped in one top-level project.

PS: The idea behind the navigation is borrowed from the ZK demo app, I hope they don't mind. Since that source is freely available it's probably fine, just giving credit where it's due though.

Saturday, January 26, 2008

ZK: Non-serializable attribute error on StandardSession

I got this one while starting up Tomcat and struggled with it for probably two days. The stack trace looks something like this:

SEVERE: Servlet.service() for servlet zkLoader threw exception
java.lang.IllegalArgumentException: setAttribute: Non-serializable attribute
at org.apache.catalina.session.StandardSession.setAttribute(StandardSession.java:1293)
at org.apache.catalina.session.StandardSession.setAttribute(StandardSession.java:1254)
at org.apache.catalina.session.StandardSessionFacade.setAttribute(StandardSessionFacade.java:130)
at org.zkoss.zk.ui.http.WebManager.newSession(WebManager.java:308)
at org.zkoss.zk.ui.http.WebManager.getSession(WebManager.java:320)
at org.zkoss.zk.ui.http.WebManager.getSession(WebManager.java:287)


The problem was a tag in my web.xml. I must have clicked it on in IDEA's graphical view of the web.xml. Just remove the tag and you're good to go.

Saturday, August 4, 2007

Hibernate / PostgreSQL issue: operator does not exist: bigint = bytea

This one comes up during HQL and criteria queries seldom enough to confuse the hell out of me every time I see it. However, the problem is always exactly the same thing:

You are generating your schema from your hibernate mappings, and one of your query parameters are either null or transient.

What happens is that the generated schema, with postgresql, uses bytea as the type of the foreign key when mapping collections. Describe the table that the collection gets persisted in and you'll see it. Then, Hibernate does explicit typecasting to change it to an int or bigint when you pass in a value, but when you pass in null it breaks down and you get the above error.

Friday, July 27, 2007

ZK, EL and XML Escaping

If you've worked with ZK for a bit, you will know that it uses an EL style of expression to set values and perform actions anywhere in the page. Which means you should be able to do something like:

<radio label="Sale" onCheck="${priceLabel.value='fruit'}"/>

However, this is not correct, in fact EL expressions will not be evaluated at all for the above line. The correct way to do it is as follows:

<radio label="Sale" onCheck="priceLabel.value=&quot;fruit&quot;"/>

Why? The developers guide explains it: "you could use EL expressions in any part of ZUML pages, except the names of attributes,
elements and processing instructions"

As the above is a processing instruction, you need to drop the EL syntax, which is a little strange as the syntax remains the same and is apparently still processed by BeanShell. Oh well. As for the escaping, that's just well-formed XML.

Sunday, July 22, 2007

Transaction management with Spring / Hibernate + ZK

Normal Spring transaction management for Hibernate does not work out of the box with ZK. Actually to be exact the transaction management works fine, but the Hibernate session is not kept open after the transaction until the end of the request as it should when using OpenSessionInViewFilter. The end result is that you get LazyInitializationException when trying to access proxies, since the session is closed.

To fix this, a file called WEB-INF/zk.xml must be created with the following contents:
<zk>
<listener>
<description>ThreadLocal Variables Synchronizer</description>
<listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class>
</listener>

<preference>
<name>ThreadLocal</name>
<value>
org.springframework.transaction.support.TransactionSynchronizationManager=resources,synchronizations,currentTransactionName,currentTransactionReadOnly,actualTransactionActive;
org.springframework.orm.hibernate3.SessionFactoryUtils=deferredCloseHolder;
org.springframework.transaction.interceptor.TransactionAspectSupport=transactionInfoHolder;
</value>
</preference>
</zk>



The problem is that the Spring OpenSessionInViewFilter opens a session, and binds it to the request's thread. However, ZK uses a multi-threaded event driven approach, so the actual processing happens in a new thread that gets kicked off by the ZK servlet - this breaks the connection to the open session. Adding the above listener copies the ThreadLocal variables with the session etc from the request thread's context into the event thread. As you can see, more prefs can be added to copy other objects from the ThreadLocal context.

I had to inspect a lot of code to fix this, turns out it was documented in great detail all along (but sort of all over the place):


Another possible solution is to make the ZK servlet perform all processing within its own thread, this was a new feature in ZK 2.4. I haven't tried this though and it looks like this may be dangerous, since the thread will block the entire servlet if it blocks for any reason. To enable this, the following is required in the zk.xml:

<system-config>
<disable-event-thread/>
</system-config>

Friday, July 20, 2007

Getting Maven to do ZK

I couldn't find the Maven dependecies for ZK on the zkoss site, so here is what I had to include in my pom.xml to get it working. Beanshell is pretty central to ZK so I'm not sure why it doesn't get pulled in with the base ZK dependency. The jhlabs dependency is needed for captchas, if you're going to need that. By the way I would post the actual XML except then I have to escape the tags which is painful.

org.zkoss.zk:zkplus:2.4.1
org.zkoss.common:zweb:2.4.1
org.zkoss.common:zcommon:2.4.1
org.zkoss.zkforge:fckez:2.3-3
org.zkoss.zk:zul:2.4.1
org.zkoss.zk:zhtml:2.4.1
org.zkoss.zkforge:timelinez:1.1_3
org.zkoss.zk:zk:2.4.1

com.jhlabs:filters:2.0.235
org.beanshell:bsh:2.0b4

ZK + Eclipse: XML schema validation / Content assist

To get Eclipse to use content assist with ZK, I had to declare the schema as follows. This took me a while to figure out for some reason. I use MyEclipse, by the way, so WTP should be similiar.

<window xmlns="http://www.zkoss.org/2005/zul" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.zkoss.org/2005/zul http://www.zkoss.org/2005/zul/zul.xsd">

</window>