Tuesday, February 21, 2012

Perforce and diff emails

After working with Subversion, and getting used to the colorful post-commit diff email, I wanted to set up a similar setting in Perforce.
Unfortunately I couldn't find post-commit hooks in Perforce.
So I came up with the following steps to generate the diff on the client side:
  1. Create a "Review" trigger in Perforce for the branches you want to monitor.
    In the visual Perforce client click "Connection" > "Edit current user" > "Reviews" tab, now right click and "Include" every branch you want to be notified about.
    This sends an email about every changelist submitted to Perforce, without specifying the changelist content
  2. Get the jar, or source code, that creates the HTML diff.

    I am aware that there are tools to convert diff 2 HTML, but since my team is using Java (thus they have a JRE), mostly on Windows (so no Python), I wanted a Java convertor.
     
  3. Use a VBA script in Outlook to convert the triggered email to a diff of the changelist content.
    The script fires up the local Perforce client and uses the command "p4 describe " to create the diff, uses the Java class to convert the diff to HTML and replaces the email body with the HTML diff.
    Open the script below in a text editor:

    Sub P4Diff(MyMail As MailItem)
    
        Dim strSplit As Variant
        Dim changelist As String
        Dim sOutput As String
        Dim sOutputErr As String
        Dim sP4Cmd As String
        Dim sJavaCmd As String
        Dim sJarPath As String
        Dim sP4Port As String
        Dim sP4User As String
        Dim sP4Password As String
    
        ' error handling directive
        On Error GoTo errMyErrorHandler
    
        ' Variables that need to be set per environment
        sP4Cmd = "C:\Progra~1\Perforce\p4.exe"
        sJavaCmd = "C:\Java\jre6\bin\java.exe"
     sJarPath = "C:\SomePath\p4diff.jar"
     sP4Port = "yourperforce:1667"
        sP4User = "youruser"
        sP4Password = "yourpassword"
        
        ' parse the changelist from the email subject
        strSplit = Split(MyMail.Subject, " ")
        changelist = strSplit(2)
        
        ' create a shell
        Set wshShell = VBA.CreateObject("WScript.Shell")
        ' set the p4 environment variables
        Set processEnvVars = wshShell.Environment("PROCESS")
        processEnvVars("P4PORT") = sP4Port
        processEnvVars("P4USER") = sP4User
        processEnvVars("P4PASSWD") = sP4Password
        ' execute the diff and wait for it to finish
        Set oExec = wshShell.Exec("%COMSPEC% /c " & sP4Cmd & " describe " & changelist & " | " & sJavaCmd & " -jar " & sJarPath)
        Do While oExec.Status = WshRunning
            If oExec.StdOut.AtEndOfStream = 0 Then
                sOutput = sOutput & oExec.StdOut.ReadLine()
            End If
        Loop
    
        If oExec.StdOut.AtEndOfStream = 0 Then
            sOutput = sOutput & oExec.StdOut.ReadLine()
        End If
        
        ' Read the diff result and set in the email body
        sOutputErr = oExec.StdErr.ReadAll()
        MyMail.HTMLBody = sOutput & sOutputErr
        MyMail.Save
    
        Exit Sub
    
    errMyErrorHandler:
      MsgBox Err.Description, _
        vbExclamation + vbOKCancel, _
        "Error: " & CStr(Err.Number)
    
    End Sub
    

    Change the script variables to match your environment.

    Install the VBA script:
    1.  In Outlook click "Tools" > "Macro" > "Visual Basic Editor"
    2. Open "ThisOutlookSession"
    3. Paste the edited script from your text editor into the VB editor and save
    4. Close the VBA editor and text editor

    Change the Outlook Macro security so it will be able to run the script

  4. Create a rule in Outlook to run the script when the email subject contains "PERFORCE change".
    For some reason this doesn't work well if you run the script AND move the email to a different folder

Sunday, November 20, 2011

Devoxx 2011

