ClassCastException whlile accessing custom object in Geode's Function.execute() method - geode

I am adding the custom object(Account) into Cache and then trying to access the object in the Function.execute() method.
But it throws org.apache.geode.pdx.internal.PdxInstanceImpl cannot be cast to com.sas.cpm.model.Account.
Custom object Account.java
public class Account implements PdxSerializable, Declarable{
public Account() {
super();
// TODO Auto-generated constructor
}
#Override
public void fromData(PdxReader pr) {…..}
#Override
public void toData(PdxWriter pw) {… }
}
Client code:
ClientCache cache = new ClientCacheFactory()
.addPoolLocator("localhost", 10334).set("log-level", "INFO").create();
// create a local region that matches the server region
// Account is the domain object
Region<String, Account> region =
cache.<String, Account>createClientRegionFactory(ClientRegionShortcut.CACHING_PROXY)
.create("testRegion");
feedData(region); //add Account object to region
Execution execution = FunctionService.onRegion(region);
ResultCollector<Integer, List> rc = execution.execute("UpdateCost");//.ID);//com.sas.cpm.geode
Function class : UpdateCost.java
public class UpdateCost implements Function{
#Override
public void execute(FunctionContext context) {
RegionFunctionContext regionContext = (RegionFunctionContext) context;
Region<String, Account> region = regionContext.getDataSet();
for ( Map.Entry<String, Account> entry : region.entrySet() ) {
Account account = entry.getValue(); /// THIS LINE GIVES THE ERROR
}
}
}
Error:
Exception in thread "main" org.apache.geode.cache.execute.FunctionException: org.apache.geode.cache.client.ServerOperationException: remote server on dsinsbb01ina4(46560:loner):59343:2f7e3885: While performing a remote executeRegionFunction
at org.apache.geode.internal.cache.execute.ServerRegionFunctionExecutor.executeOnServer(ServerRegionFunctionExecutor.java:229)
at org.apache.geode.internal.cache.execute.ServerRegionFunctionExecutor.executeFunction(ServerRegionFunctionExecutor.java:178)
at org.apache.geode.internal.cache.execute.ServerRegionFunctionExecutor.execute(ServerRegionFunctionExecutor.java:379)
at geodeproject1.Example.funcUpdateExec(Example.java:186)
at geodeproject1.Example.main(Example.java:68)
Caused by: org.apache.geode.cache.client.ServerOperationException: remote server on dsinsbb01ina4(46560:loner):59343:2f7e3885: While performing a remote executeRegionFunction
at org.apache.geode.cache.client.internal.ExecuteRegionFunctionOp$ExecuteRegionFunctionOpImpl.processResponse(ExecuteRegionFunctionOp.java:606)
at org.apache.geode.cache.client.internal.AbstractOp.processResponse(AbstractOp.java:225)
at org.apache.geode.cache.client.internal.AbstractOp.attemptReadResponse(AbstractOp.java:198)
at org.apache.geode.cache.client.internal.AbstractOp.attempt(AbstractOp.java:386)
at org.apache.geode.cache.client.internal.ConnectionImpl.execute(ConnectionImpl.java:269)
at org.apache.geode.cache.client.internal.pooling.PooledConnection.execute(PooledConnection.java:325)
at org.apache.geode.cache.client.internal.OpExecutorImpl.executeWithPossibleReAuthentication(OpExecutorImpl.java:892)
at org.apache.geode.cache.client.internal.OpExecutorImpl.execute(OpExecutorImpl.java:171)
at org.apache.geode.cache.client.internal.PoolImpl.execute(PoolImpl.java:772)
at org.apache.geode.cache.client.internal.ExecuteRegionFunctionOp.execute(ExecuteRegionFunctionOp.java:162)
at org.apache.geode.cache.client.internal.ServerRegionProxy.executeFunction(ServerRegionProxy.java:732)
at org.apache.geode.internal.cache.execute.ServerRegionFunctionExecutor.executeOnServer(ServerRegionFunctionExecutor.java:220)
... 4 more
Caused by: org.apache.geode.cache.execute.FunctionException: java.lang.ClassCastException: org.apache.geode.pdx.internal.PdxInstanceImpl cannot be cast to com.sas.cpm.model.Account
at org.apache.geode.cache.client.internal.ExecuteRegionFunctionOp$ExecuteRegionFunctionOpImpl.processResponse(ExecuteRegionFunctionOp.java:583)
... 15 more
Caused by: java.lang.ClassCastException: org.apache.geode.pdx.internal.PdxInstanceImpl cannot be cast to com.sas.cpm.model.Account
at com.sas.cpm.geode.UpdateCost.execute(UpdateCost.java:49)
at org.apache.geode.internal.cache.execute.AbstractExecution.executeFunctionLocally(AbstractExecution.java:331)
at org.apache.geode.internal.cache.execute.AbstractExecution$2.run(AbstractExecution.java:300)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.geode.distributed.internal.ClusterDistributionManager.runUntilShutdown(ClusterDistributionManager.java:949)
at org.apache.geode.distributed.internal.ClusterDistributionManager.doFunctionExecutionThread(ClusterDistributionManager.java:803)
at org.apache.geode.internal.logging.LoggingThreadFactory.lambda$newThread$0(LoggingThreadFactory.java:121)
at java.lang.Thread.run(Unknown Source)

