Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Monday, March 5, 2012

Setting up a multi tenant environment using the Spring Framework

In this post I'll describe how to set up a multi tenant application using the Spring Framework (I am using Spring 3).
I'll use a pure Java application, but the concepts work well in enterprise apps.

The requirements are simple:
  • Single application to serve all the tenants
  • Every tenant uses the same wiring, each tenant has different properties 
  • Have common properties and wiring that can be shared by all the tenants
    • Allow each tenant to override the common settings
The solution:
  • Create a Spring ApplicationContext for every tenant
    • Set the PropertyConfigurer in runtime
Let's see this in action:

public class MultiTenantSpringExample {
 
 private ClassPathXmlApplicationContext commonCtx; // use ClassPathXmlApplicationContext instead of ApplicationContext so we can destory them
 private List tenantContexts;

 public void init( List tenants ) {

  tenantContexts = new ArrayList( tenants.size() );
  // create a common application context, shared among all the tenants
  commonCtx = new ClassPathXmlApplicationContext( "/commonContext.xml" );
  // set up all the tenants
  for ( String tenant : tenants ) {
   // for each tenant create a Spring ApplicationContext
   ClassPathXmlApplicationContext tenantCtx = new ClassPathXmlApplicationContext();
   tenantCtx.setParent( commonCtx );
   tenantCtx.setConfigLocation( "/tenantContext.xml" );
   TenantPropertyPlaceholderConfigurer beanFactoryPostProcessor = new TenantPropertyPlaceholderConfigurer( tenant );
   tenantCtx.addBeanFactoryPostProcessor( beanFactoryPostProcessor );
   tenantCtx.refresh();
   tenantContexts.add( tenantCtx );
  }
 }
 
 public void destroy() {
  // destroy the tenant contexts
  for ( ClassPathXmlApplicationContext tenantContext : tenantContexts ) {
   tenantContext.destroy();
  }
  tenantContexts.clear();
  // destroy the common context
  commonCtx.destroy();
 }
 
 public static void main( String[] args ) {
  MultiTenantSpringExample example = new MultiTenantSpringExample();
  example.init( Arrays.asList( "a", "b" ) );
  example.destroy();
 }
}

On line 18 I create the common wiring, that is shared among tenants, and set it as the parent for the tenant application context on line 23.
On line 25-27 I inject the PropertyPlaceholderConfigurer, which is created differently for every tenant.

public class TenantPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
 public TenantPropertyPlaceholderConfigurer( String tenant ) {
  super();
  setIgnoreResourceNotFound( true ); // this makes the common file and tenant file optional
  // prepare the default properties
  String defaultPropertiesResourcePath = "/global.properties";

  Resource defaultPropertiesResource = new ClassPathResource( defaultPropertiesResourcePath );
  // prepare tenant properties
  String tenantPropertiesResourcePath = '/' + tenant + ".properties";
  Resource tenantPropertiesResource = new ClassPathResource( tenantPropertiesResourcePath );
  // set the locations
  Resource[] locations = new Resource[] { defaultPropertiesResource, tenantPropertiesResource };
  setLocations( locations );
 }
}


The TenantPropertyPlaceholderConfigurer uses classpath resources, using a common properties file, shared for all the tenants, and a per-tenant properties file.

To complete the example I created a simple class, A, holding an int, and printing the int in the print() method.
public class A {
 private int i;
 
 public void setI( int i ) {
  this.i = i;
 }
 
 public int getI() {
  return i;
 }
 
 public void print() {
  System.out.println( "Example property: " + i );
 }
}


And the resource files: commonContext.xml, empty in the example but can be used for sharing wiring among tenants
<beans>
</beans>


tenantContext.xml, creates an instance of A for every tenant, each one with different property values, and calls the print() method after constructing the object to print the value.
<beans> 
 <bean class="A" id="a" init-method="print">
  <property name="i" value="${tenant-i}">
 </property></bean>
</beans>


And two matching properties files: a.properties
tenant-i = 1

b.properties
tenant-i = 2

When executing the 'main' method the program outputs "1" for tenant 'a' and "2" for tenant 'b'

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!

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.

Wednesday, May 12, 2010

Creating a log4j logger in an Eclipse template

private static Logger logger = Logger.getLogger( MyClass.class );
Are you tired of writing the same line of code over and over again?

No more!


Using the Eclipse templates you can generate this code easily.
Create the template by clicking Window -> Preferences -> Java -> Editor -> Templates -> New

Enter the template name
logger


Enter the pattern
private static Logger logger = Logger.getLogger( ${enclosing_type}.class );
${:import(org.apache.log4j.Logger)}
This pattern also takes care of the import statement.


Creating an Eclipse template




Now see it in action:




Sunday, March 7, 2010

Serializing Google Protocol Buffer to Blazeds AMF

What are these technologies and how can I use them?
Protocol buffers are a flexible, efficient, automated mechanism for serializing structured data.
You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
read more

Action Message Format (AMF) is a binary format used to serialize ActionScript objects. It is used primarily to exchange data between an Adobe Flash application and a remote service, usually over the internet.
read more

BlazeDS is the server-based Java remoting and web messaging technology.
read more

OK. I read this and I am intrigued. How can I use these technologies together?

You start by defining your Protocol Buffer data structure and generate the matching Java classes.
Now you need to serialize your data structure to AMF using BlazeDS. Simple as that.

But wait, I read here that
For Java objects that BlazeDS does not handle implicitly, values found in public bean properties with get/set methods and public variables are sent to the client as properties on an Object. Private properties, constants, static properties, and read-only properties, and so on, are not serialized.
My generated data structures don't have setters, they use the Builder pattern. How can I tell BlazeDS what to serialize? I don't want to hack the code generation process and add implement my data structures as Externalizable.

You need to register your own PropertyProxy in the PropertyProxyRegistry.

I wrote a property proxy to help you out:
package amf;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import com.google.protobuf.Descriptors;
import com.google.protobuf.GeneratedMessage;
import com.google.protobuf.Message;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.JavaType;

import flex.messaging.io.ArrayCollection;
import flex.messaging.io.ArrayList;
import flex.messaging.io.BeanProxy;

/**
 * This is an implementation of the blazeds PropertyProxy for Google Protocol Buffers.
 */
@SuppressWarnings("unchecked")
public class GoogleProtocolBufferProxy extends BeanProxy
{
 private static ConcurrentHashMap propertiesCache = new ConcurrentHashMap();

private static final long serialVersionUID = -2175023450177522134L;

/**
* @see flex.messaging.io.PropertyProxy#getPropertyNames(java.lang.Object)
*/
public List getPropertyNames( Object instance )
{
List propertyNames = propertiesCache.get( instance.getClass() );
if ( propertyNames != null )
{
return propertyNames;
}

if ( instance instanceof Descriptors.EnumValueDescriptor )
{
Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor)instance;
String enumName = enumValueDescriptor.getType().getName();
return Collections.singletonList( enumName ); // an enum descriptor has a single enum value
}
else if ( instance instanceof GeneratedMessage )
{
// use GPB reflection to figure out the proprety names
GeneratedMessage message = (GeneratedMessage)instance;
List fields = message.getDescriptorForType().getFields();
propertyNames = new ArrayList( fields.size() );
for ( FieldDescriptor field : fields )
{
propertyNames.add( field.getName() );
}

propertiesCache.put( instance.getClass(), propertyNames );

return propertyNames;
}
else
{
throw new IllegalArgumentException( "Can only serialize GPB objects, not " + instance.getClass() );
}

}

/**
* @see flex.messaging.io.BeanProxy#getValue(java.lang.Object, java.lang.String)
*/
public Object getValue( Object instance, String propertyName )
{
if ( instance instanceof Descriptors.EnumValueDescriptor )
{
Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor)instance;
String enumValue = enumValueDescriptor.getName();
return enumValue;
}
if ( instance instanceof GeneratedMessage )
{
// use GPB reflection to figure out the proprety names
GeneratedMessage message = (GeneratedMessage)instance;
FieldDescriptor field = message.getDescriptorForType().findFieldByName( propertyName );
Object value = message.getField( field );
return value;
}
else
{
throw new IllegalArgumentException( "Can only serialize GPB objects, not " + instance.getClass() );
}
}

/**
* @see flex.messaging.io.AbstractProxy#createInstance(java.lang.String)
*/
public Object createInstance( String className )
{
if ( Descriptors.EnumValueDescriptor.class.getName().equals( className ) )
{
return new StringBuilder(); // use a StringBuilder to hold the value of the enum
}
try
{
Class clz = Class.forName( className );
Method newBuilderMethod = clz.getDeclaredMethod( "newBuilder", new Class[] {} );
newBuilderMethod.setAccessible( true );
Message.Builder builder = (Message.Builder)newBuilderMethod.invoke( null );
return builder;
}
catch ( ClassNotFoundException e )
{
throw new IllegalArgumentException( e );
}
catch ( NoSuchMethodException e )
{
throw new IllegalArgumentException( e );
}
catch ( InvocationTargetException e )
{
throw new IllegalArgumentException( e );
}
catch ( IllegalAccessException e )
{
throw new IllegalArgumentException( e );
}
catch ( SecurityException e )
{
throw new RuntimeException( e );
}
}

