Pages

Monday, October 22, 2012

JAVA_HOME setting on MacOS

I just downloaded the latest Java Update 1.7.09 for MacOS and installed it. And as usual, I had to fix the JAVA_HOME environmental property, so that all my Maven etc. tools still worked properly. In ~/.bash_profile, I added/updated following entries:
export JAVA_HOME6=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
export JAVA_HOME7=/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
export JAVA_HOME=$JAVA_HOME7
export PATH=$JAVA_HOME/bin:$PATH
I use JAVA_HOME6 and JAVA_HOME7 so that I can easily switch the standard Java version.

But, stop, stop, there must be a better way to do that, so that I don't have to update these lines after every Java download. And actually it is by using the not-so-well-known-but-very-useful command java_home:
The java_home command returns a path suitable for setting the JAVA_HOME environment variable. It determines this path from the user's enabled and preferred JVMs in the Java Preferences application. Additional constraints may be provided to filter the list of JVMs available. [...] The path is printed to standard output.
On my machine, java_home prints following path to the STDOUT:
[561]% /usr/libexec/java_home --version 1.7
/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
So, I can change my .bash_profile as follows:
export JAVA_HOME6=`/usr/libexec/java_home --version 1.6`
export JAVA_HOME7=`/usr/libexec/java_home --version 1.7`
export JAVA_HOME=$JAVA_HOME7
export PATH=$JAVA_HOME/bin:$PATH
On my machine, my Java environmental parameters becomes:
[555]% env | grep JAVA
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
JAVA_HOME6=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home
JAVA_HOME7=/Library/Java/JavaVirtualMachines/jdk1.7.0_09.jdk/Contents/Home
Finito.

Saturday, October 13, 2012

Create an XSD from XML using Trang

I recently needed to create an XML Schema XSD file from an existing XML file. A short research pointed me to Trang. Description from their website:

Trang, a program for converting between different schema languages, focused on RELAX NG; in particular, it can convert between the compact and XML syntaxes, it can convert between RELAX NG and DTDs, and it can convert from RELAX NG to W3C XML Schema

But beside the convertion between different schema languages, Trang is also able to create schemas based on XML files.

The creation of an XSD is done as follows for UTF-8 encoding using a JRE 1.5 or above:

java -jar trang.jar -i encoding=UTF-8 message.xml message.xsd
Or, with explicit input and output format if the former command does not work as expected:
java -jar trang.jar -I xml -O xsd -i encoding=UTF-8 message.xml message.xsd
Easy.

Tuesday, August 28, 2012

Short novice guide for importing a project to Subversion

My new employer uses Subversion as the central version control system, so I'm trying to take the first steps as my former employer still used the good old CVS. (This also means that you don't find advanced infos here but only novice infos.)
(And of course, it would be nice, if my new employer would use Git, however, Subversion and Git can co-exist as you may know already. But this is not the subject of this blog entry.)
So, how to import an existing project to Subversion? I don't mind loosing the history of the project, of course this makes the whole migration process a lot easier.

1. Create a Subversion repository

First, a central Subversion repository has to be created if it does not exist already:
$ svnadmin create /svn/repos

2. Import an existing project to the trunk

So, let's import the project directory myproject to Subversion. I already learned that the recommended structure should be as follows:
myproject/
  trunk/
  tags/
  branches/
As in a non-Subversion project, the trunk, tags and branches directories usually do not exist yet, they have to be created first. The import sequence for the project directory myproject becomes as follows:
$ cd <parent directory of the project directory myproject>
$ mkdir myproject_svn myproject_svn/tags myproject_svn/branches
$ mv myproject myproject_svn/trunk
$ svn import myproject_svn file:///svn/repos/myproject
As the Subversion repository is locally visible, I use the file:// schema.
Don't forget the myproject in the last command or all files in svn_myproject will be imported directly into the root directory of the repository.
You cannot work directly with the created myproject_svn directory above, you first have to checkout it again:
svn checkout file:///svn/repos/myproject/trunk -d myproject

3. Create a branch

