I have a collection of jax-rs resources, such as
#Path("/api/v1/ping")
public class PingResource {
#GET
public String ping(#QueryParam("name") String name) {
return "pong: " + name;
}
}
I want figure out how urls map into resource methods, e.g.
public class ResourceMethod {
Method method;
Object[] arguments;
}
public interface UrlToResourceMapper {
ResourceMethod mapToResource(HttpServletRequest request);
}
UrlToResourceMapper mapper = new UrlToResourceMapperImpl(Arrays.asList(PingResource.class));
ResourceMethod resourceMethod = mapper.mapToResource(new RequestImpl("GET", "http://host/api/v1/ping?name=josh", ...));
assertEquals(PingResource.class.getMethod("ping"), resourceMethod.method);
assertEquals(1, resourceMethod.arguments.length);
assertEquals("josh", (String)resourceMethod.arguments[0]);
Is there a library that implements something like the UrlToResourceMapper interface? e.g. Is there a class within Jersey that does this? Everything I've found so far either invokes the method, or only makes the method accessible from within the context of an executing request. I don't want to invoke the method, and I don't want to submit a request (I don't want filters to be invoked, for instance). I just want a library that tells me what invocation results from a given request.
Related
I am trying to unittest an API REST function.
builder = webTarget.request();
returns builder of the type
javax.ws.rs.client.Invocation.Builder
But if I take that builder and call builder.method("POST", entity) on it, the method called looks thus:
public Response method(final String name, final Entity<?> entity) throws ProcessingException {
requestContext.setMethod(name);
storeEntity(entity);
return new JerseyInvocation(this).invoke();
}
And the last line uses as "this" different builder:
org.glassfish.jersey.client.JerseyInvocation.Builder
And the run fails on that line.
I am looking at it and feel me crazy: How could it be, that the function is called as a member of one class, but when "this" is used in that method, absolutely different class is used?
Both Invocation and Invocation.Builder are interfaces. The WebTarget.request() contract is to return Invocation.Builder. These are all interfaces we are talking about here; WebTarget, Invocation, Invocation.Builder. This is contract design by the JAX-RS specification. It is up the JAX-RS implementation to implement these interfaces. Jersey implementation are JerseyWebTarget, JerseyInvocation, and JerseyInvociation.Builder, respectively.
It's the same if I created something like this
public interface Model {}
public interface Service {
Model getModel();
}
public ModelImpl implements Model {}
public class ServiceImpl implements Service {
#Override
public Model getModel() {
return new ModelImpl();
}
}
There's nothing special goin on here. The Service contract says that the getModel() method returns a Model, which is an interface, the actual return value will be of type ModelImpl, the implementation. Polymorphism in the works.
I have created a CN1 web service which some custom objects that I want to externalize in order to send over the network. I read through several articles on how to create the web service and how to work with the CN1 Externalizable interface.
This works well for web service methods that return a custom externalizable object, however the only indicator that I have is that a method which takes an externalizable object as an argument, I get the following error:
SCHWERWIEGEND: Servlet.service() for servlet [CN1WebServiceServlet]
in context with path [/<myPath>] threw exception
java.io.IOException: Object type not supported: Post
The object is properly registered with the Util class, as changing either the object ID or commenting out the register call will cause a null pointer instead of the IO exception.
The Post class looks like this (simplified to the minimum which already fails):
public class Post implements Externalizable {
public int postid;
public int userid;
// default constructor needed for web service marshalling
public Post() {
}
#Override
public int getVersion() {
return 1;
}
#Override
public void externalize(DataOutputStream out) throws IOException {
Util.writeUTF("" + postid, out);
Util.writeUTF("" + userid, out);
}
#Override
public void internalize(int version, DataInputStream in) throws IOException {
this.postid = Integer.parseInt(Util.readUTF(in));
this.userid = Integer.parseInt(Util.readUTF(in));
}
#Override
public String getObjectId() {
return "Post";
}
Note that this Post object works well when I call a web service method which returns a post object, but not when I send a Post object to the web service:
// works
public static com.codename1.io.Externalizable getPostDetails(int postid) {
return getPostDetails(postid);
}
// fails
public static void sendPost(com.codename1.io.Externalizable post) {
sendPost(post);
}
I am at a loss of what I missed here.
Thanks and best regards
In your Servlet code call Util.register("Post", Post.class); which should hopefully resolve this.
Thanks a lot Shai! My mistake was to assume that registering the externalizable object on one side only. But of course it needs to be registered wherever it is going to be internalized, so in this case on my server.
Solution:
Within the "CN1WebServiceServlet" (not the ProxyServer class where the rest of the code has to be completed), call Util.register("Post", Post.class);
if(methodName.equals("sendPost")) {
Util.register("Post", Post.class); // this is a my insertedline, rest is generated
Object[] args = ProxyServerHelper.readMethodArguments(di, def_sendPost);
WebServiceProxyServer.sendPost((com.codename1.io.Externalizable)args[0]);
ProxyServerHelper.writeResponse(response, def_sendPost);
return;
}
I am trying to implement a simple client in rest easy, but I am getting an error saying "You must use at least one, but no more than one http method annotation". In my server implementation, I have added a http annotation on my method.
#Path("/")
public class TestResource
{
#GET
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
I debugged it through, the first time it is not hitting the runtime exception, However, it is making a second call to it and failing, not sure why and how.
My client as junit test:
#Test
public void testPerformRestEasy() {
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://localhost:8080/");
TestResource proxy = target.proxy(TestResource.class);
String response = proxy.get("user");
Assert.assertEquals("Hellouser", response);
}
The code where it is failing
private static <T> ClientInvoker createClientInvoker(Class<T> clazz, Method method, ResteasyWebTarget base, ProxyConfig config)
{
Set<String> httpMethods = IsHttpMethod.getHttpMethods(method);
if (httpMethods == null || httpMethods.size() != 1)
{
throw new RuntimeException("You must use at least one, but no more than one http method annotation on: " + method.toString());
}
ClientInvoker invoker = new ClientInvoker(base, clazz, method, config);
invoker.setHttpMethod(httpMethods.iterator().next());
return invoker;
}
Error:
java.lang.RuntimeException: You must use at least one, but no more than one http method annotation on: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.createClientInvoker(ProxyBuilder.java:76)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:52)
at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:120)
at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:72)
Does anyone know what the issue is here?
The Resteasy JAXRS 2 client does not seem to accept implementation classes directly. To make it work, you have to create a properly annotated interface. It is used by Resteasy to generate a client proxy and your server must implement exactly the same interface.
So in your case, you have to split your code into an interface and a separate implementation class:
#Path("/")
public interface TestResource {
#GET
#Path("/domain/{value}")
String get(#PathParam("value") final String value);
}
public class TestResourceImpl implements TestResource {
#Override String get(final String value) {
return "Hello" + value;
}
}
I'm not sure if this is Resteasy-specific or required by the specification, but solved the same issue for me. You can find the section that gave me the hint here in the documentation.
You have to define the MIME media type resource representation of resource(#Produces/#Consumes) from client. Like -
#Path("/")
public class TestResource
{
#GET
#Produces("text/plain")
#Path("/domain/{value}")
public String get(#PathParam("value") final String value) {
return "Hello" + value;
}
}
The Jboss Client framework Doc will help you more.
In my case the developer of the Rest Client Interface had wrongly extended RestEasyClientProxy. It wasn't the methods in the Rest Interface that were missing the http annotations, but the inherited methods.
Removing extends RestEasyClientProxy from the Rest Client Interface code fixed the issue.
Consider the following ServerResource derived type:
public class UserResource extends ServerResource {
#Get
public User getUser(int id) {
return new User(id, "Mark", "Kharitonov");
}
}
(Yes, it always returns the same user no matter the given id).
Is it possible to make it work in Restlet? Because, as far as I understand, the expected signature of the GET handler is:
Representation get();
OR
Representation get(Variant v); // (no idea what it means yet)
Now I understand, that I can implement the non type safe GET handler to extract the arguments from the request and then invoke getUser, after which to compose the respective Representation instance from the result and return. But this is a boilerplate code, it does not belong with the application code, its place is inside the framework. At least, this is how it is done by OpenRasta - the REST framework I have been using in .NET
Thanks.
You should remove the parameter from the signature
#Get
public User getUser() {
String id = getQuery().getFirstValue("id");
return new User(id, "Mark", "Kharitonov");
}
No need to override the get() methods in this case as the #Get annotation will be automatically detected.
Using Jersey 1.7, JAX-WS 2.2.3, Tomcat 6.0.30 and the following method declaration prevents Jersey servlet to start:
#POST
#Produces("text/plain")
public void postIt(#WebParam(name = "paramOne") final String paramOne,
final String paramTwo) {
// ...
}
The generated exception is:
SEVERE: Missing dependency for method public
java.lang.String com.sun.jersey.issue.MyResource.postIt(
java.lang.String,java.lang.String) at parameter at index 0
SEVERE: Method, public void
com.sun.jersey.issue.MyResource.postIt(
java.lang.String,java.lang.String),
annotated with POST of resource,
class com.sun.jersey.issue.MyResource,
is not recognized as valid resource method.
If the #WebParam annotation is removed, it all works fine.
Now, please have in mind that I am not trying to work with mere strings, rather, I am migrating complicated Objects that got marshalled/unmarshalled using SOAP to RESTful services, but I must provide both interfaces for a while, without breaking the previous WASDs. The method is just a minimalistic scenario.
Has any of you any idea of the status of this? Has it been fixed? Suggestions?
The specification is clear on this. Section 3.3.2.1 tells us that:
Resource methods MUST NOT have more
than one parameter that is not
annotated with one of the above listed
annotations.
The above listed annotations are the JAX-RS parameter annotations: #QueryParam, #MatrixParam, etc.
There is, however, a Jersey specific way to solve this problem. Using InjectableProvider. So, a method that defines two non-JAX-RS parameters:
#POST
public void postIt(#CustomInjectable final Customer customer,
final Transaction transaction) {
// ...
}
Of course, we have to code the annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface CustomInjectable {
}
An implementation of InjectableProvider that knows how to provide Customers:
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import com.sun.jersey.api.model.Parameter;
#Provider
public class CustomerInjectableProvider implements
InjectableProvider<CustomInjectable, Parameter> {
// you can use #Context variables, as in any Provider/Resource
#Context
private Request request;
public ComponentScope getScope() {
// ComponentScope.Singleton, Request or Undefined
}
public Injectable getInjectable(ComponentContext i,
CustomInjectable annotation,
Parameter param) {
Injectable injectable = null;
if (Customer.getClass().isAssignableFrom(param.getParameterClass()) {
injectable = getInjectable();
}
return injectable;
}
private Injectable getInjectable() {
return new Injectable<Customer>() {
public Customer getValue() {
// parse the customer from request... or session... or whatever...
}
};
}
}
But, Jersey considers only the last annotation (see JERSEY-ISSUE-731), so be careful.
And, a more portable way (if you do care about that, anyway):
// simple bean
public class CustomerWithTransaction {
private Customer customer;
private Transaction transaction;
// getters and setters
}
Then change the method to:
#POST
public void postIt(CustomerWithTransaction customerWithTransaction) {
// ...
}
Then create your own MessageBodyReader for CustomerWithTransaction, where you can also access any context variables (request, headers, etc.).