2

I am trying to send a date from flex side to java using BlazeDS

field type in flex

private var _scheduleDate:Date;

I have initialized it with new Date() , and if alert it, correct date and time is displayed as on my system. now when the object is sent to java. the date get changed.

Please note that my flex application and java (JBoss server is running on same machine). If i independently print a date on java side initialized with new Date(), it also display correct date as of the system.

Now conversion part: on flex it was

25/04/2013 12:30 PM (as Alert displayed)

when i print this passed date(at java side), its

25/04/2013 02:30 PM (2 hours difference)

I have read many blogs etc for the solution, they refer it a time zone issue, but i didnt get it, if client and server are both on single system, how could time zone issue causes this problem.

Alert(flex) and println(java) with new Date() display correct date as of the system, so how that timezone issue comes in. only i can think of is, BlazeDS is causing this issue.

Here in this link they have refered some custom marshling for blazeDS but it went above my head Custom Marshalling from Java to Flex via BlazeDS

Right now i have only one solution for this problem to send date in String as plain text instead of Date object and convert String back to Date object on java side.

Is there any better solution or if there is any issue with my understanding can someone please point out that.

Thanks,

Community
  • 1
  • 1
PHP Avenger
  • 1,744
  • 6
  • 37
  • 66
  • Struggling same problem for last few days – Timofei Davydik Apr 25 '13 at 08:33
  • Is the date being converted to UTC when passing from client to server, but somehow not getting converted back? – JeffryHouser Apr 25 '13 at 15:58
  • I am not sure but when i print received date in java the string value includes "CDT" which i googled and its (Central Daylight Time) otherwise if i print new Date() on java or flex both display it as GMT+5 in string value – PHP Avenger Apr 25 '13 at 16:35

2 Answers2

2

I suggest the following solution.

First, send client timezone offset and store it as a session attribute.

Flex

ro.setClientTimezoneOffset(-new Date().getTimezoneOffset() * 60 * 1000);

Java

public void setClientTimezoneOffset(Long t) {
    FlexContext.getFlexSession().setAttribute("clientTimezoneOffset", t);
}

Create our own endpoint class extending flex.messaging.endpoints.AMFEndpoint and point it at channel definition:

services-config.xml

<channel-definition id="my-amf"
            class="mx.messaging.channels.AMFChannel">
    <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" 
                class="com.package.AMFEndpoint"/>
</channel-definition>

AMFEndpoint.java

package com.package;

public class AMFEndpoint extends flex.messaging.endpoints.AMFEndpoint {

    @Override
    protected String getSerializerClassName() {
        return Serializer.class.getName();
    }

    @Override
    protected String getDeserializerClassName() {
        return Deserializer.class.getName();
    }
}

Extend amf serializer, deserializer, amf input/output:

Serializer.java

package com.package;

import flex.messaging.io.MessageIOConstants;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageSerializer;
import flex.messaging.io.amf.AmfTrace;

import java.io.OutputStream;

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);
    }
}

Deserializer.java

package com.package;

import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageDeserializer;
import flex.messaging.io.amf.AmfTrace;

import java.io.InputStream;

public class Deserializer extends AmfMessageDeserializer {
    @Override
    public void initialize(SerializationContext context, InputStream in, AmfTrace trace) {
        amfIn = new AMF0Input(context);
        amfIn.setInputStream(in);

        debugTrace = trace;
        isDebug = debugTrace != null;
        amfIn.setDebugTrace(debugTrace);
    }
}

AMF0Input.java

package com.package;

import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf0Input;

import java.io.IOException;

public class AMF0Input extends Amf0Input {
    public AMF0Input(SerializationContext context) {
        super(context);
    }

    @Override
    public Object readObject() throws ClassNotFoundException, IOException {
        if (avmPlusInput == null) {
            avmPlusInput = new AMF3Input(context);
            avmPlusInput.setDebugTrace(trace);
            avmPlusInput.setInputStream(in);
        }
        return super.readObject();
    }
}

AMF0Output.java

package com.package;    

import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf0Output;

public class AMF0Output extends Amf0Output {
    public AMF0Output(SerializationContext context) {
        super(context);
    }

    @Override
    protected void createAMF3Output()
    {
        avmPlusOutput = new AMF3Output(context);
        avmPlusOutput.setOutputStream(out);
        avmPlusOutput.setDebugTrace(trace);
    }
}

