Friday 24 August 2007

Just in time compilation of GWT code

From Echo2 to GWT... I am still searching for the best toolkit.

What I liked at Echo2 is that the javascript code for the client is created just in time. There is no compilation step to be made before the deployment like in GWT.
GWT allows to develop an application in hosted mode, so probably there is no big need for a just in time compiler, because you will not deploy the application that often as with echo2 (does this make sense?).

Anyway, perhaps this feature may be useful for somebody.

The javascript code for the GWT client, which is normally deployed after compilation as static files, is produced in this example dynamically by a servlet. The servlet compiles the client code to javascript and caches it.

The source can be downloaded from
http://www.banapple.de/sandbox/JustInTimeCompilerServlet.java

What has to be done?


  1. The sources for the client have to be in the classpath of your web application.

  2. The os dependent GWT library containing the GWTCompiler has to be in the classpath.

  3. The servlet is defined to be accessible under the relative url /gwt-jit (or anything you want).

  4. The HTML page containing the GWT client has to include the GWT module using the servlet url, like in

    <meta name='gwt:module' content='/gwt-jit=de.banapple.MyModule'>




How does it work?

The servlet creates to directories under WEB-INF/. One for holding the
compiled files, one as a temporary directory for the compilation.

The GWT bootstrap code requests the file
/gwt-jit/de.banapple.MyModule.nocache.html.

The servlet first looks whether the file
de.banapple.MyModule.nocache.html
is already in the cache of compiled files.
If not the module is compiled in method handleModule. This method just uses the class com.google.gwt.dev.GWTCompiler, which stores the compiled files in the temporary compilation directory.

Afterwards the temporary files are copied to the cache directory. The GWTCompiler creates a directory with the module name in the temporary compilation directory. When the GWT bootstrap code requests further cache files (those files with names being a hash), the servlet can not determine which module they belong to and can therefore not look into directory named after the module (ok, it could, it would have to search all those module directories). So they are all copied flat into the cache directory, hoping that the filenames of different modules do not clash.

The servlet is definitely not fail-proof, e.g it will not work if two request come in which initiate the compilation. But as a first shot it worked for me.

Sunday 27 May 2007

On using the echo2 googlemaps component

There is a echo2 component for google maps
here. And it works really good so far.

The author of this component says that the echo2 code has to be patched such that the google map api can be loaded at page load time which is needed for google maps (at least with echo2 version 2.1.0beta5).

I managed to get the component running with echo2 2.1.0rc2 without patching:

The starting point for an echo2 application is a servlet which extends nextapp.echo2.webrender.WebRenderServlet. The normal behaviour of this servlet is to register some services. One of these services is the nextapp.echo2.webcontainer.WindowHtmlService which produces the output in its service method. The trick is now to replace this class with your own implementation.

I made it this way:

The constructor of my HelloWorldServlet which extends nextapp.echo2.webrender.WebRenderServlet and which is the starting point of my echo2 application is the following:

public HelloWorldServlet()
{
super();

final String METHOD = "HelloWorldServlet: ";

ServiceRegistry serviceRegistry = WebRenderServlet.getServiceRegistry();
serviceRegistry.remove(WindowHtmlService.INSTANCE);
serviceRegistry.remove(NewInstanceService.INSTANCE);
serviceRegistry.add(MyNewInstanceService.INSTANCE);
serviceRegistry.add(MyWindowHtmlService.INSTANCE);

log.debug(METHOD+"successful");
}

This removes the old service implementations and replaces it with my own implementations:

package de.banapple.echo2test;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import nextapp.echo2.webcontainer.ContainerInstance;
import nextapp.echo2.webrender.Connection;
import nextapp.echo2.webrender.Service;

public class MyNewInstanceService
implements Service
{
private static Log log = LogFactory.getLog(MyNewInstanceService.class);

public String getId()
{
return "Echo.NewInstance";
}

public int getVersion()
{
return -1;
}

public void service(Connection conn)
throws IOException
{
final String METHOD = "service: ";
log.debug(METHOD+"conn="+conn);

ContainerInstance.newInstance(conn);
MyWindowHtmlService.INSTANCE.service(conn);
}

public static final MyNewInstanceService INSTANCE =
new MyNewInstanceService();

}



package de.banapple.echo2test;

import java.io.IOException;

import nextapp.echo2.webcontainer.ContainerInstance;
import nextapp.echo2.webcontainer.WindowHtmlService;
import nextapp.echo2.webrender.BaseHtmlDocument;
import nextapp.echo2.webrender.Connection;
import nextapp.echo2.webrender.ContentType;
import nextapp.echo2.webrender.output.CssStyle;
import nextapp.echo2.webrender.service.CoreServices;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;

