3
votes

If a client connects to a web service (JAX-RS) like this:

URL u = new URL(server);
URLConnection con = u.openConnection();
con.setDoOutput(true);
con.getOutputStream().write(stream.toByteArray()); // ByteArrayOutputStream 
con.connect();
InputStream inputStream = con.getInputStream();
byte [] urlBytes = new byte [inputStream.available()];
inputStream.read(urlBytes);
url = new String(urlBytes);

Then what should be the web service interface definition? I have:

@POST 
@Path("/upload")
@Consumes("image/jpeg")
@Produces(MediaType.TEXT_PLAIN)
String uploadPicture();

However, when accessed by the client, it throws:

[ERROR] INFO: generating jax-rs proxy loader class. [INFO] DEBUG [SynchronousDispatcher] PathInfo: /blob/upload [INFO] WARN [ExceptionHandler] Failed executing POST /blob/upload [INFO] org.jboss.resteasy.spi.UnsupportedMediaTypeException: Cannot consume content type [INFO] at org.jboss.resteasy.core.registry.Segment.match(Segment.java:117) [INFO] at org.jboss.resteasy.core.registry.SimpleSegment.matchSimple(SimpleSegment.java:33) [INFO] at org.jboss.resteasy.core.registry.RootSegment.matchChildren(RootSegment.java:327) [INFO] at org.jboss.resteasy.core.registry.SimpleSegment.matchSimple(SimpleSegment.java:44) [INFO] at org.jboss.resteasy.core.registry.RootSegment.matchChildren(RootSegment.java:327) [INFO] at org.jboss.resteasy.core.registry.RootSegment.matchRoot(RootSegment.java:374) [INFO] at org.jboss.resteasy.core.registry.RootSegment.matchRoot(RootSegment.java:367) [INFO] at org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker(ResourceMethodRegistry.java:354) [INFO] at org.jboss.resteasy.core.SynchronousDispatcher.getInvoker(SynchronousDispatcher.java:231) [INFO] at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:170) [INFO] at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:208) [INFO] at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56) [INFO] at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51) [INFO] at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) [INFO] at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) [INFO] at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:66) [INFO] at com.googlecode.objectify.cache.AsyncCacheFilter.doFilter(AsyncCacheFilter.java:39) [INFO] at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163) [INFO] at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58) [INFO] at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) [INFO] at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) [INFO] at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) [INFO] at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) [INFO] at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) [INFO] at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) [INFO] at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [INFO] at com.google.inject.servlet.FilterDefinition.doFilter(FilterDefinition.java:163) [INFO] at com.google.inject.servlet.FilterChainInvocation.doFilter(FilterChainInvocation.java:58) [INFO] at com.google.inject.servlet.ManagedFilterPipeline.dispatch(ManagedFilterPipeline.java:118) [INFO] at com.google.inject.servlet.GuiceFilter.doFilter(GuiceFilter.java:113) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at com.google.appengine.tools.development.DevAppServerServersFilter.doDirectRequest(DevAppServerServersFilter.java:369) [INFO] at com.google.appengine.tools.development.DevAppServerServersFilter.doDirectServerRequest(DevAppServerServersFilter.java:352) [INFO] at com.google.appengine.tools.development.DevAppServerServersFilter.doFilter(DevAppServerServersFilter.java:115) [INFO] at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) [INFO] at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) [INFO] at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) [INFO] at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) [INFO] at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) [INFO] at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) [INFO] at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:97) [INFO] at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) [INFO] at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:438) [INFO] at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) [INFO] at org.mortbay.jetty.Server.handle(Server.java:326) [INFO] at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) [INFO] at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938) [INFO] at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755) [INFO] at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) [INFO] at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) [INFO] at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) [INFO] at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

What should be the JAX-RS interface for this client?

Update:

One thing to note here is that the client code is already compiled and signed and I can't just change it.

1
Where are you setting the Content-Type header for you request? I do not see that in your client code above. If you don't tell Resteasy what content type you are sending it is not going to be able to match that up with an @Consumes annotation. - gregwhitaker

1 Answers

6
votes

Too bad you can't change the client... the error belongs there:

  • does not set the appropriate Content-Type, so the connection will default to application/x-www-form-urlencoded

  • then it sends the image data as a simple stream (so it's not correctly encoded)

The shortest (tough not so elegant) path to a viable solution could be accepting everything:

@POST 
@Path("/upload")
@Consumes("*/*")
@Produces(MediaType.TEXT_PLAIN)
String uploadPicture(InputStream image);