I attended the Devoxx 2011 conference in Antwerp, Belgium (thank you RSA Security).
This is the second large conference I've attended, the first being Java ONE in 2007, and I had a great time.

Stephan Janssenn and the Devoxx team did an excellent job organizing the conference.
The conference was packed with over 3,500 participants, 95% men, situated in Metropolis Antwerp Business Center.
The lecture halls were actually cinema theatres. Even though I haven't seen a movie in them (I missed the "Tintin 3D" feature film) they are hands-down the best cinema theatres I've been to, with more than enough leg room, extremely comfortable seats and arm rests which you don't need to fight over :)
Between lectures the screens showed the twitter wall, which was a brilliant idea I liked very much.



Every participant got a wrist band, very similar to the one I got a few months ago from the maternity ward when my son was born.
The thing is, it has to remain on your wrist until the conference is over. I can see why some people found this annoying but it actually worked in my favor, while sitting in the lobby of the hotel waiting for a taxi to take me to the conference the guy that sat next to me also wore the wristband so we ended up talking and sharing the ride.

Day 1: The Java SE keynote by Henrik Stahl was good. There was no big announcement, but I liked the message "Java will always be there for you".
I got a little bored in Cameron Purdy's Java EE keynote, where he promoted Weblogic and Oracle servers and left before it ended.
The "Play 2.0" talk was very good. I was impressed with the creativeness of Play 1.0 when I started using it, but these guys don't rest. They added innovative features to Play 2.0 continuing to make web development easier and faster.
"7 reasons to love JBoss AS 7" sounded promising but lacked technical details. It felt more like a marketing pitch. But it sounds promising and I'll need to check it out.
I then moved to the "JRuby enhancing Java developers' lives" talk, but it started off showing how to write web applications in Ruby, so I left and joined the "PhoneGap" talk, which was very amusing. I even managed to pick up a thing or two about mobile development, which is something I haven't tried (yet).
Next was "NoSQL for Java developers" which showed how a restaurant directory application would look like in an RDBMS (MySQL), a Key-Value store (Redis), a document store (mongoDB) and a multicolumn DB (Cassandra).
I think it's wrong to demonstrate all of them using the same use case. Each DB was built to solve a specific set of problems. Assuming the goal of the lecture was to show how non-relational DBs defer from relational DBs, then to show the strengths of every DB you need a different use case. E.g. show how your web application sessions would work with a KV store as opposed to RDBMS, how document stores have flexible schemas as opposed to rigid schemas in an RDBMS, and how queries performance differs as you scale out.
My day ended with an excellent talk from Brian Goetz about "Language/Library co-evolution in Java SE 8" which focused on Lambda and Closures, and how the existing JDK libraries might be enhanced to use these new features.

Day 2: The keynote from Tim Bray convinced me I should write mobile apps. I'm pretty sure I'm not gonna save the world, but I'm sure to it will be fun.
In the "Introducing Akka" talk I understood the concept of "Actors" and it sure sounds like a good tool to have in your developer toolbox.
"JMS 2.0" showed that there's not much to change in the JMS spec. If the spec wasn't 10 years old I would name the new spec version 1.2 :)
I then went to "Why we shouldn't target women", which about the state of female developers in the IT world. I was surprised to hear that in France and the UK only 15% of CS graduates are women. It was an interesting discussion without any conclusions.
"Java Posse live" was a comic relief. I got a beer and sat on the stairs to watch the show. I'm not sure this Posse podcast will provide much value to the listeners :)
"Having fun with Java and Home Automation" was a peek at the future. You'll be able to control and follow your house on twitter.
I ended the day with the "Code Generation" talk which got me thinking about the need for code generation in enterprise projects in the annotations era, see what Play! does in this area. I'm not sure it's very useful these days. I did learn a bunch of new stuff from the Xtext, Xtend and Spring Roo code generation demos.

