JAX-WS #WebService on RESTful web-service endpoint - java

Some time ago in one of the projects I found #WebService annotations on some of the jersey root(#Path) resource classes. As far as I understood at the time it was some legacy code or simply a misused JAX-WS annotation. Recently I stumbled upon this post in which mixing JAX-RS service with #WebService annotation for the sake of EJB mentioned(as a side note, that project I worked on didn't make use of EJB at all, so I still think it was an improper use of #WebService). As a result of all that I am now confused if it is in general justifiable to mix #WebService and JAX-RS. What are the cases for that? Anything apart from EJB features mentioned?

Exposing a JAX-RS bean as the methods of a SOAP WS using #WebService may be technically possible. It will not lead to a good API design.
Consider some very common JAX-RS methods:
#GET
#Path("/foos")
#Produces("application/json")
public Response getFoos() {
// get all Foos
List<Foo> foos = ...;
return Response.ok(foos).build();
}
#GET
#Path("/foos/{id}")
#Produces("application/json")
public Response getSingleFoo(#PathParam("id") String id) {
// get the Foo
Foo foo = ...;
return Response.ok(foo).build();
}
It is immediatley obvious how the URLs to call these methods will be structured and what the result will be.
But exposing these methods using #WebService leads to many questions:
What is a Response in a SOAP response?
Will the response use JSON as the representation?
How are the methods called?
I can imagine no usecase that is not completely trivial for which it makes sense to expose the same method using both JAX-RS and JAX-WS. It can either be a useful method for one but not for both.
Don't do this.

Related

Method interception or validation of request params in Sitebricks controller

I'm using Sitebricks with Guice to implement REST service and I have a set of methods like this:
#Get
#At("/:version/har/mostRecentEntry/assertResponseTimeWithin")
public Reply<?> doSomething(#Named("version") int version, Request<String> request) {
// Validation logic for request parameters ...
// Extracting parameters (converting url params to domain area objects)
// Actual business logic
}
Which leads to a lot of copy/pasted code.
I'm looking for some way to separate common validating & extracting data logic from request parameters. Probably I can use AOP to do it, but maybe there's easier way Sitebricks provides?
A few considerations:
Google's Sitebricks project is dead
official site is down (sitebricks.org)
last Github commit - 2015
My recommendation is to not build anything with that framework.
You should definitely consider alternatives for implementing REST services (e.g. SpringBoot).
maybe there's easier way Sitebricks provides?
That being said, Sitebricks doesn't seem to offer validation out of the box.
The code you can find related to validation in Sitebrick is:
#ImplementedBy(AlwaysValidationValidator.class)
public interface SitebricksValidator {
Set<? extends ConstraintViolation<?>> validate(Object object);
}
and this:
public class AlwaysValidationValidator implements SitebricksValidator {
#Override
public Set<? extends ConstraintViolation<?>> validate(Object object) {
return null; //unfinished
}
}
This is unfinished implementation!
Your best option is to use javax validation in a standalone setup.
This includes hibernate-validator + javax expression language - reference implementation of JSR 380. It has a lot of build in constraints (e.g. #NotNull, #Size etc.) and is extensible - you can create your own constraints implementing the right interfaces (the AOP part is handled by the framework).
A more simple alternative is Guava's PreConditions.

JAX-RS #Path in Composed Annotation

This one seems relatively straightforward. I'm messing around with composed annotations, and I'm trying to do the following:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Path("")
public #interface TestAnnotation {
#AliasFor(annotation = Path.class, attribute = "value")
String path();
}
This does not work. When I use it like so:
#Path("")
public class MyResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#TestAnnotation(path = "/things")
public void postIt(Thing myThing) {
// Do various things and then return a Response
}
}
...I receive a 405 in return. If I do this:
// Remove class-level #Path
// #Path("")
public class MyResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#TestAnnotation(path = "/things")
public void postIt(Thing myThing) {
// Do various things and then return a Response
}
}
...I receive a 404 in return.
There is just something about #Path or the fact that #Path has a required value attribute that results in this just not functioning, and I have no idea how to remedy it.
After further experimentation and research, it would appear that what I am trying to do is literally not possible.
A follow-up attempt was made to utilize Jackson's #JsonView and expose it through my composed annotation via Spring's #AliasFor, and this also failed to function.
I spent some time thinking about how annotations work and considering peeskillet's comments about compilation vs. processing, and I have come to the conclusion that both Jersey and Jackson must use annotation processors that basically just call "Method.isAnnotationPresent()" for detection of relevant annotations. Spring's #AliasFor most likely does not compile nor weave the aliased meta-annotations into the byte-code of the target methods, and thus they are not found by the processors.
My personal solution to this problem was to drop JAX-RS entirely and use Spring's #RequestMapping, which can be aliased in a composed annotation, and to just accept that Jackson's #JsonView simply can't be integrated into a composed annotation.
Obviously, this is not an ideal solution for most people, especially those with large, already established systems. In such situations, it is more likely that the idea of composed annotations will be abandoned long before JAX-RS.
So if anyone has some more insight into the problem, or someone directly from the Spring team wants to chime in, feel free.