The ClassCastException is thrown because you're receiving a PdxInstance from the cache instead of an Account, this happens when you implement the PdxSerializable interface in your domain object and configure PDX Serialization with read-serialized=true.
Please have a look at Implementing PdxSerializable in Your Domain Object and Programming Your Application to Use PdxInstances for further details.
Cheers.

Related

Purpose and behaviour of init() in Vertx class

I have the following verticle for testing purposes:
public class UserVerticle extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(UserVerticle.class);
#Override
public void start(Future<Void> sf) {
log.info("start()");
JsonObject cnf = config();
log.info("start.config={}", cnf.toString());
sf.complete();
}
#Override
public void stop(Future<Void> sf) {
log.info("stop()");
sf.complete();
}
private void onMessage(Message<JsonObject> message) { ... }
log.info("onMessage(message={})", message);
}
}
Is is deployed from the main verticle with
vertx.deployVerticle("org.buguigny.cluster.UserVerticle",
new DeploymentOptions()
.setInstances(1)
.setConfig(new JsonObject()
.put(some_key, some_data)
),
ar -> {
if(ar.succeeded()) {
log.info("UserVerticle(uname={}, addr={}) deployed", uname, addr);
// continue when OK
}
else {
log.error("Could not deploy UserVerticle(uname={}). Cause: {}", uname, ar.cause());
// continue when KO
}
});
This code works fine.
I had a look at the Verticle documentation and discovered an init() callback method I didn't see before. As the documentation doesn't say much about what it really does, I defined it to see where in the life cycle of a verticle it gets called.
#Override
public void init(Vertx vertx, Context context) {
log.info("init()");
JsonObject cnf = context.config();
log.info("init.config={}", cnf.toString());
}
However, when init() is defined I get a java.lang.NullPointerException on the line where I call JsonObject cnf = config(); in start():
java.lang.NullPointerException: null
at io.vertx.core.AbstractVerticle.config(AbstractVerticle.java:85)
at org.buguigny.cluster.UserVerticle.start(UserVerticle.java:30)
at io.vertx.core.impl.DeploymentManager.lambda$doDeploy$8(DeploymentManager.java:494)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
at io.vertx.core.impl.EventLoopContext.lambda$executeAsync$0(EventLoopContext.java:38)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
My questions are:
Q1 : any clue why NullPointerException is thrown?
Q2 : what is the purpose of init()? Is it internal to Vertx or can it be be implemented by client code to, for example, define some fields in the verticle objects passed in deployment config ?
The init method is for internal usage and documented as such in the Javadoc. Here's the source code:
/**
* Initialise the verticle.<p>
* This is called by Vert.x when the verticle instance is deployed. Don't call it yourself.
* #param vertx the deploying Vert.x instance
* #param context the context of the verticle
*/
#Override
public void init(Vertx vertx, Context context) {
this.vertx = vertx;
this.context = context;
}
If init is documented in any user documentation it's a mistake, please report it.

PowerMock with Jersey, InternalServerErrorException error

