Custom Marshalling from Java to Flex via BlazeDS
My team are putting together a proof-of-concept Flex application sitting on top of a Spring-based server using BlazeDS.
We do quite a lot of date calculations, so we use Joda Time extensively throughout the code and in our domain model.
We're now trying to figure out how we can continue to use Joda Time in our DTOs that are sent back-and-forth with the Flex frontend via BlazeDS.
Our goal is to use the Actionscript 3 data type Date
on the Flex side and have that map to our use of Joda time's DateTime
, LocalDate
and LocalTime
types on the Java side.
We can solve the problem of converting Actionscript 3's Date
type when calling Java with a custom type marshaller plugged into BlazeDS, but this appears to only be invoked for the Flex->Java/BlazeDS direction and not for the Java/BlazeDS->Flex direction.
I'm now lo开发者_JAVA百科oking at custom PropertyProxy
implementations for BlazeDS, but this doesn't look like the right thing either.
The other idea was to implement Externalizable
on our Java DTOs, but this seems like too much work, especially when I look at the BlazeDS rival GraniteDS and that shows plugging in Joda Time support in their documentation with a simple type converter!
Any ideas appreciated.
OK - I've found the answer on my own. This involved writing my own AMF endpoint class + related serializing classes. I've gotta say that the guys over at http://flexblog.faratasystems.com have been a great source of inspiration on hacking BlazeDS.
This code should really be incorporated into BlazeDS itself or some Open Source extension project - it's so basic.
Channel Definition
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="ch.hedgesphere.core.blazeds.endpoint.AMFEndpoint"/>
<properties>
<serialization>
<type-marshaller>ch.hedgesphere.core.blazeds.translator.HedgesphereASTranslator</type-marshaller>
</serialization>
</properties>
</channel-definition>
Custom AMF Endpoint
package ch.hedgesphere.core.blazeds.endpoint;
import ch.hedgesphere.core.blazeds.serialization.Serializer;
public class AMFEndpoint extends flex.messaging.endpoints.AMFEndpoint {
@Override
protected String getSerializerClassName() {
return Serializer.class.getName();
}
}
Custom Serializer
package ch.hedgesphere.core.blazeds.serialization;
import java.io.OutputStream;
import flex.messaging.io.MessageIOConstants;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageSerializer;
import flex.messaging.io.amf.AmfTrace;
public class Serializer extends AmfMessageSerializer {
@Override
public void initialize(SerializationContext context, OutputStream out, AmfTrace trace)
{
amfOut = new AMF0Output(context);
amfOut.setOutputStream(out);
amfOut.setAvmPlus(version >= MessageIOConstants.AMF3);
debugTrace = trace;
isDebug = trace != null;
amfOut.setDebugTrace(debugTrace);
}
}
Custom AMF 0 Handling
package ch.hedgesphere.core.blazeds.serialization;
import flex.messaging.io.SerializationContext;
public class AMF0Output extends flex.messaging.io.amf.Amf0Output {
public AMF0Output(SerializationContext context) {
super(context);
}
@Override
protected void createAMF3Output()
{
avmPlusOutput = new AMF3Output(context);
avmPlusOutput.setOutputStream(out);
avmPlusOutput.setDebugTrace(trace);
}
}
Custom AMF 3 Handling
package ch.hedgesphere.core.blazeds.serialization;
import java.io.IOException;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import flex.messaging.io.SerializationContext;
public class AMF3Output extends flex.messaging.io.amf.Amf3Output {
public AMF3Output(SerializationContext context) {
super(context);
}
@Override
public void writeObject(Object value) throws IOException {
if(value instanceof DateTime) {
value = convertToDate((DateTime)value);
}
if(value instanceof LocalDate) {
value = convertToDate((LocalDate)value);
}
if(value instanceof LocalTime) {
value = convertToDate((LocalTime)value);
}
super.writeObject(value);
}
private Object convertToDate(LocalTime time) {
return time.toDateTimeToday().toDate();
}
private Object convertToDate(LocalDate date) {
return date.toDateMidnight().toDate();
}
private Object convertToDate(DateTime dateTime) {
return dateTime.toDate();
}
}
Custom Marshaller for Flex->Java Calling
package ch.hedgesphere.core.blazeds.translator;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import flex.messaging.io.amf.translator.ASTranslator;
public class HedgesphereASTranslator extends ASTranslator {
@SuppressWarnings({"rawtypes"})
@Override
public Object convert(Object originalValue, Class type) {
if( type.equals(DateTime.class)) {
return convertToDateTime(originalValue);
}
if( type.equals(LocalDate.class)) {
return convertToLocalDate(originalValue);
}
if( type.equals(LocalTime.class)) {
return convertToLocalTime(originalValue);
}
return super.convert(originalValue, type);
}
private Object convertToLocalTime(Object originalValue) {
return originalValue == null ? null : new LocalTime(originalValue);
}
private Object convertToLocalDate(Object originalValue) {
return originalValue == null ? null : new LocalDate(originalValue);
}
private Object convertToDateTime(Object originalValue) {
return originalValue == null ? null : new DateTime(originalValue);
}
@SuppressWarnings({"rawtypes"})
@Override
public Object createInstance(Object source, Class type) {
return super.createInstance(source, type);
}
}
For Java apps using the Spring-BlazeDS integration project from SpringSource, there is a much simpler way of handling this:
Write an implementation of GenericConverter that handles mapping ReadableDateTime to/from java.util.Date.
Create a subclass of AbstractAmfConversionServiceConfigProcessor and override configureConverters, adding your converter implementation to the registry.
Update your Spring configuration by creating an instance of your ConfigProcessor and wiring it up:
XML:
<flex:message-broker>
<flex:config-processor ref="customConfigProcessor"/>
</flex:message-broker>
More info here:
http://static.springsource.org/spring-flex/docs/1.5.x/reference/html/index.html#amf-custom-converters
Have you tried the custom marshallers approach outlined on this blog:
http://flexblog.faratasystems.com/index.php/custom-type-masrhaller-in-blazeds
精彩评论