Custom generic classes in shared state?

Hey,

is it possible to use your own java generics in shared state? There shouldn’t be too much of a problem in GWT, but it seems the serialization/deserialization mechanism can’t really handle it. To be precise, this works:

[code]
public class TestBidiMap implements Serializable
{
private static final long serialVersionUID = -685313744581604160L;

public Map<String, String> keyToValue;
public Map<String, String> valueToKey;

public TestBidiMap()
{
    ...        
}

}
[/code]whereas, this does not work:

[code]
public class BidiMap<K,V> implements Serializable
{
private static final long serialVersionUID = 5011484513683193827L;

public Map<K,V> keyToValue;
public Map<V,K> valueToKey;

public BidiMap()
{
    ...
}

}
[/code]I think I haven’t seen anything to really explain this, not even
here
. So, unless I missed something, what’s the catch here?
The generated error:

com.vaadin.server.PaintException: Failed to serialize shared state for connector org.pikater.web.vaadin.gui.KineticEditor (2): Can not encode org.pikater.shared.util.BidiMap<java.lang.String, java.lang.String>
    at com.vaadin.server.communication.SharedStateWriter.write(SharedStateWriter.java:66)
    at com.vaadin.server.communication.UidlWriter.write(UidlWriter.java:123)
    at com.vaadin.server.communication.UIInitHandler.getInitialUidl(UIInitHandler.java:282)
    at com.vaadin.server.communication.UIInitHandler.synchronizedHandleRequest(UIInitHandler.java:79)
    at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:37)
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1387)
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:238)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)
Caused by: org.json.JSONException: Can not encode org.pikater.shared.util.BidiMap<java.lang.String, java.lang.String>
    at com.vaadin.server.JsonCodec.encodeObject(JsonCodec.java:753)
    at com.vaadin.server.JsonCodec.encode(JsonCodec.java:682)
    at com.vaadin.server.LegacyCommunicationManager.encodeState(LegacyCommunicationManager.java:115)
    at com.vaadin.server.AbstractClientConnector.encodeState(AbstractClientConnector.java:237)
    at com.vaadin.server.communication.SharedStateWriter.write(SharedStateWriter.java:60)
    ... 23 more
Caused by: org.json.JSONException: Can not encode org.pikater.shared.util.BidiMap<java.lang.String, java.lang.String>
    at com.vaadin.server.JsonCodec.encodeObject(JsonCodec.java:753)
    at com.vaadin.server.JsonCodec.encode(JsonCodec.java:682)
    at com.vaadin.server.JsonCodec.encodeObject(JsonCodec.java:743)
    ... 27 more
Caused by: org.json.JSONException: Can not encode org.pikater.shared.util.BidiMap<java.lang.String, java.lang.String>
    at com.vaadin.server.JsonCodec.encode(JsonCodec.java:685)
    at com.vaadin.server.JsonCodec.encodeObject(JsonCodec.java:743)
    ... 29 more

Hi,

the list of supported types for RPC and SharedState can be found at
https://vaadin.com/vaadin7
:

  • Supports:
    [list]
  • Primitive types
  • Boxed primitive types
  • String
  • Java beans as long as all property types are also supported
  • Connector
  • enum
  • Array
  • URLReference (used to transmit Resource)
  • List
  • Map
  • Set

    [/list]

In your example, it seems to me your K and V could be
any
Java type, so that just isn’t supported.

-tepi

Actually, I don’t think there’s a fundamental reason why parameterized types couldn’t be supported; in this case, for instance, the actual type would be
BidiMap<String, String>
(based on the stack trace) which is perfectly possible to serialize. In JsonCodec.encode there should be a case for
valueType instanceof ParameterizedType
in addition to the
valueType instanceof Class<?>
check that handles non-generic classes. This might be worth a ticket.

Opened
#13726
. AFAICS it should only take a few lines of code to implement.

Thank you for your answers :).
Yes, I had a similar feeling as you Johannes - seemed to me this could well be implemented…

Anyway, while we’re at it, is there a deep reason why static fields are not synchronized in share state? E.g.:

The class:

public class TestClass implements Serializable
{
    private static final long serialVersionUID = -6699631245594495465L;
    
    public int whatever = 0;
}

The shared state:

public class KineticEditorState extends AbstractComponentState
{
    private static final long serialVersionUID = 7400546695911691608L;
    
    public static TestClass testClass = new TestClass();
}

If I set the “whatever” variable to (for instance) 5 when creating the server-side component, I still get the value “0” when I access it in the connector’s “onStateChanged” method. The same happens if I replace the “TestClass” instance for a primitive type, like int.

Maybe I’m mistaken but seems to me that this could be implemented too…