How to use PATCH method in CXF

I am trying to use PATCH method in my client using CXF implementation of JAX-RS.
At first I defined the PATCH annotation as
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#HttpMethod("PATCH")
public #interface PATCH {
}
Referencing what was written here :
How to have a #PATCH annotation for JAX-RS?
Then I found out #PATCH was added into CXF 3.1.2, so I changed version in my maven's pom.xml and indeed there is public #interface PATCH inside of package org.apache.cxf.jaxrs.ext; and the code actually looks exactly as what I posted above.
However, when I try to use this annotation on my service definition as
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public interface AbcService {
#PATCH
#Path("/abc/efg")
public SomeDTO patchSomething(RequestObject request);
}
I end up with the java.net.ProtocolException: Invalid HTTP method: PATCH as was said in the queston link I posted above. They discuss some solution for this with Jersey, however what I can I do in CXF, so that I can use :
AbcService abcService = JAXRSClientFactory.create(myURI, AbcService.class, myProviders, true);
abcService.patchSomething(new RequestObject('something'));
So I have couple of questions:
How can I make this work ? No I need to write custom CXF interceptor ?
Why did they add the PATCH annotation into CXF if it doesn't work ?
Some guys in the other topic said that the mentioned PATCH annotation definition works for them. How come ? Does it only make trouble on the client side, and if so why is it ?
Why I can't find this annotation in CXF documentation ? I looked into org.apache.cxf.jaxrs.ext package at http://cxf.apache.org/javadoc/latest/ and I don't see any PATCH. Yet in the latest cxf 3.1.2 I really can find it in this package.
It turns out it's cause because in JAVA7, HttpURLConnection doesn't support PATCH, the supported methods in that class are defined statically as
private static final String[] methods = {
"GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
};
However it is possible to send PATCH request in CXF, but the Conduit object must be of type AsyncHTTPConduit.
To make CXF use AsyncHTTPConduit, you can programatically achieve it like this
AbcService service = JAXRSClientFactory.create(myURI, AbcService.class, myProviders, true);
WebClient.getConfig(service).getRequestContext().put("use.async.http.conduit", true);
service.patchEnvironmentParameters(patchRequest);
Or
WebClient client = WebClient.create("http://localhost:53261/v1-0/api/environment/parameters");
WebClient.getConfig(client).getRequestContext().put("use.async.http.conduit", true);
client.invoke("PATCH", "{}");
But beware !! In order to make this work, you have put this dependency into your project
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-hc</artifactId>
<version>${cxf.version}</version>
</dependency>
Also make sure that you use the same version of cxf-rt-transports-http-hc and cxf.
But as you can see what I described doesn't solve the original issue, this way I just made 1 specific PATCH request. However in my project there are many PATCH services defined using interfaces like I showed originally
public interface AbcService {
#PATCH
#Path("/abc/efg")
public SomeDTO patchSomething(RequestObject request);
}
So in order to use the AsyncHTTPConduit only on PATCH methods, I had to write custom CXF interceptor, about which you can learn more here http://cxf.apache.org/docs/interceptors.html
The interceptor I wrote runs in PRE_LOGIC phase and it checks what kind of method is used and in case it PATCH, it defined the conduit property. Then in latter phases of service invocation, CXF uses this property to choose which Conduit implementation should be used, and so after
if ( message.get(Message.HTTP_REQUEST_METHOD).equals("PATCH") {
message.put("use.async.http.conduit", true);
}
the AsyncHTTPConduit instance will be used with which the PATCH will work.
Could you try to replace the use of #PATCH with #POST in your code to see if it works ? Your AbcService interface misses an #Path annotation at the type level (unless it is a subresource ?), so it might be worth trying with a standard HTTP verb first to make sure everything else is properly configured.

Bypassing JAX-WS SOAP overhead with JAX-RS/Jersey?

The only web services I've ever integrated with or used have been RESTful. I'm now trying to integrate with a 3rd party SOAP service and am awe-struck at how seemingly convoluted SOAP appears to be.
With REST, I use a JAX-RS client called Jersey that makes hitting RESTful endpoints a piece a' cake. For instance, if a service is exposing a POST endpoint at http://api.example.com/fizz (say, for upserting Fizz objects), then in Jersey I might make a service client that looks like this (pseudo-code):
// Groovy pseudo-code
class Fizz {
int type
boolean derps
String uid
}
class FizzClient {
WebResource webResource = initAt("https://api.example.com")
upsertFizz(Fizz fizz) {
webResource.path("fizz").post(fizz)
}
}
But Java-based SOAP clients seem, at first blush, to be fairly more complicated. If I understand the setup correctly, the general process is this:
Obtain an XML document called a WSDL from the service provider; this appears to be a language-agnostic description of all the available endpoints
Run a JDK tool called wsimport on the WSDL which actually generates Java source code, which implements JAX-WS APIs and actually represents my SOAP client
Import those generated source files into my project and use them
First off, if anything I have said about this process is incorrect, please begin by correcting me! Assuming I'm more or less correct, what I don't understand is: why is this necessary if its all an HTTP conversation? Why couldn't I achieve SOAP-based conversations with Jersey, and bypass all this source-generation boilerplate?
For instance, say the same endpoint exists, but is governed by SOAP:
class FizzClient {
WebResource webResource = initAt("https://api.example.com")
FizzSerializer serializer // I take Fizz instances and turn them into XML
FizzDeserializer deserializer // I take XML and turn them into Fizz instances
upsertFizz(Fizz fizz) {
String xmlFizz = serializer.serialize(fizz)
webResource.path("fizz").post(xmlFizz)
}
}
If I understand SOAP correctly, its just a way of utilizing HTTP verbs and request/response entities to send app-specific messages around; it's an HTTP "conversation". So why couldn't I hijack a REST framework like Jersey to HTTP POST messages, and in doing so, bypass this SOAP overhead?
This is going to attract opinion-based answers, but first, you should understand that
jax-rs is much younger than jax-ws (jax-ws had a final draft in 2006, JAX-RS came out in 2008-9).
RESTful webservices standard, for many purposes is quite amorphous - many businesses prefer the comfort of a contract in the form of a WSDL.
Not to mention that JAX-WS, in concert with WS-I provides many other standards that govern security, message reliability and other enterprise-goodies (under the generic "WS-*" banner) that businesses care about. There's a hodge-podge of libraries that are attempting to get that kind of uniformity on to the jax-rs platform, but for now, jax-ws/WS-I is the industry standard

REST annotations injection together with injection of custom XML objects in one request

1)
I'm dealing with similar situation like at How can I pass complex objects as arguments to a RESTful service? , but actually the injection of my custom XML objects if injected all right, IF i do not annotate method parameter with #Form.
This is wrapper request object to injection:
#XmlRootElement(name = "request")
#XmlAccessorType(XmlAccessType.NONE)
#XmlType
#Consumes({"application/xml", "application/json"})
public class TestRequest {
#PathParam(value = "value")
private String value; // this is injected only when #Form param is added to the method parameter definition
#XmlElement(type = Test.class)
private Test test; // this is XML object I want to inject from the REST request
#XmlElement
private String text; // or inject other XML element like this
}
So this would inject me REST parameters (e.g. {value} - #PathParam("value") annotated in TestRequest).
BUT this doesn't unmarshall the XML object Test in wrapper object TestRequested.
#POST
#Path("tests/{value}")
#Consumes("application/xml")
#Produces("application/xml")
public void addTest(**#Form** TestRequest req);
And following definition would inject only the XML object Test but doesn't inject the REST annotations (e.g. {value} from URI):
public void addTest(TestRequest req); // without #Form annotation now
2) I also tried another approach by catching request to custom MessageBodyReader implementation but have been lost in the details where to find code, method or class of JAX-RS or RESTEasy that actually does this parsing/injecting of REST annotations(#PathParam, #QueryParam,...).
I also noticed that when there is #Form annotation in method definition, then custom MessageBodyReader isn't even catched (propably built-in one for REST parameters catches that request and custom reader is then ignored).
I could in this custom message body reader solution somehow call that built-in injection provider but i didn't find suitable documentation and it seems I'm doing something wrong and all can be done more simplier some other way.
To summarise the goal: inject both REST parameters (#PathParam, #QueryParam etc.) and custom XML/JSON objects somehow in one request in ONE wrapper object.
(It works with one wrapper object annotated #Form and the other parameter to be without #Form annotation, but I would like to have all in one wrapper object).
Thank you for any insights or help.
You are mixing JAX-RS and JAXB annotations. That's a bad idea. Use JAX-RS annotations on resource classes and JAXB annotations on represenation classes.

Categories