Creating a branch is just a Subversion copy:
$ cd myproject
$ svn copy trunk/ branches/myproject-branch
$ svn status
A +  branches/myproject-branch
Here, the trunk/ directory is copied recursively in the working directory brances/myproject-branch. The output of the command svn status indicates that the directory is ready to be added to the repository. The + says that branches/myproject-branch is just a copy and nothing new.
When commiting, Subversion creates the directory in the repository:
$ svn commit -m"creating new branch"
Adding         branches/myproject-branch
Committed revision 987.
Subversion does not really copy all files, but just creates a new repository entry pointing to the original tree, so this is also called a "cheap copy". Please refer to the Subversion documentation for more detailed infos.
The one step method without the need of a temporary directory is as follows:
$ svn copy file:///svn/repos/myproject-trunk \
  file:///svn/repos/myproject/branches/myproject-branch \
  -m"creating new branch"

Monday, June 18, 2012

How to set up Eclipse template for inserting Log4j Logger

Eclipse Template for inserting Log4j Logger

Tired adding private static final Logger logger = Logger.getLogger(XXX.class); by hand to your Java classes?
Use Eclipse templates! Navigate to Windows -> Preferences -> Java -> Editor -> Templates. And add following new template to the Java context:
private static final Logger logger 
  = Logger.getLogger(${enclosing_type}.class);
  ${:import(org.apache.log4j.Logger)}
Finally, this should look as follows:



Now, if you are in the Java context, then type "logger" followed by "Ctrl+space". You are invited to choose from different templates including your template entered above:



After choosing the template, you ending up with an added logger plus the corresponding Java import:



Sunday, April 22, 2012

Reading and Writing UTC Timestamps to DB with Hibernate

Problem

Reading and writing UTC timestamps to a database when the default timezone may change. E.g., this might happen in an application server if an application running in the same JRE changes the default timezone as follows:
    TimeZone.setDefault(TimeZone.getTimeZone("Europe/Zurich")); 
This is not possible as the applications are running in separated classloaders, you may say. I thought the same. And perhaps this must not happen because there is a spec that does forbid this (by the way, is there any?). However, it is possible to change the default timezone for all applications in the same node that way at least in Oracle Weblogic server. Try it out, if you dont't believe me.

Solution

The easiest solution to solve this problem I know is to create an own mapping type extending the standard Hibernate timestamp type org.hibernate.type.TimestampType:
package ch.meteoswiss.commons.hibernate;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.SimpleTimeZone;


/**
 * timestamp: A type that maps an SQL TIMESTAMP to a Java
 * java.util.Date or java.sql.Timestamp using UTC time zone.
 * @author Peter Keller
 */
public class UTCTimestampType extends org.hibernate.type.TimestampType {

    @Override
    public Object get(ResultSet rs, String name) throws SQLException {
        return rs.getTimestamp(name, createUTCCalendar());
    }

    /**
     * Creates UTC calendar. DO NOT USE a static calendar instance.
     * This may lead to concurrency problems with (at least) the Oracle DB
     * driver!
     * @return Calendar with UTC time zone.
     */
    private static Calendar createUTCCalendar() {
        final Calendar c = Calendar.getInstance();
        c.setTimeZone(new SimpleTimeZone(0, "UTC"));
        return c;
    }
    
    @Override
    public void set(PreparedStatement st, Object value, int index) throws SQLException {
        Timestamp ts;
        if (value instanceof Timestamp) {
            ts = (Timestamp) value;
        } else {
            ts = new Timestamp(((java.util.Date)value).getTime());
        }
        st.setTimestamp(index, ts, createUTCCalendar());
    }

}

Use it as follows with Java annotations (you could use the type class also in a XML configuration file):
import java.util.Date;
import org.hibernate.annotations.Type;

@Entity
@Table(name="...")
public class Data {

    private Date receptionTimeDt;
 
    @Type(type="ch.meteoswiss.commons.hibernate.UTCTimestampType")
    @Column(name="RECEPTION_TIME_DT", nullable=false)
    public Date getReceptionTimeDt() {
        return receptionTimeDt;
    }

}
Of course, this mapping class only works with Hibernate and is not standard JPA.

