Thursday, September 18, 2008

Custom Ant task to read version number from Constants.class file

Problem:
To keep track of a version number, we used to have it in two files: once in a Constants.java for the application itself, and once in the Ant build script in order to have a version number in the resulted WAR's META-INF section. This duplication was cumbersome and error-prone, we always had to update both files and make sure the version numbers matched; in a distributed develop/build environment, this was a nighmare.

Solution: We decided to stick to one version number, in the Constants.java file and to create a custom Ant taskdef to retrieve this number from the compiled class file at the end of the building process.

We have the following parts:

  • in our build.xml, the following taskdef:

    <taskdef name="versionPropertyRetriever" classname="VersionPropertyRetriever" classpath="${basedir}"/>

    and the following task call

    <versionPropertyRetriever name="app.version" className="com.datamat.care.base.Constants" classpath="${basedir}; ${build.web.home}/WEB-INF/classes"/>

    which retrieves the version number and makes it available as the app.version property.


  • a VersionPropertyRetriever java class, extending Ant's Property.java, whose main purpose is to retrieve the value of the VERSION constant in the className class through introspection. Besides adding a getter and setter for the className attribute, its main business logic is implemented within the private parseClass method:

    private void parseClass() {
    try {
    ClassLoader cL = getProject().createClassLoader(classpath);
    Class c = cL.loadClass(className);
    if (c==null)
    throw new BuildException("Couldn't find the Constants class");

    Field field = c.getField("VERSION");
    if (field==null)
    throw new BuildException("'VERSION' field not found within Constants.java file");

    Object value = field.get(null);
    if (value==null)
    throw new BuildException("Version not set within Constants.java file");

    // all fine -> add as a property
    addProperty(name,value.toString());
    }
    catch(Exception e) {
    throw new BuildException("Unexpected Exception: " + e.getMessage());
    }
    }





We compiled the VersionPropertyRetriever.java file through another xml file, to be executed with Ant, buildVersionPropertyClass.xml:

<!--
Script to compile the VersionProperty class starting from the VersionProperty.java file
-->
<project name="buildVersionPropertyClass" default="main" basedir="./">
<property file="build.properties"/>


<path id="compile.classpath">
<!-- Include all JAR files needed to compile -->
<fileset dir="${basedir}" includes="*.class"/>
<fileset dir="${libraries.home}/Ant" includes="*.jar"/>
</path>


<target name="main" depends="" description="Compile java file">
<echo message="Compiling"/>
<javac srcdir="${basedir}" destdir="${basedir}" includes="*.java">
<classpath refid="compile.classpath"/>
</javac>
</target>

</project>

Sunday, May 25, 2008

Create a classpath dynamically

In order to create a classpath dynamically, containing all jars in a certain directory lib, from within a batch file (Windows), just do the following:

  1. Create a cpAppend.bat file, just containing:

    set CLASSPATH=%CLASSPATH%;%1


  2. In the runnable batch file, add the following to construct the path dynamically:

    set CLASSPATH=
    for %%i in (lib\*.jar) do call cpappend.bat %%i


    The variable CLASSPATH will now contain all jars in the lib dir

That's it! On Linux/Unix the method is very similar.

Avoid absolute path in log4j properties file

In order to avoid an absolute path in a log4j.properties file (and hence to be able to add a standard log4j.properties to a cvs), just do the following:

  1. Use a placeholder in the properties file when you indicate the appender file, for instance

    log4j.appender.file.File=${log4j.logFile}


  2. Pass this placeholder value as a VM parameter to the (web) application. The log4j engine will try to resolve the placeholder and eventually fall back on the System parameters, therefore, in our example:

    ... -Dlog4j.logFile=C:/logs/MyApplication.log ...


Easy and perfectly functional!

Wednesday, May 14, 2008

Use Spring's PropertyPlaceholderConfigurer

If you define a datasource within Spring's applicationContext.xml, and don't want to put the db's parameters hardcoded into the file, but have them read from an external configuration file, you can use Spring's PropertyPlaceholderConfigurer