And finally you extend amf 3 input/output classes, where date is serialized and deserialized. You apply offset difference.

AMF3Input.java

package com.package;

import flex.messaging.FlexContext;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;

import java.io.IOException;
import java.util.Date;

public class AMF3Input extends Amf3Input {
    @Override
    protected Date readDate() throws IOException {
        Date d = super.readDate();
        if (d != null) {
            Long clientOffset = (Long) FlexContext.getFlexSession().getAttribute("clientTimezoneOffset");
            Long serverOffset = (Long) (-d.getTimezoneOffset() * 60L * 1000);
            d.setTime(d.getTime() - (serverOffset - clientOffset));
        }
        return d;
    }

    public AMF3Input(SerializationContext context) {
        super(context);
    }
}

AMF3Output.java

package com.package;

import flex.messaging.FlexContext;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Output;

import java.io.IOException;
import java.util.Date;

public class AMF3Output extends Amf3Output {
    public AMF3Output(SerializationContext context) {
        super(context);
    }

    @Override
    protected void writeAMFDate(Date d) throws IOException {
        if (d != null) {
            Long clientOffset = (Long) FlexContext.getFlexSession().getAttribute("clientTimezoneOffset");
            Long serverOffset = (Long) (-d.getTimezoneOffset() * 60L * 1000);
            d.setTime(d.getTime() + (serverOffset - clientOffset));
        }
        super.writeAMFDate(d);
    }
}

Now all dates, passed between Flex front-end and BlazeDS back-end will be automatically converted. Note, that you actually change the date.

Assume server time zone is GMT+6, and client is in GMT+2 timezome. You're retrieving date from database. The date on java is 01.01.2013 10.00.00 GMT+6, normally flex would get 01.01.2013 06.00.00 GMT+2. It's the same date, but String equivalent differs. In our case flex will get 01.01.2013 10.00.00 GMT+2. We changed the date. But string equivalent is the same.

Timofei Davydik
  • 7,244
  • 7
  • 33
  • 59
  • Well a detailed reply, but it requires lot of work to do? before implementing above solution, why should we override these classes etc. why dont we perform above conversion as soon as date is recived on java side, and back to flex. like we can send Date and offset from flex, similarly when java has to send data to flex it can also include offset and date from db? will it work, or the above thing is the only solution? by the way why its happening??? even if client and server are one and same machine? Thanks for your reply – PHP Avenger Apr 25 '13 at 16:30
  • I have implemented an workaround as i have mentioned above, instead of sending Date object, I send date as string and on java side with formatter i convert it back to Date object which works perfectly fine. Although it serve my purpose but why its happening at the first place and what are the drawbacks of my string solution??? – PHP Avenger Apr 25 '13 at 16:39
  • @Net Surgeon I don't know how it happens, when dates differs on the same machine. My solution will convert any date passed Flex -> Java, Java -> Flex. No doubt you can make the conversion as soon as date received, but you'll need to do it in any method. In my app, I have many remote calls, where date is passed, so this solution suits me – Timofei Davydik Apr 26 '13 at 11:35
  • @Timofei Davydik you solution works really well and I think is the best solution I have found to this problem so far. I should note that naming your files .as threw me off initially as I had disregarded the post as I needed / wanted to do the fix server side. All of the files AMFEndpoint.as Serializer.as etc are actually .java files. – Giles Horwitch-Smith Jul 15 '13 at 15:36
  • @Giles Horwitch-Smith thanks for pointing my mistake, they are really *.java files. I'm glad it helped you! – Timofei Davydik Jul 18 '13 at 08:01
0

In addition create another class for compatibility with previous versions of Java:

package br.com.ultreia.flex;

import java.io.OutputStream;

import flex.messaging.io.MessageIOConstants;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfTrace;

public class Java15AmfMessageSerializer extends flex.messaging.io.amf.Java15AmfMessageSerializer{
    @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);
    }
}

And I changed the class AMFEndpoint.java

public class AMFEndpoint extends flex.messaging.endpoints.AMFEndpoint {

    public AMFEndpoint(){
        super();
    }

    @Override
    protected String getSerializerClassName() {
        return Serializer.class.getName();
    }

    @Override
    protected String getSerializerJava15ClassName() {
        return Java15AmfMessageSerializer.class.getName();
    }

    @Override
    protected String getDeserializerClassName() {
        return Deserializer.class.getName();
    }
}