Anonymous | Login | Signup for a new account | 11-21-2024 22:02 PST |
Main | My View | View Issues | Change Log | Docs |
Viewing Issue Advanced Details [ Jump to Notes ] | [ View Simple ] [ Issue History ] [ Print ] | |||||||||||
ID | Category | Severity | Reproducibility | Date Submitted | Last Update | |||||||
0004080 | [Hessian] | major | always | 06-16-10 01:12 | 06-16-10 01:12 | |||||||
Reporter | dilbert | View Status | public | |||||||||
Assigned To | ||||||||||||
Priority | normal | Resolution | open | Platform | ||||||||
Status | new | OS | ||||||||||
Projection | none | OS Version | ||||||||||
ETA | none | Fixed in Version | Product Version | 4.0.6 | ||||||||
Product Build | ||||||||||||
Summary | 0004080: SecurityException thrown when serializing Throwable on Google App Engine | |||||||||||
Description |
When an exception is thrown on GAE (a servlet that extends HessianServlet) it is not forwarded to the client, instead a SecurityException is thrown. Here is an example. First the exception declaration: public class TestException extends RuntimeException { } Next the service declaration: public interface IService { void testException(); } And finally the service implementation: public class Service extends HessianServlet implements IService { public void testException() { throw new TestException(); } } As You can see this is a trivial implementation to test the exception. When executed on Google servers it dies like this: java.lang.SecurityException: java.lang.IllegalAccessException: Reflection is not allowed on private java.lang.Throwable java.lang.Throwable.cause at com.google.appengine.runtime.Request.process-9880ff155b30e983(Request.java) at java.lang.reflect.Field.setAccessible(Field.java:166) at com.caucho.hessian.io.JavaSerializer.introspect(JavaSerializer.java:122) at com.caucho.hessian.io.JavaSerializer.<init>(JavaSerializer.java:81) at com.caucho.hessian.io.ThrowableSerializer.<init> ThrowableSerializer.java:59) at com.caucho.hessian.io.SerializerFactory.loadSerializer(SerializerFactory.java:301) at com.caucho.hessian.io.SerializerFactory.getSerializer(SerializerFactory.java:224) at com.caucho.hessian.io.SerializerFactory.getObjectSerializer(SerializerFactory.java:197) at com.caucho.hessian.io.Hessian2Output.writeObject(Hessian2Output.java:418) at com.caucho.hessian.io.Hessian2Output.writeFault(Hessian2Output.java:400) at com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:314) at com.caucho.hessian.server.HessianSkeleton.invoke(HessianSkeleton.java:202) at com.caucho.hessian.server.HessianServlet.invoke(HessianServlet.java:389) at com.caucho.hessian.server.HessianServlet.service(HessianServlet.java:369) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:97) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:35) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:238) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle(Server.java:326) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:135) at com.google.apphosting.runtime.JavaRuntime.handleRequest(JavaRuntime.java:250) at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:5838) at com.google.apphosting.base.RuntimePb$EvaluationRuntime$6.handleBlockingRequest(RuntimePb.java:5836) at com.google.net.rpc.impl.BlockingApplicationHandler.handleRequest(BlockingApplicationHandler.java:24) at com.google.net.rpc.impl.RpcUtil.runRpcInApplication(RpcUtil.java:398) at com.google.net.rpc.impl.Server$2.run(Server.java:852) at com.google.tracing.LocalTraceSpanRunnable.run(LocalTraceSpanRunnable.java:56) at com.google.tracing.LocalTraceSpanBuilder.internalContinueSpan(LocalTraceSpanBuilder.java:576) at com.google.net.rpc.impl.Server.startRpc(Server.java:807) at com.google.net.rpc.impl.Server.processRequest(Server.java:369) at com.google.net.rpc.impl.ServerConnection.messageReceived(ServerConnection.java:442) at com.google.net.rpc.impl.RpcConnection.parseMessages(RpcConnection.java:319) at com.google.net.rpc.impl.RpcConnection.dataReceived(RpcConnection.java:290) at com.google.net.async.Connection.handleReadEvent(Connection.java:474) at com.google.net.async.EventDispatcher.processNetworkEvents(EventDispatcher.java:831) at com.google.net.async.EventDispatcher.internalLoop(EventDispatcher.java:207) at com.google.net.async.EventDispatcher.loop(EventDispatcher.java:103) at com.google.net.rpc.RpcService.runUntilServerShutdown(RpcService.java:251) at com.google.apphosting.runtime.JavaRuntime$RpcRunnable.run(JavaRuntime.java:413) at java.lang.Thread.run(Unknown Source) Caused by: java.lang.IllegalAccessException: Reflection is not allowed on private java.lang.Throwable java.lang.Throwable.cause ... 54 more On the client I get something like this: java.lang.reflect.UndeclaredThrowableException at $Proxy0.testException(Unknown Source) at com.noveideje.testHessian.client.Main.main(Main.java:37) 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.intellij.rt.execution.application.AppMain.main(AppMain.java:110) Caused by: java.io.EOFException: readObject: unexpected end of file at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2133) at com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:114) at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:1653) at com.caucho.hessian.io.Hessian2Input.readReply(Hessian2Input.java:348) at com.caucho.hessian.client.HessianProxy.invoke(HessianProxy.java:194) ... 7 more I have found a workaround by using a custom serializer. Here is how. First the Serializer: public class ThrowableSerializer extends AbstractSerializer { @Override public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { if (obj != null) { final Class cl = obj.getClass(); if (out.addRef(obj)) return; int ref = out.writeObjectBegin(cl.getName()); Throwable tr = (Throwable) obj; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); try { oos.writeObject(tr); if (ref < -1) { out.writeString("value"); out.writeBytes(bos.toByteArray()); out.writeMapEnd(); } else { if (ref == -1) { out.writeInt(1); out.writeString("value"); out.writeObjectBegin(cl.getName()); } out.writeBytes(bos.toByteArray()); } } finally { oos.close(); bos.close(); } } else out.writeNull(); } } The other class we need is the Deserializer: public class ThrowableDeserializer extends AbstractDeserializer { @Override public Class getType() { return Throwable.class; } @Override public Object readMap(AbstractHessianInput in) throws IOException { int ref = in.addRef(null); byte[] initValue = null; while (!in.isEnd()) { String key = in.readString(); if (key.equals("value")) initValue = in.readBytes(); else in.readString(); } in.readMapEnd(); ByteArrayInputStream bis = new ByteArrayInputStream(initValue); ObjectInputStream ois = new ObjectInputStream(bis); try { Object value = ois.readObject(); in.setRef(ref, value); return value; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { ois.close(); bis.close(); } } @Override public Object readObject(AbstractHessianInput in, Object[] fieldNames) throws IOException { int ref = in.addRef(null); byte[] initValue = null; for (Object o : fieldNames) { if (o instanceof String) { final String key = (String) o; if (key.equals("value")) initValue = in.readBytes(); else in.readObject(); } } ByteArrayInputStream bis = new ByteArrayInputStream(initValue); ObjectInputStream ois = new ObjectInputStream(bis); try { Object value = ois.readObject(); in.setRef(ref, value); return value; } catch (ClassNotFoundException e) { throw new RuntimeException(e); } finally { ois.close(); bis.close(); } } } I'm not sure if the readMap part is actually needed since I rearranged this code from another example. Also, a ThrowableSerializerFactory is needed: public class ThrowableSerializerFactory extends AbstractSerializerFactory { @Override public Serializer getSerializer(Class cl) throws HessianProtocolException { if (Throwable.class.isAssignableFrom(cl)) { return new ThrowableSerializer(); } return null; } @Override public Deserializer getDeserializer(Class cl) throws HessianProtocolException { if (Throwable.class.isAssignableFrom(cl)) { return new ThrowableDeserializer(); } return null; } } What this code essentially does is take a Throwable (which implements Serializable), serializes it to a byte[] and pushes it over to the other side. This serialization does not use the problematic setAccessible method (like com.caucho.hessian.io.ThrowableSerializer) and works correctly on App engine (I tested it). The only part left to do is to plug all this into the servlet and the client. Here is how to do it on the servlet: public class Service extends HessianServlet implements IService { @Override public void init(ServletConfig config) throws ServletException { super.init(config); getSerializerFactory().addFactory(new ThrowableSerializerFactory()); } // implement IService methods... } And here is how to do it on the client: String url = "http://whatever.appspot.com/service"; [^] HessianProxyFactory factory = new HessianProxyFactory(); factory.getSerializerFactory().addFactory(new ThrowableSerializerFactory()); IService service = (IService) factory.create(IService.class, url); If you have any additional questions do not hesitate to ask. Thank you for your time. The attachment contains an example project in which the bug is reproduced. |
|||||||||||
Steps To Reproduce | ||||||||||||
Additional Information |
This bug report obsoletes this bug: http://bugs.caucho.com/view.php?id=4061 [^] Some forums where this bug was discussed: http://groups.google.com/group/google-appengine-java/browse_thread/thread/0ccb9d0ff6b88545/ [^] http://forum.caucho.com/showthread.php?t=9999 [^] |
|||||||||||
Attached Files | testHessian.zip [^] (21,521 bytes) 06-16-10 01:12 | |||||||||||
|
There are no notes attached to this issue. |
Issue History | |||
Date Modified | Username | Field | Change |
06-16-10 01:12 | dilbert | New Issue | |
06-16-10 01:12 | dilbert | File Added: testHessian.zip | |
06-16-10 01:29 | dilbert | Issue Monitored: dilbert |
Mantis 1.0.0rc3[^]
Copyright © 2000 - 2005 Mantis Group
28 total queries executed. 24 unique queries executed. |