Tuesday, April 10, 2012

Hibernate returning NULL entities?

Ever got NULL entity entries from Hibernate? I did.

Having a table V_MEAS_SITE with following attributes:
MEAS_SITE_ID:     NUMBER(8)
INSTALLATION_ID  NUMBER(8)
NAME_TX          VARCHAR2(10)
...
The "V_" in the table name indicates that this is a Oracle view and not a table. This name follows our naming conventions. As the name of the view indicates, the view is holding all information about measurement sites (I actually work for the national weather service). This table is mapped to the following Java class:
@Entity(table="V_MEAS_SITE")
public class MeasurementSite {

    @Id
    @Column(name="MEAS_SITE_ID")
    private int measurementSiteId;

    @Column(name="INSTALLATION_ID")
    private int installationId;

    @Column(name="NAME_TX")
    private String name;

    // getter/setter are ommited for brevity

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((this.getMeasurementSiteId() == null) ? 0 : this.getMeasurementSiteId().hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof MeasurementSite)) {
            return false;
        }
        final MeasurementSite other = (MeasurementSite)obj;
        if (this.getInstallationId() == null) {
            if (other.getInstallationId() != null) {
                return false;
            }
        } else if (!this.getMeasurementSiteId().equals(other.getMeasurementSiteId())) {
            return false;
        }
        return true;
    }
}
Let's write an EJB session bean reading some data:
@Session
public class MeasurementSiteService implements MeasurementSiteLocal {
    
    @PersistenceContext(unitName="DefaultEntityManager")
    private EntityManager em;

    public List<MeasurementSite> findByName(String name) {
        return em.createQuery("select m from MeasurementSite where m.name like :name")
            .setParameter("name", name)
            .list();
    }

    // used for testing
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }

}
That's easy. OK, let's test this mapping with a JUnit test class:
    
public class MeasurementSiteServiceTest {
    private EntityManager em = ....; // set up the entity manager   
    
    @Test
    public void findByName() {
         final MeasurementSiteService s = new MeasurementSiteService();
         s.setEntityManager(em);

         final List<MeasurementSite> list = s.findByName("Z%");
         for (MeasurementSite m: list) {
             Assert.notNull(m); 
         }
    }
   
}

However, this test fails. There are indeed some NULL entity entries in the list. Hibernate must be broken!

Of course, Hibernate is not broken. Do you see what went wrong? I did after 3 hours of debugging. The view catches content of different tables and is therefore de-normalized. The primary key of the V_MEAS_SITE view is not MEAS_SITE_ID but INSTALLATION_ID! As a view has no explicit contraints there is no primary key constraint neither and therefore you have no explicit indications about the "logic" in the data. In this case it means that there are more than one entry for a given measurement site ID. And that's why Hibernate returned the NULL values. Of course it would have been nice, if Hibernate would have thrown some Exceptions to help a careless Java developer...

By the way, Oracle DB allows to query the DLL statement:
select dbms_metadata.get_ddl('VIEW', 'V_MEAS_SITE') as dll from dual

Sunday, April 8, 2012

Hibernate SQL Logging to Log4j

In my projects, for logging the SQL statements generated by Hibernate I always set the hibernate.sql_show property to true in the hibernate.cfg.xml file:
true
This logs the SQL statements to the STDOUT console. Only recently I realized that the logs can also be sent to Log4j. Of course this allows to log the SQL statements not only to the STDOUT but also to all possible Log4j appenders such as a file appender etc.. There are (at least) two ways to do this.

1) Setting the log level statically in the log4j.properties configuration file, e.g.:
log4j.logger.org.hibernate.SQL=DEBUG, FILE_APPENDER
log4j.additivity.org.hibernate.SQL=false
Setting additivity to false ensures that log messages aren't bubbled up to parent handlers.

2) Or setting the log level dynamically in Java, e.g.:
import org.apache.log4j.Logger;
import org.apache.log4j.Level;

...    

Logger log = Logger.getLogger("org.hibernate.SQL");
log.setLevel(Level.DEBUG);