We have a gradle Jersey project and we're trying to use PowerMock and EasyMock (and JUnit) for the unit tests because we have several static methods we need to mock. We finally got PowerMock working with Jersey (see this question), but PowerMock will only allow our REST calls to return Strings. All other return types--like Lists, for example--result in an InternalServerErrorException. Full stack trace:
javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1020)
at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:877)
at org.glassfish.jersey.client.JerseyInvocation.access$800(JerseyInvocation.java:92)
at org.glassfish.jersey.client.JerseyInvocation$3.call(JerseyInvocation.java:722)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:718)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:430)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:321)
at com.company.project.sm.rest.PowerMockTest.aListTest(PowerMockTest.java:104)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:298)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:218)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:160)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:134)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:136)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
The REST calls in the unit tests work without PowerMock; they fail when run with PowerMock. We can't spot what we're doing wrong.
Here are examples of our unit tests (entire class):
// comment-out these two lines to turn off PowerMock
#org.powermock.core.classloader.annotations.PowerMockIgnore({"javax.ws.*", "org.glassfish.*"})
#RunWith(PowerMockRunner.class)
public class PowerMockTest {
private JerseyTest jerseyTest;
#Before
public void setUp() throws Exception {
this.jerseyTest = new JerseyTest() {
#Override
protected Application configure() {
return PowerMockTest.this.configure();
}
};
this.jerseyTest.setUp();
}
#After
public void tearDown() throws Exception {
this.jerseyTest.tearDown();
}
public ResourceConfig configure() {
return new ResourceConfig(PowerMockTestResource.class);
}
#Test
public void thisTestWorks() {
String fooString = this.jerseyTest.target("test/string")
.request(MediaType.TEXT_PLAIN)
.get( String.class );
assertNotNull( fooString );
}
#Test
public void thisTestFails() {
List<PowerMockStringWrapper> response = this.jerseyTest.target("test/list")
.request( MediaType.APPLICATION_JSON )
.get( new GenericType<List<PowerMockStringWrapper>>() {} );
assertNotNull( response );
}
}
The .get line in thisTestFails() is the line the stack trace reports is failing.
As you can see, we're not even doing anything PowerMock-ey in our tests. The PowerMockStringWrapper is just a small class that wraps String, because Jersey (for some reason) can't return Lists of base Strings. Once again, both these unit tests work without PowerMock, but the second one fails as soon as we turn PowerMock on.
Can anyone suggest anything?

Portico RTI + Java WEB Application = NoClassDefFoundError

I'm trying to start a Federate (HLA RTI) from a Java Web application, but I'm receiving the error java.lang.NoClassDefFoundError: hla/rti1516/FederateAmbassador.
The same Federate is starting well from an ordinary java application.
My goal is to start the RTI and a Federation by starting a Federate when the web application is started. So I create a WebListener class to start my Federate:
#WebListener
public class Startup implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent arg0) {
//
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
Federate fed = new Federate();
fed.start();
}
}
This is the Federate start() code. I'll not put all code because this is not even reached:
public class Federate {
public void start() {
System.out.println("start");
try {
RTIambassador rtiAmb = RtiFactoryFactory.getRtiFactory().getRtiAmbassador();
MyFederateAmbassador fedAmb = new MyFederateAmbassador();
...
}
When my webserver is starting, I never see the System.out.println("start") output, just only this error :
Grave: Exception sending context initialized event to listener instance of class cmabreu.scorpio.startup.Startup
java.lang.NoClassDefFoundError: hla/rti1516/FederateAmbassador
at cmabreu.scorpio.startup.Startup.contextInitialized(Startup.java:29)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4791)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5285)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
My portico.jar is configured in Build Path and the Federate imports are pretty fine ( no errors ):
import hla.rti1516.AttributeHandle;
import hla.rti1516.AttributeHandleSet;
import hla.rti1516.AttributeHandleValueMap;
import hla.rti1516.LogicalTime;
import hla.rti1516.ObjectClassHandle;
import hla.rti1516.ObjectInstanceHandle;
import hla.rti1516.RTIambassador;
import hla.rti1516.ResignAction;
import hla.rti1516.jlc.RtiFactoryFactory;
What I'm doing wrong?
After contact Portico creator, Tim Pokorny, I solved the problem changing some code in Portico source. The Portico's Log4j is in conflict with catalina's Log4J and a few other things more.

Problems executing project using jbehave in eclipse

