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") publicT 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 Mapas = 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(); } } }