<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="file:${jdbc.configuration}"/>
</bean>

<!-- Datasource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>


and set the "jdbc.configuration" parameter as a VM parameter. When the propertyResolver doesn't find an existing property, it tries to fallback on the VM's system parameters, and this is just what we need.
Only the external configuration file contains the actual jdbc values, while applicationContext.xml doesn't contain any hardcoded data. Very smooth and clean!

Wednesday, March 26, 2008

ArrayUtils.toString(Object[] array)

While programming in Java, I needed a quick way to write out the contents of an array of Objects in java (Object[]), eg. using

logger.debug("result: " + list);

where list is a Object[], results in an unreadable

result: [[Ljava.lang.Object;@5a49e6]


The trick is to use:

logger.debug("result: " + ArrayUtils.toString(list));

where ArrayUtils is a class of the (already mentioned before) Jakarta commons-lang library.

Wednesday, March 19, 2008

Useful Keyboard Shortcuts for the DOS Command Prompt in Windows

Never knew about these very useful DOS hacks, but found them through a post on my favourite tech site, www.lifehacker.com; check out the F7 one!

Tuesday, March 11, 2008

Getting Fieldset Backgrounds and Legends to Behave in IE

When you are building a form with a fieldset tag and a legend tag, and then style the fieldset with a background color, while this works seamlessly as expected in Firefox or Opera, IE6/7 does something a little different: it causes the background color to be applied to the legend as well, mysteriously causing the background color to “spill” out of the fieldset.

I found help on http://www.mattheerema.com/archive/getting-fieldset-backgrounds-and-legends-to-behave-in-ie and especially the code (from within the comments, ), seems to work:

fieldset {
position: relative;
border: 1px solid #fff;
padding: 0px 10px 10px;
margin: 20px auto;
}

legend{
margin-top:-5px;
top: -0.5em;
margin-left: 10px;
padding: 0px 10px;
background: #000;
font-weight : bold;
border: 1px solid #fff;
-moz-border-radius: 10px;
}

Wednesday, March 5, 2008

Mimic hovering in IE

This is old, but might help someone:
The CSS code

td:hover {
background-color: navy;
}

won't work in IE, since it only allows hovering for anchored elements.

A simple workaround for this is creating a new css class that mimics the hovering

td:hover, td.hover {
background-color: navy;
}

and adding in the HTML

<td onmouseover="this.className = 'hover'" onmouseout="this.className = ''">

This will do the trick in IE. A better but more complicated way is using IE's CSS behaviour features.

Tuesday, March 4, 2008

Commons-lang package

Only lately I have found out about the very useful functions inside the commons-lang package. If you ever have written a custom toString method for an array of strings, or a method to check if a string is empty (ie. null or with length=0), you will wish, just like I did, that you had stumbled earlier on this gem.

Api: http://commons.apache.org/lang/api-release/index.html

Check out the ArrayUtils and the StringUtils packages, they are very nice.

Default web method parameter names are arg0, arg1,...

When creating a web service method with the Jax-WS annotations, not specifying the attribute name on the @WebParam annotation, results in your parameters being named arg1, arg2 etc in the created scheme, which is almost always an undesired behaviour.
Hence, always specify the name attribute, it might save you later on.
Example:

@WebMethod
public Customer getCustomer(@WebParam(name = "customerId") String customerId)

saveOrUpdateOrMerge method for Hibernate

Problem
Due to a very non-normalised proprietary database scheme, when doing an update() with Hibernate through Spring's HibernateTemplate, the system sometimes throws NonUniqueObjectExceptions, which can be avoided by doing a merge().

Solution
I added the following convenience method in my AbstractDao class and use this one instead of the Template's update or merge methods. It isn't perfect, but does its job :-)

public void saveOrUpdateOrMerge(Object obj) throws CareDaoException {
try {
getHibernateTemplate().update(obj);
getHibernateTemplate().flush();
}
catch(HibernateSystemException e) {
if (e.getCause() instanceof NonUniqueObjectException) {
logger.warn("update: NonUniqueObjectException found: retrying with merge()");
getHibernateTemplate().merge(obj);
}
else
throw e;
}
}