Day 3: The final day began with a technical discussion panel which was interesting mostly because of the cynical remarks from Oracle and Google people.
I decided I had to go to one HTML5 talk so I entered "HTML5 Game Development" which was very good. Animation always make me feel I should brush up my Mathematics.
The last talk was about "Shazam", which is an app that identifies songs by hearing them. Roy van Rijn did a fantastic job explaining how he built a similar application in Java, including a good explanation about Fourier Transformation using a yellow stick. His talk ended with a lively discussion about software patents and violation after getting intimidating emails from Landmark (the company holding the Shazam patents).

Notes for next year:
  • Don't stay at Novotel Antwerp. It has no public transportation and no entertainment in its vicinity.
  • Set aside more time to see Antwerp. I have no idea what the city looks like.
  • Get a larger suitcase. My small trolley almost didn't have room for all the marketing items handed out at the booths.
See you again next year!

Sunday, February 6, 2011

Autoboxing puzzler

While performing code review I came across the following code (simplified for this post)

private void box(Long l) {
 if (l.equals(1)) {
  System.out.println("eq");
 }
 else {
  System.out.println("not eq");
 }
}

What will be printed when we call box(new Long(1))?
Can you spot the bug?

The condition l.equals(1) will always evaluate to false.

This happens due to the autoboxing specification and the implementation of the Long.equals() method.
According to the specification if the primitive value (1 in this case) is an int it is converted to a reference of class Integer.
The Long.equals() method starts with type comparison using the instanceof operator, which returns false for an instance of type Integer.

A possible solution is to compare the value to a reference of class Long value using if (l.equals(1L)), which boxes the value 1L to a Long.

Wednesday, November 3, 2010

Quickest JPA test-setup ever!

Have you ever wanted to test some JPA functionality? Were you discouraged to do so because of the long setup process?
I wanted to test some JPA functionality without the hassle of setting up a DB and configuring XML files, A simple, short piece of code that I was able to run immediately.

This can be done very easily with in-memory HSQLDB and configuration using annotations. You only need Hibernate and HSQLDB on your classpath.
Take a look:

package jpa;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.ejb.Ejb3Configuration;

@Entity
public class SimpleEntityTest
{
    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    public SimpleEntityTest()
    {
    }
 
    public Long getId()
    {
        return id;
    }
    
    public void setId( Long id )
    {
        this.id = id;
    }
    
    public String getName()
    {
        return name;
    }
    
    public void setName( String name )
    {
        this.name = name;
    }
    
    @SuppressWarnings("unchecked")
    public static void main( String[] args ) throws Exception
    {
        Ejb3Configuration cfg = new Ejb3Configuration();
        cfg.setProperty( "hibernate.connection.driver_class", "org.hsqldb.jdbcDriver" );
        cfg.setProperty( "hibernate.connection.username", "sa" );
        cfg.setProperty( "hibernate.connection.password", "" );
        cfg.setProperty( "hibernate.connection.url", "jdbc:hsqldb:mem:test" );
        cfg.setProperty( "hibernate.dialect", "org.hibernate.dialect.HSQLDialect" );
        cfg.setProperty("hibernate.hbm2ddl.auto", "create-drop" );
        cfg.addAnnotatedClass( SimpleEntityTest.class );
        EntityManager em = cfg.buildEntityManagerFactory().createEntityManager();
        
        System.out.println( "create the entity" );
        EntityTransaction trx = em.getTransaction();
        trx.begin();
        SimpleEntityTest ent = new SimpleEntityTest();
        ent.setName( "test" );

        em.persist( ent );
        trx.commit();
     
        System.out.println( "change the entity" );
        trx = em.getTransaction();
        trx.begin();
        List entlist = em.createQuery( "from " + SimpleEntityTest.class.getName() ).getResultList();
        for ( Object object : entlist )
        {
            SimpleEntityTest ent1 = (SimpleEntityTest)object;
            ent1.setName( "other" );
        }
        trx.commit();

        // check with a connection that is not managed by Hibernate
        Connection con = DriverManager.getConnection( "jdbc:hsqldb:mem:test", "sa", "" );
        Statement stmt = con.createStatement();
        ResultSet rs = stmt.executeQuery( "select name from " + SimpleEntityTest.class.getSimpleName() );
        while ( rs.next() )
        {
            System.out.println( "Entity name: " + rs.getString( 1 ) );
        }
        rs.close();
        stmt.close();
        con.close();
   }
}