I am completely new to jbehave and even automated testing.
I read a tutorial online and tried following the steps.
I am trying to run this application in eclipse IDE.
I made a Math.story file which contains the tests:
Scenario: 2 squared
Given a variable x with value 2
When I multiply x by 2
Then x should equal 4
In a .java file called ExampleSteps.java, the steps are written:
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Named;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.steps.Steps;
public class ExampleSteps extends Steps {
int x;
#Given("a variable x with value $value")
public void givenXValue(#Named("value") int value) {
x = value;
}
#When("I multiply x by $value")
public void whenImultiplyXBy(#Named("value") int value) {
x = x * value;
}
#Then("x should equal $value")
public void thenXshouldBe(#Named("value") int value) {
if (value != x)
throw new RuntimeException("x is " + x + ", but should be " + value);
}
}
I created another class SimpleJbehave which has the main method:
import java.util.Arrays;
import java.util.List;
import org.jbehave.core.embedder.Embedder;
public class SimpleJBehave {
private static Embedder embedder = new Embedder();
private static List<String> storyPaths = Arrays
.asList("Math.story");
public static void main(String[] args) {
embedder.candidateSteps().add(new ExampleSteps());
embedder.runStoriesAsPaths(storyPaths);
}
}
When I run this code, I get the following exception:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/collections/Transformer
at org.jbehave.core.configuration.Configuration.<init>(Configuration.java:112)
at org.jbehave.core.configuration.MostUsefulConfiguration.<init>(MostUsefulConfiguration.java:49)
at org.jbehave.core.embedder.Embedder.<init>(Embedder.java:30)
at org.jbehave.core.embedder.Embedder.<init>(Embedder.java:37)
at SimpleJBehave.<clinit>(SimpleJBehave.java:8)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.collections.Transformer
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 5 more
As I am a novice, I have not been able to understand what exactly the problem is.
It will be really nice if someone could tell me what I should do to get this code working.
Is my approach wrong?
Thank you very much in advance.
It looks like you don't have org.apache.commons.collections.Transformer on your classpath. It looks like this class is available in the apache-commons-transformer library here: http://commons.apache.org/collections/api-release/org/apache/commons/collections/Transformer.html
Download the jar and add it to your classpath. It might work.

A parameterized AutoBean type containing a typed member