The flush is needed to send the query to the db straight away, if not, it would do this only at commit time (on a higher level) and the exception wouldn't be caught in the Dao class.

Monday, March 3, 2008

IE6: Location.href in onclick event not working

I stepped on this problem today, and it was driving me crazy, until I found the link below:
Problem:
If you have
1. A hyperlink with an onclick attribute that calls...
2. A JavaScript function that sets the location.href value...
3. and an href attribute with a javascript: protocol value that does nothing (void)

then, when clicking on the link, IE6 won't leave the page you are on.

Workaround:
Use the hash anchor naming option. If you use a non-existent value, then the browser won't scroll either! ;-)
<a href="#none" onclick="doThis();">This works</a>

More info and example on:
http://webbugtrack.blogspot.com/2007/09/bug-223-magical-http-get-requests-in.html

Adding WS-addressing to a JAX-WS Web service

I had the following code:

@WebService(targetNamespace=Constants.NAMESPACE)
public class CustomersWebService{
@Resource
private WebServiceContext context;

static {
// don't put the stacktrace in the soap fault
System.setProperty("com.sun.xml.ws.fault.SOAPFaultBuilder.disableCaptureStackTrace", "true");
}

@WebMethod
public String registrationRequest(@WebParam(name = "registrationRequest") InitialEntity registrationRequest) throws CareException{
...}
...
}


and I needed to make WS-addressing work. Web Services Addressing provides transport-neutral mechanisms to address Web services and messages. (https://jax-ws.dev.java.net/jax-ws-21-ea3/docs/why-wsaddressing.html)

First I had put

@javax.xml.ws.Action(
input="http://example.com/inputAction",
output="http://example.com/outputAction",
fault = {
@javax.xml.ws.FaultAction(className=AddNumbersException.class, value="http://example.com/faultAction")
})

according to http://weblogs.java.net/blog/ramapulavarthi/archive/2006/09/support_for_wsa_1.html above each method, but that wasn't sufficient: the schema didn't change.

What made things work, was adding a @javax.xml.ws.soap.Addressing attribute above my class declaration, right under the @WebService(targetNamespace=Constants.NAMESPACE), as said on https://jax-ws.dev.java.net/jax-ws-21-ea3/docs/wsaddressing.html. The schema changed and the application reacted also to ws-addressed soap calls.

Missing context.xml file

Problem
Tomcat application (with custom db realm authentication) starts up correctly, but fails to show pages. Error in logs says:

Mar 3, 2008 10:28:02 AM org.apache.catalina.authenticator.FormAuthenticator forwardToLoginPage
WARNING: Unexpected error forwarding to login page
org.apache.jasper.JasperException: java.lang.NullPointerException
at org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:541)
...
org.apache.catalina.authenticator.FormAuthenticator.forwardToLoginPage(FormAuthenticator.java:316)
at org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAuthenticator.java:244)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:491)
at
...
Caused by: java.lang.NullPointerException
at org.apache.jsp.login_jsp._jspInit(login_jsp.java:22)
at org.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52)
at
...


Solution
Even though I at first thought this was a problem with conflicting jars, the error was caused by a missing context.xml file, in which a datasource was defined; this datasource was also used by the custom realm and apparently this caused the weird nullPointerException in login.jsp. Putting back our context.xml file solved the problem. An indication of this problem was the line

SEVERE: Null component Catalina:type=DataSource,path=/,host=localhost,class=javax. sql.DataSource,
within the server log; about which the following google search result put me on the right track:
http://www.theserverside.com/news/thread.tss?thread_id=33892

Welcome

Hello!

I have decided to create this blog to keep track of any solutions or smart ideas I discovered whilst being confronted with work related problems. I am currently concentrating on Java/J2EE programming, so most of these problems and the solutions will be related to the Java programming language and its numerous tentacles. ;-) 
This blog is principally meant to my own advantage, to have a quick way to get back 
to previous solutions when being confronted with similar problems; but if what I write is useful for anybody else, why not!
Enjoy!