Even if you don't have anything installed you can be up and running in minutes using Maven (assuming you have Maven installed and working).
Follow 4 simple steps:
  • Create a Maven project: mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=jpa -DartifactId=jpa-test
  • Copy the above class to jpa-test/src/main/java/jpa
  • replace the pom.xml content with
    
     4.0.0
    
     jpa
     jpa-test
     1.0-SNAPSHOT
     jar
    
     jpa-test
     http://maven.apache.org
    
     
      UTF-8
     
    
     
      
       jboss.org
       Jboss Repo ORG
       default
       http://repository.jboss.org/maven2
       
        false
       
       
        true
       
      
     
    
     
      
       junit
       junit
       3.8.1
       test
      
      
       org.hibernate
       hibernate-core
       3.5.1-Final
      
      
       org.hibernate
       hibernate-annotations
       3.5.1-Final
      
      
       org.hibernate
       hibernate-entitymanager
       3.5.1-Final
      
      
       org.slf4j
       slf4j-api
       1.6.1
      
      
         org.slf4j
         slf4j-nop
         1.6.1
      
      
       hsqldb
       hsqldb
       1.8.0.10
      
     
    
     
      
       
        org.apache.maven.plugins
        maven-compiler-plugin
        
         1.6
         1.6
        
       
      
     
    
    
    
  • Build using Maven or your favorite IDE

Thursday, July 8, 2010

Running Play! framework JPA from a command line process

If you are using the Play! framework and wanted to run a command line process that uses the Play! JPA enhancements this post is for you.
It requires some classloader magic and is based on reading the source code of Play 1.0.x, I am not sure if this will be supported in future versions.

You need your Main class to prepare the Play! framework classes, set the classloader and load your "real" Main class using the Play! classloader.
This is how it's done:

public class LoaderMain
{
 public static void main( String[] args ) throws Exception
 {
        File root = new File(System.getProperty("application.path"));
        Play.init(root, System.getProperty("play.id", ""));
        Thread.currentThread().setContextClassLoader( Play.classloader );
        Class c = Play.classloader.loadClass( "com.incapsula.batch.PlayLoaderMain" );
        Method m = c.getMethod( "run" );
        m.invoke( c.newInstance() );
 }
}

Now since you are not invoking web services you need to execute the framework methods by yourself, e.g. initializing the plugins and openning a JPA transaction.
You'll have to get yourself familiar with the Play! framework source code for any dependencies your process has on the Play! frameowrk.

public class PlayLoaderMain
{
 public void run() throws Exception
 {
  new DBPlugin().onApplicationStart();
  new JPAPlugin().onApplicationStart();

  JPAPlugin.startTx( true );
  Fixtures.load( "initial-data.yml" );
  System.out.println( User.findAll() );
  JPAPlugin.closeTx( false );
 }
}

There are a few things to notice:
  • You need the Play jars in your classpath (play.jar, framework/lib jar files and module/lib jar files for every module you are using)
  • You need to point the "application.path" JVM property to your Play application
  • You need to initialize different Play! plugins if you need them (e.g. if you are using the Play! templates in your process you also need to initialize the MessagesPlugin)

Tuesday, July 6, 2010

JAXB, Sun and how to marshal a CDATA element

According to https://jaxb.dev.java.net/faq/index.html#marshalling_cdata there is no direct support in marshalling CDATA blocks, this is vendor specific.
I'll describe how this is done when using the built-in Sun implementation by an example.