Question
Is there any way to deserialize JSON using the AutoBean framework such that the resulting bean has a type parameter that affects the type of one or more of its members?
Background
RPC with JSON results
I'm using GWT (RequestBuilder) to perform RPC requests. The JSON payload returned is of the following form:
{
"resultSet": [{...}, {...}, ...], // items requested; say, items 150-160
"totalCount": 15330 // total matching items in DB
}
The objects in resultSet vary in type depending on the specific RPC I'm calling.
AutoBean interface
I'd like to deserialize this JSON using AutoBean. I'm trying to represent this object as follows:
interface RpcResults<T> {
List<T> getResultSet();
void setResultSet(List<T> resultSet);
int getTotalCount();
void setTotalCount(int totalCount);
}
I've also created appropriate interfaces representing each type of object that could exist within resultSet. Finally, I set up the appropriate call to AutoBeanCodex.decode.
Running the code
Attempting to run this code in development mode causes the following stack trace to appear in the console:
19:44:23.791 [ERROR] [xcbackend] Uncaught exception escaped
java.lang.IllegalArgumentException: The AutoBeanFactory cannot create a java.lang.Object
at com.google.gwt.autobean.shared.AutoBeanCodex$Decoder.push(AutoBeanCodex.java:240)
at com.google.gwt.autobean.shared.AutoBeanCodex$Decoder.decode(AutoBeanCodex.java:50)
at com.google.gwt.autobean.shared.AutoBeanCodex$Decoder.visitCollectionProperty(AutoBeanCodex.java:83)
at com.citrix.xenclient.backend.client.json.RpcResultsAutoBean.traverseProperties(RpcResultsAutoBean.java:100)
at com.google.gwt.autobean.shared.impl.AbstractAutoBean.traverse(AbstractAutoBean.java:153)
at com.google.gwt.autobean.shared.impl.AbstractAutoBean.accept(AbstractAutoBean.java:112)
at com.google.gwt.autobean.shared.AutoBeanCodex$Decoder.decode(AutoBeanCodex.java:51)
at com.google.gwt.autobean.shared.AutoBeanCodex.decode(AutoBeanCodex.java:505)
at com.google.gwt.autobean.shared.AutoBeanCodex.decode(AutoBeanCodex.java:521)
at com.citrix.xenclient.backend.client.services.JSONResponseResultSetHandler.onResponseReceived(JSONResponseResultSetHandler.java:51)
at com.google.gwt.http.client.Request.fireOnResponseReceived(Request.java:287)
at com.google.gwt.http.client.RequestBuilder$1.onReadyStateChange(RequestBuilder.java:395)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessagesWhileWaitingForReturn(BrowserChannelServer.java:326)
at com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:207)
at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:126)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:561)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeObject(ModuleSpace.java:269)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeObject(JavaScriptHost.java:91)
at com.google.gwt.core.client.impl.Impl.apply(Impl.java)
at com.google.gwt.core.client.impl.Impl.entry0(Impl.java:214)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:103)
at com.google.gwt.dev.shell.MethodDispatch.invoke(MethodDispatch.java:71)
at com.google.gwt.dev.shell.OophmSessionHandler.invoke(OophmSessionHandler.java:157)
at com.google.gwt.dev.shell.BrowserChannelServer.reactToMessages(BrowserChannelServer.java:281)
at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:531)
at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:352)
at java.lang.Thread.run(Thread.java:636)
Based on this stack trace, my hunch is the following:
Type erasure makes it seem that RpcResults.getResultSet() is returning a raw List.
The AutoBean deserialiser attempts to create Object instances for each item in resultSet.
Failure
Question again
Am I missing something in the AutoBean API that will allow me to do this easily? If not, is there an obvious point of attack I should look into? Is there a more sensible alternative for what I'm doing (other than JSONParser and JavaScriptObject, which I'm already using)?
This is not simple, due to Java type erasure. The type T does not exist at runtime, having been erased to Object in lieu of any other lower bound. The AutoBeanCodex requires type information in order to reify the elements of the incoming json payload. This type information is usually provided by the AutoBean implementation, but due to the T erasure, all it knows is that it contains a List<Object>.
If you can provide a class literal at runtime, the getter could be declared as Splittable getResultSet() and the individual elements of the list reified by calling AutoBeanCodex.decode(autoBeanFactory, SomeInterfaceType.class, getResultSet().get(index)). By using a Category, you could add a <T> T getResultAs(Class<T> clazz, int index) method to the AutoBean interface. This would look something like:
#Category(MyCategory.class)
interface MyFactory extends AutoBeanFactory {
AutoBean<ResultContainer> resultContainer();
}
interface ResultContainer<T> {
Splittable getResultSet();
// It's the class literal that makes it work
T getResultAs(Class<T> clazz, int index);
}
class MyCategory {
public static <T> T getResultAs(Autobean<ResultContainer> bean,
Class<T> clazz, int index) {
return AutoBeanCodex.decode(bean.getFactory(), clazz,
bean.as().getResultSet().get(index)).as();
}
}
Try overriding the .getResultSet() and .setResultSet() methods in your object-specific interfaces:
interface FooRpcResults extends RpcResults<Foo> {
#Override
List<Foo> getResultSet();
#Override
void setResultSet(List<Foo> value);
}
The following test works for me (GWT 2.3.0):
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.google.web.bindery.autobean.shared.AutoBean;
import com.google.web.bindery.autobean.shared.AutoBeanCodex;
import com.google.web.bindery.autobean.shared.AutoBeanFactory;
import com.google.web.bindery.autobean.shared.AutoBeanUtils;
import com.google.web.bindery.autobean.vm.AutoBeanFactorySource;
public class AutoBeanTest {
public static interface Page<T> {
int getDataSize();
List<T> getPage();
int getStartIndex();
void setDataSize(int value);
void setPage(List<T> value);
void setStartIndex(int value);
}
public static interface Thing {
String getName();
void setName(String value);
}
public static interface ThingFactory extends AutoBeanFactory {
AutoBean<Thing> createThing();
AutoBean<ThingPage> createThingPage();
}
public static interface ThingPage extends Page<Thing> {
#Override
List<Thing> getPage();
#Override
void setPage(List<Thing> value);
}
#Test
public void testAutoBean() {
final ThingFactory factory = AutoBeanFactorySource
.create(ThingFactory.class);
final Thing thing1 = factory.createThing().as();
thing1.setName("One");
final Thing thing2 = factory.createThing().as();
thing2.setName("Two");
final List<Thing> things = new ArrayList<Thing>();
things.add(thing1);
things.add(thing2);
final Page<Thing> page = factory.createThingPage().as();
page.setStartIndex(50);
page.setDataSize(1000);
page.setPage(things);
final String json = AutoBeanCodex.encode(
AutoBeanUtils.getAutoBean(page)).getPayload();
final Page<Thing> receivedPage = AutoBeanCodex.decode(factory,
ThingPage.class, json).as();
assertEquals(receivedPage.getStartIndex(), page.getStartIndex());
assertEquals(receivedPage.getDataSize(), page.getDataSize());
assertNotNull(receivedPage.getPage());
assertEquals(receivedPage.getPage().size(), page.getPage().size());
for (int i = 0; i < receivedPage.getPage().size(); i++) {
assertNotNull(receivedPage.getPage().get(i));
assertEquals(receivedPage.getPage().get(i).getName(), page
.getPage().get(i).getName());
}
}
}
Removing the overrides in the ThingPage interface will break it.