public class MyWindowHtmlService
extends WindowHtmlService
{
private static Log log = LogFactory.getLog(MyWindowHtmlService.class);

public void service(Connection conn)
throws IOException
{
final String METHOD = "service: ";
log.debug(METHOD+"conn="+conn);

ContainerInstance ci = (ContainerInstance)conn.getUserInstance();
conn.setContentType(ContentType.TEXT_HTML);
boolean debug = !"false".equals(conn.getServlet().getInitParameter("echo2.debug"));
BaseHtmlDocument baseDoc = new BaseHtmlDocument("c_root");
baseDoc.setGenarator("NextApp Echo v2.1.0.rc2");

/* add google maps api */
baseDoc.addJavaScriptInclude(
"js/googleload.js");

baseDoc.addJavaScriptInclude(ci.getServiceUri(CoreServices.CLIENT_ENGINE));
baseDoc.getBodyElement().setAttribute("onload", "EchoClientEngine.init('" + ci.getServletUri() + "', " + debug + ");");
Element bodyElement = baseDoc.getBodyElement();
CssStyle cssStyle = new CssStyle();
cssStyle.setAttribute("position", "absolute");
cssStyle.setAttribute("font-family", "verdana, arial, helvetica, sans-serif");
cssStyle.setAttribute("font-size", "10pt");
cssStyle.setAttribute("height", "100%");
cssStyle.setAttribute("width", "100%");
cssStyle.setAttribute("padding", "0px");
cssStyle.setAttribute("margin", "0px");
cssStyle.setAttribute("overflow", "hidden");
bodyElement.setAttribute("style", cssStyle.renderInline());
baseDoc.render(conn.getWriter());
}

public static final MyWindowHtmlService INSTANCE =
new MyWindowHtmlService();
}


The implementation of the service method of MyWindowHtmlService is copied from the original implementation and extended with the
baseDoc.addJavaScriptInclude("js/googleload.js"); which loads the google maps api with my key.

Ok, that is not really far away from patching the class, but still... It might work with forthcoming versions of echo2.

Wednesday 23 May 2007

Maven archetype for echo2 application

While testing echo2
I searched for a maven archetype which creates an initial echo2 application..,
and did not find one.

So I made one available at
http://maven-repository.banapple.de/de/banapple/maven/archetype/

If you are interested download the jar and install it in your local maven repository and then
create your echo2 app with

# mvn archetype:create \
-DarchetypeGroupId=de.banapple.maven.archetype \
-DarchetypeArtifactId=echo2 \
-DarchetypeVersion=1.0-SNAPSHOT \
-DgroupId=<your groupId here> \
-DartifactId=<your artifactId here>


Change to the newly created directy and test the application with

# mvn jetty:run


This starts a jetty server on port 8080 in which the application runs.

The archetype uses echo2 libraries which are deployed on
maven-repository.banapple.de, too.

Thursday 17 May 2007

Creating POST requests with curl for Axis2 REST service

Axis2 allows to create REST services as easily as SOAP services. By default each
SOAP service has a REST counterpart automatically available.

The responses for REST requests served by Axis2 are the same as SOAP responses but
are missing the enclosing SOAP envelope (this is because REST is handled inte
rnally like a SOAP request). Because of this the responses are currently missing
the REST feature to return links to other resources in the response.

But creating REST requests is somewhat easier than SOAP requests. This article
describes an example where a REST request is sent with curl.

Axis2 uses document-style services by default. The following excerpt of the
webservice WSDL which was used shows the XML schema for the data to be transmitted:


<xs:schema xmlns:ns="http://impl.service.banapple.de/xsd" attributeFormDefault="qualified" elementFormDefault="unqualified" targetNamespace="http://impl.serv ice.banapple.de/xsd">
<xs:element name="createContact">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="incomingData" nillable="true" type="ns10:IncomingData" />
<xs:element maxOccurs="unbounded" name="foreignKeys" nillable="true" type="ns10:IncomingForeignKey" />
<xs:element name="incomingCreator" nillable="true" type="ns10:IncomingCreator" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="createContactResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="return" nillable="true" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema xmlns:ax242="http://bean.model.banapple.de/xsd" xmlns:ax283="http://lang.java/xsd" xmlns:ax244="http://util.java/xsd" attributeFormDefault="qualified" elementFormDefault="unqualified" targetNamespace="http://bean.model.banapple.de/xsd">
<xs:import namespace="http://util.java/xsd" />
<xs:import namespace="http://lang.java/xsd" />
<xs:element name="IncomingData" type="ns10:IncomingData" />
<xs:complexType name="IncomingData">
<xs:sequence>
<xs:element name="creationDate" type="xs:long" />
<xs:element name="dataTypeName" nillable="true" type="xs:string" />
<xs:element name="dataValue" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="IncomingForeignKey" type="ns10:IncomingForeignKey" />
<xs:complexType name="IncomingForeignKey">
<xs:sequence>
<xs:element name="foreignKeyTypeName" nillable="true" type="xs:string" />
<xs:element name="foreignKeyValue" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="IncomingCreator" type="ns10:IncomingCreator" />
<xs:complexType name="IncomingCreator">
<xs:sequence>
<xs:element name="name" nillable="true" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>


Two namespaces are used:
http://impl.service.banapple.de/xsd for the element describing the
operation,
http://bean.model.banapple.de/xsd for the parameters of the operation.

The example payload for the request is the following:


<createContact
xmlns="http://impl.service.banapple.de/xsd"
xmlns:bean="http://bean.model.banapple.de/xsd">
<bean:incomingData>
<creationDate>0</creationDate>
<dataTypeName>CUSTOMER_ID</dataTypeName>
<dataValue>300042045</dataValue>
</bean:incomingData>
<foreignKeys>
<foreignKeyTypeName>UCID</foreignKeyTypeName>
<foreignKeyValue>1</foreignKeyValue>
</foreignKeys>
<incomingCreator>
<name>Foobar</name>
</incomingCreator>
</createContact>


The payload was stored in a file createcontact.xml and sent to the
REST service with curl with the command:


curl -H "Content-Type: text/xml; charset=UTF-8"
--data-binary @createcontact.xml
http://localhost:8080/contactdb/rest/DataReceiverService/createContact



It is important to sent the Content-Type header otherwise the REST
service will produce an error response with the not very helpful message

Required element null defined in the schema can not be found in the request

Thursday 10 May 2007

Adding Axis2 webservice programmatically at runtime

I just tried out Axis2 (in version 1.1.1).

But I wanted to embed the AxisEngine into my own web application instead of building an aar and deploying it into the axis2 default web application. One possible way would have been to use the same web application directory layout as used in the axis2 default web app and put a service archive into the WEB-INF/services directory or to package my web application war having the exploded service archive in the WEB-INF/services. Both solutions would have complicated the build process.

So I wanted to add services programmatically at runtime. Perhaps using some annotations on classes which mark them as webservices.

I found code like that:

service = AxisService.createService(
MyServiceImpl.class.getName(),
axisConfiguration,
RPCMessageReceiver.class);
service.setName("MyService");
axisConfiguration.addService(service);
axisConfiguration.startService(service.getName());


But the problem was to get the AxisConfiguration. The solution was simple to extend the AxisServlet which has a reference to the configuration. So MyAxisServlet overrode the init method in the following way...


public void init(ServletConfig config) throws ServletException
{
super.init(config);

AxisService service;
try {
service = AxisService.createService(
MyServiceImpl.class.getName(),
axisConfiguration,
RPCMessageReceiver.class);
service.setName("MyService");

axisConfiguration.addService(service);
axisConfiguration.startService(service.getName());
} catch (AxisFault e) {
e.printStackTrace();
}
}


...and substituted the default AxisServlet in the web.xml with MyAxisServlet ...and it worked.

At the moment I don't know if I made everything right but I had a webservice running.

The next step was then to change the init method to search all classes having an annotation @Webservice and add them in the same way as above. Name, scope and targetNamespace were set with annotation attributes.

So I have my classes annotated and added automatically to the AxisEngine. When I add another webservice class to my web app the only thing to do is to add the annotation. And I don't need a special build process to build the archive or package my war file.

Tuesday 1 May 2007

Versioning of database entities with Hibernate

At work I had the requirement to introduce a versioning mechanism for database entities in an already productive application which used Hibernate as persistence framework.

Versioning was meant in the following way: the state of an entity had to be reconstructable at any point of time and for each change the id of the changer and the time had to be stored. In short: who made what when?

The following ideas and techniques are not new and are not my own ideas. The implementation orients itself on suggestions from the book "Hibernate in Action" written by Gavin King and Christian Bauer, which I can recommend.

The database in use did not have triggers, therefore an implementation using database triggers was not possible.
Instead of that the mechanism was to be implemented with Hibernate means.

Since the application was already in production the changes had to be minor.
One possibility to achieve the goal would have been to introduce a superclass for all entities to be versioned. But this would have been a bigger change. The risk of errors would have been to great.

The implementation was done in the following way:
1. a new annotation Historicizable marks the entities to be versioned
2. two new database tables were introduced: EntityHistory and EntityHistoryData. The former includes class name and id of the changed entity, date of the change, the initiator of the change and the type of change (INSERT, UPDATE, DELETE). The latter contains information about each changed attribute of the entity. There is a one-to-many relationship between them.
3. an Interceptor was defined which collects data about changes and stores them.

The Interceptor
gets registered at the SessionFactory. It is therefore used in each Hibernate session. Our interceptor implements the methods onSave, onFlushDirty, onDelete, afterTransactionCompletion and postFlush. The first three just collect information about changes and create a list of EntityHistory beans. The interceptor checks whether the entity for which the interceptor was called is annotated with Historicizable. If not no data is collected.
afterTransactionCompletion checks whether the current transaction was rolled back. In this case the list of collected EntityHistory beans is cleared.
Method postFlush makes the list of EntityHistory beans persistent. This method has to use its own Hibernate Session but which can share the same connection.

The existing API method of the application did not have an argument which defines the initiator of a change. Instead of changing each of these methods the initiator data can be set as a ThreadLocal variable on the interceptor. So the versioning mechanism relies on the client of the application API to set the initiator properly before calling any API method which could possibly include versioning.

E.g. some API methods were published as webservices. The webservices itself were extended so that initiator data could be send as a SOAP header with the request.

This versioning mechanism only works for entities having only primitive or simple properties. Versioning of entities with references to other entities is not supported.