Suppose this is my JAXB annotated class:
package org.oded;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Item
{
 @XmlAttribute public int id;
 
 public String text;
}

Next I run the Main class:
package org.oded;

import java.io.*;

import javax.xml.bind.*;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;

public class Main
{
 public static void main( String[] args ) throws Exception
 {
  Item i1 = new Item();
  i1.text = "hello";
  i1.id = 1;
  
  Item i2 = new Item();
  i2.text = "";
  i2.id = 2;
    
  Marshaller m = JAXBContext.newInstance( Item.class ).createMarshaller();
  
  m.marshal( i1, new OutputStreamWriter( System.out ) );
  System.out.println();
  m.marshal( i2, new OutputStreamWriter( System.out ) );
 }
}

The output is:
hello
<code><helloWorld/></code>

Now suppose I want to wrap the XML value of text in a CDATA element and avoid the escaping, I need to add specify an Adapter for text to surround the value in a CDATA element in the following way:

package org.oded;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Item
{
 @XmlAttribute public int id;
 
 @XmlJavaTypeAdapter(value=Adapter.class) 
 public String text;
 
 private static class Adapter extends XmlAdapter
 {

  @Override
  public String marshal( String v ) throws Exception
  {
   return "<![CDATA[" + v + "]]>";
  }

  @Override
  public String unmarshal( String v ) throws Exception
  {
   return v;
  }
  
 }
}

Now tell the Marshaller, in a Sun-specific way, not to escape the value of text:
package org.oded;

import java.io.*;

import javax.xml.bind.*;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;

public class Main
{
 public static void main( String[] args ) throws Exception
 {
  Item i1 = new Item();
  i1.text = "hello";
  i1.id = 1;
  
  Item i2 = new Item();
  i2.text = "";
  i2.id = 2;
    
  Marshaller m = JAXBContext.newInstance( Item.class ).createMarshaller();
  m.setProperty( "com.sun.xml.internal.bind.characterEscapeHandler", new CharacterEscapeHandler() {
   @Override
   public void escape( char[] ac, int i, int j, boolean flag, Writer writer ) throws IOException
   {
    // do not escape
    writer.write( ac, i, j );
   }
  });
  
  m.marshal( i1, new OutputStreamWriter( System.out ) );
  System.out.println();
  m.marshal( i2, new OutputStreamWriter( System.out ) );
 }
}

Now the output is:
<![CDATA[hello]]>
<![CDATA[]]>

Thursday, May 27, 2010

Using Play! precompiled classes

If you want to run Play! in PROD mode, after precompiling your code, you probably noticed that Play.usePrecompiled is set to false and doesn't change. This causes your code to be compiled when the PROD server starts.
I read somewhere that in the GAE module it's set to true.

Well, what if I want to deploy my Play! application without the sources and use my precompiled classes?
This only requires setting Play.usePrecompiled to true, writing a new module for this seems too much.
I thought the plugins architecture was the way to go, setting this in the onLoad() method, but without setting usePrecompiled Play! can not invoke my plugin.

I found an acceptable hack.
One of the first thing Play! does in the init() method is invoke initStaticStuff(), this method searches for files named "play.static" in the classpath, each line in the files must be a Java class name, and Class.forName() is invoked for each such class.
This doesn't do much but I can set the usePrecompiled value to true in a static initializer block.
I use a JVM property ("usePrecompile") to control the use of the precompiled classes.
I wrote a new class
package org.oded;

import play.Play;

public class Bootstrap {
 static {
  Play.usePrecompiled = Boolean.getBoolean( "usePrecompile" ) && Play.getFile( "precompiled" ).exists();
 }
}

I added a "play.static" file to my conf directory, the file has one line "org.oded.Bootstrap".

In addition I updated my build script, the one that invokes "play precompile", also creates a jar file in myserver/lib with only the Bootstrap file in it.

This is probably not the use that was intended for this hook, but it works.