/**
* @see flex.messaging.io.BeanProxy#setValue(java.lang.Object, java.lang.String, java.lang.Object)
*/
public void setValue( Object instance, String propertyName, Object value )
{
if ( instance instanceof StringBuilder ) // this instance is an enum
{
StringBuilder builder = (StringBuilder)instance;
builder.append( value ); // save the enum value
}
else if ( instance instanceof Message.Builder ) // note the input is a message builder, not a message
{
Message.Builder builder = (Message.Builder)instance;
FieldDescriptor field = builder.getDescriptorForType().findFieldByName( propertyName );
if ( value instanceof Double )
{
Double dbl = (Double)value;
Object realValue = dbl;
// ActionScript Number is desrialized to Java Double 
// Java Double, Long, Float, are serialized to ActionScript Number
// here is the place to desrialized wisely
if ( field.getJavaType().equals( JavaType.LONG ) )
{
realValue = new Long( dbl.longValue() );
}
else if ( field.getJavaType().equals( JavaType.FLOAT ) )
{
realValue = new Float( dbl.floatValue() );
}
builder.setField( field, realValue );
}
else if ( field.getJavaType().equals( JavaType.ENUM ) && value instanceof StringBuilder ) // if this is an enum for which we saved the value set the correct enum value
{
EnumValueDescriptor enumValueDescriptor = field.getEnumType().findValueByName( String.valueOf( value ) );
builder.setField( field, enumValueDescriptor );
}
else if ( field.getJavaType().equals( JavaType.ENUM ) && value instanceof ArrayCollection ) // if this is an enum for which we saved the value set the correct enum value
{
ArrayCollection enumCollection = (ArrayCollection)value;
Iterator enumValuesIterator = enumCollection.iterator();
while ( enumValuesIterator.hasNext() )
{
StringBuilder enumValueBuilder = (StringBuilder)enumValuesIterator.next();
EnumValueDescriptor enumValueDescriptor = field.getEnumType().findValueByName( String.valueOf( enumValueBuilder ) );
builder.addRepeatedField( field, enumValueDescriptor );
}
}
else
{
builder.setField( field, value );
}
}
else
{
throw new IllegalArgumentException( "Can only serialize GPB objects, not " + instance.getClass() );
}
}

/**
* @see flex.messaging.io.AbstractProxy#instanceComplete(java.lang.Object)
*/
public Object instanceComplete( Object instance )
{
if ( instance instanceof StringBuilder )
{
return instance; // return the same string builder which holds the enum value
}
else if ( instance instanceof Message.Builder ) // note the input is a message builder, not a message
{
Message.Builder builder = (Message.Builder)instance;
return builder.build();
}
else
{
throw new IllegalArgumentException( "Can only serialize GPB objects, not " + instance.getClass() );
}
}

}

In your unit test don't forget to setup the registry. in JUnit it looks like this

@BeforeClass
public static void setup()
{
 PropertyProxyRegistry.getRegistry().register( GeneratedMessage.class, new GoogleProtocolBufferProxy() );
 PropertyProxyRegistry.getRegistry().register( Descriptors.EnumValueDescriptor.class, new GoogleProtocolBufferProxy() );
}

Happy serializing!

Tuesday, November 3, 2009

How to run an external processes from a Java program

A QA engineers recently asked for my help invoking an external process from a Java program.
I told him "use java.lang.Runtime.exec" (link opens in a new tab).
It's been a long time since I used it, the opportunity rarely rises.
He struggled with the API, and couldn't understand how to write the output of the process to his Java console.
So I took a longer look in the API and sent this back as an example:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class ProcessRunner {

public static void main( String[] args ) throws Exception {
execute( "cmd.exe /c dir" );
execute( "cmd.exe /c java1" );
execute( "java -version" );
execute( "java -versiond" );
}

public static void execute( String cmd ) throws Exception {
Process p = Runtime.getRuntime().exec( cmd );

OutputReader stdout = new OutputReader( p.getInputStream() );
Thread t1 = new Thread( stdout );
t1.start();

OutputReader stderr = new OutputReader( p.getErrorStream() );
Thread t2 = new Thread( stderr );
t2.start();

int status = p.waitFor();
System.out.println( "status " + status );
System.out.println();
}

private static class OutputReader implements Runnable {
InputStream is;
public OutputReader( InputStream is ) {
this.is = is;
}

public void run() {
BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
try {
String line = null;
while ( ( line = reader.readLine() ) != null ) {
System.out.println( line );
}
} catch ( IOException e ) {
e.printStackTrace();
}
try
{
reader.close();
}
catch ( IOException e )
{
e.printStackTrace();
}
}
}
}

Looks cumbersome, but then many mundane operations in Java feel the same (e.g. read from a file).