package gwtgaetools.server;
import gwtgaetools.shared.model.GenericMessage;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
/**
* Wraps the ChannelService up in our application-specific
* push-messaging infrastructure.
*
* @author Toby Reyelts
*/
public class PushServer<M extends GenericMessage<?>> {
private static final Method dummyMethod = getDummyMethod();
private static SerializationPolicy serializationPolicy;
public PushServer(String appPath) {
createPushSerializationPolicy(appPath);
}
/**
* Creates a new SerializationPolicy for push RPC.
*/
public void createPushSerializationPolicy(String appPath) {
// We're reading all of the SerializationPolicy files in the app
// and merging them together. This approach seems a bit crappy,
// but less crappy than the other alternatives.
File[] files = new File(appPath).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".gwt.rpc");
}
});
List<SerializationPolicy> policies = new ArrayList<SerializationPolicy>();
for (File f : files) {
try {
BufferedInputStream input = new BufferedInputStream(
new FileInputStream(f));
policies.add(SerializationPolicyLoader.loadFromStream(input, null));
} catch (Exception e) {
throw new RuntimeException(
"Unable to load a policy file: " + f.getAbsolutePath());
}
}
serializationPolicy = new MergedSerializationPolicy(policies);
}
// public String encodeMessage(ArrayList<M> msg) {
// try {
// return RPC.encodeResponseForSuccess(dummyMethod, msg, serializationPolicy);
// } catch (SerializationException e) {
// throw new RuntimeException("Unable to encode a message for push.\n" + msg, e);
// }
// }
//
public String encodeMessage(M msg) {
try {
return RPC.encodeResponseForSuccess(dummyMethod, msg, serializationPolicy);
} catch (SerializationException e) {
throw new RuntimeException("Unable to encode a message for push.\n" + msg, e);
}
}
/**
* This method exists to make GWT RPC happy.
* <p>
* {@link RPC#encodeResponseForSuccess(java.lang.reflect.Method, Object)}
* insists that we pass it a Method that has a return type equal to the
* object we're encoding. What we really want to use is
* {@link RPC#encodeResponse(Class, Object, boolean, int, com.google.gwt.user.server.rpc.SerializationPolicy)},
* but it is unfortunately private.
*/
@SuppressWarnings("unused")
private M dummyMethod() {
throw new UnsupportedOperationException("This should never be called.");
}
private static Method getDummyMethod() throws RuntimeException {
try {
return PushServer.class.getDeclaredMethod("dummyMethod");
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find the dummy RPC method.");
}
}
private static class MergedSerializationPolicy extends SerializationPolicy {
List<SerializationPolicy> policies;
MergedSerializationPolicy(List<SerializationPolicy> policies) {
this.policies = policies;
}
@Override
public boolean shouldDeserializeFields(Class<?> clazz) {
for (SerializationPolicy p : policies) {
if (p.shouldDeserializeFields(clazz)) {
return true;
}
}
return false;
}
@Override
public boolean shouldSerializeFields(Class<?> clazz) {
for (SerializationPolicy p : policies) {
if (p.shouldSerializeFields(clazz)) {
return true;
}
}
return false;
}
@Override
public void validateDeserialize(Class<?> clazz)
throws SerializationException {
SerializationException se = null;
for (SerializationPolicy p : policies) {
try {
p.validateDeserialize(clazz);
return;
} catch (SerializationException e) {
se = e;
}
}
throw se;
}
@Override
public void validateSerialize(Class<?> clazz) throws SerializationException {
SerializationException se = null;
for (SerializationPolicy p : policies) {
try {
p.validateSerialize(clazz);
return;
} catch (SerializationException e) {
se = e;
}
}
throw se;
}
}
}