But what happens if I don't want to serialize my model objects?
Suppose my entity has internalId, name and value fields and I only want to send the name and value fields.
According to BlazeDS developer guide I can solve this by setting fields as transient or implementing the Externalizable interface.
I can't set my internalId field as transient, nor do I want to implement Externalizable, which I would have to update every time I change my model object.
This can be solved using the PropertyProxyRegistry in the BlazeDS framework.
Let's define a new annotation - AMFTransient - this annotation will mark which fields should not be serialized.
package amf;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value={ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AmfTransient
{
}
Now write our own new BeanProxy
package amf;
import java.lang.reflect.Field;
import java.util.List;
import flex.messaging.io.BeanProxy;
@SuppressWarnings("serial")
public class TransientAwareBeanProxy extends BeanProxy
{
@SuppressWarnings("unchecked")
@Override
public List getPropertyNames( Object instance )
{
List propertyNames = super.getPropertyNames( instance );
// find which fields where marked as transient and remove them
Class c = instance.getClass();
for ( Field field : c.getDeclaredFields() )
{
if ( field.isAnnotationPresent( AmfTransient.class ) )
{
propertyNames.remove( field.getName() );
}
}
return propertyNames;
}
}
Finally we have to register our Proxy in bootstrap code (each application probably does this differently)
PropertyProxyRegistry.getRegistry().register( Object.class, new TransientAwareBeanProxy() );
Finally, let's put it all together with our AMF serializer and Main class
package amf;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;
public class AmfSerializer
{
public void toAmf( Object source, OutputStream os ) throws IOException
{
Amf3Output amf3Output = new Amf3Output( getSerializationContext() );
amf3Output.setOutputStream( os );
amf3Output.writeObject( source );
amf3Output.flush();
amf3Output.close();
}
@SuppressWarnings("unchecked")
public T fromAmf( byte[] amf ) throws ClassNotFoundException, IOException
{
InputStream bIn = new ByteArrayInputStream( amf );
Amf3Input amf3Input = new Amf3Input( getSerializationContext() );
amf3Input.setInputStream( bIn );
return (T) amf3Input.readObject();
}
private SerializationContext getSerializationContext()
{
// Let the framework create the object and set the thread local variable
SerializationContext context = SerializationContext.getSerializationContext();
// set flags on the serialization context here
return context;
}
} package amf;
import java.io.ByteArrayOutputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import flex.messaging.io.PropertyProxyRegistry;
public class Main
{
public static void main( String[] args ) throws java.lang.Exception
{
// by default send objects to the transient aware proxy
// you should set this in your bootstrap code
// ff you don't want certain classes to use this proxy set the BeanProxy explicitly for them
PropertyProxyRegistry.getRegistry().register( Object.class, new TransientAwareBeanProxy() );
B toAmf = new B( "bid" );
toAmf.as.put( "a1", new A( 11, "aid1" ) );
toAmf.as.put( "a2", new A( 12, "aid2" ) );
ByteArrayOutputStream bout = new ByteArrayOutputStream();
AmfSerializer serializer = new AmfSerializer();
serializer.toAmf( toAmf, bout );
byte[] amf = bout.toByteArray();
B fromAmf = serializer.fromAmf( amf );
System.out.println( "this should be null: " + fromAmf.d );
System.out.println( "this should be null: " + ((A)(fromAmf.as.values().toArray()[0])).id );
System.out.println( "this should be a number: " + ((A)(fromAmf.as.values().toArray()[0])).i );
}
public static class A {
@AmfTransient public String id;
private Integer i;
public A() {} // required for desrializing AMF
public A( Integer i, String str ) {
this.i = i;
this.id = str;
}
public void setI( Integer i ) {
this.i = i;
}
public Integer getI() {
return i;
}
public boolean equals( Object obj ) {
A tmp = (A)obj;
return id.equals( tmp.id );
}
public int hashCode() {
return id.hashCode();
}
}
public static class B {
@AmfTransient public Date d;
public String id;
public Map as = new HashMap();
public B() {} // required for desrializing AMF
public B( String id ) {
this.id = id;
d = new Date();
}
public boolean equals( Object obj ) {
B tmp = (B)obj;
return id.equals( tmp.id );
}
public int hashCode() {
return id.hashCode();
}
}
}