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.
Related
I am working on porting an old HTTP server to Micronaut and I am stuck trying to port an authorization filter that used the javax.ws.rs NameBinding annotation to a Micronaut HTTP server filter. 90% of my endpoints/controllers use the NameBinding annotation I have so using the standard Micronaut HTTP server filter would be difficult.
One code smelly thought was to create a filter accepting all api endpoints (ie. #Filter("/**")) and then maybe storing a list of all the paths that don't require authorization and comparing that against the requested path.
Another hack I attempted to was to try and derive the target method with reflections through the request/chain but it seems that target method is held in an #Internal class which leads me to believe I should not be reflecting on the method from a filter. If I was able to reflect on the target method from a filter I could look for my old annotation and filter on that.
In general are there any guiding principles for providing filters to a large subset of controllers/methods excluding a handful, for example an inverse filter pattern (although this would also not be ideal)?
Is there any way in micronaut to manually control the injection of filters?
If you need a fine grained control over your endpoints, I'll go for micronaut AOP
#Documented
#Retention(RUNTIME)
#Target(ElementType.METHOD)
#Around
#Type(AuthenticatedInterceptor.class)
public #interface Authenticated {
}
and the interceptor coresponding
#Singleton
public class AuthenticatedInterceptor implements MethodInterceptor<Object, Object> {
#Override
public Object intercept(MethodInvocationContext<Object, Object> context) {
final var authHeader = ServerRequestContext.currentRequest()
.map(HttpMessage::getHeaders)
.flatMap(HttpHeaders::getAuthorization)
.orElseThrow(() -> new RuntimeException("no header"));
validate(authHeader);
return context.proceed();
}
}
then you'll have to add #Authenticated on each methods that need to be authenticated.
UPDATE
Micronaut security provides it's own #Secured annotation.
I am creating a very basic controller using Kotlin with javax.ws and retrofit libraries.
I created a controller like this...
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
suspend fun sayHello(request: StudentRequest): StudentResponse {
that basically calls another service.
But when I run the app I get this error:
[FATAL] Method public final java.lang.Object MyResource.sayHello(StudentRequest,kotlin.coroutines.Continuation) on resource class MyResource contains multiple parameters with no annotation. Unable to resolve the injection source.;
handlerConstructors=[org.glassfish.jersey.server.model.HandlerConstructor#a0bf272]},
definitionMethod=public final java.lang.Object my.org.package.MyResource(sayHello,k**otlin.coroutines.Continuation**),
the weird part is that are couple of similar posts Jersey #PathParam : contains multiple parameters with no annotation
How can I pass multiple parameter to restful webservice using http post
https://github.com/dropwizard/dropwizard/issues/1115
but are not the same because my problem is with my ONLY parameter
There is no missing tag to my body request and I basically dont know what to look for at this point, any idea what could be wrong with this?
After debugging I noticed that there are two parameters, mine and one injected by Kotlin, when removing the "suspend" everything works fine, but then I am not able to make my async call.
To use coroutines from blocking code you need to use coroutine builder (e.g. launch {} or runBlocking {}).
Unfortunately in this case you can't just mark your glassfish controller as a suspendable function because framework don't know how to deal with continuations.
I am currently integrating Swagger (swagger-jaxrs artifact in version 1.5.7) into our existing JAX-RS REST application. After adding Swagger I added the #Api annotation to our interface class and already got a documentation which doesn't look to bad.
Unfortunately Swagger does not honor the #Produces annotations on my API methods until I annotate those methods with #ApiOperation:
Doesn't list text/plain as the returned media type:
#GET
#Path("/overallStatus")
#Produces(MediaType.TEXT_PLAIN)
public String getOverallStatus() {
}
Does list it:
#GET
#Path("/overallStatus")
#Produces(MediaType.TEXT_PLAIN)
#ApiOperation(value = "Get the overall system status")
public String getOverallStatus() {
}
Is there a way to have the media type in the Swagger output without adding #ApiOperation to all of them? Since the required information is already there, I don't see why I would need it.
You have to add the #ApiOperation to your JAX-RS endpoints since methods without the #ApiOperation annotation will be ignored as per the Swagger Wiki page:
Only methods that are annotated with #ApiOperation will be scanned and added the Swagger definition.
You can find more information here: https://github.com/swagger-api/swagger-core/wiki/Annotations-1.5.X#apioperation
I need to manage different versions that come in the URL of the Webservice. I'm using a common method in the Webservice for SOAP and REST, and when I tried to get the path in the endPoint class I got the correct path for REST, but not for SOAP(I got a null in this case), does anybody how to get the path in soap?
The url looks like: http://localhost:8083/webService/v1/test and the code is:
#Stateless
#WebService(endpointInterface = "ItestgEndpoint", serviceName="testService")
#Component("testEndpoint")
#Path("/webService")
#Consumes({MediaType.APPLICATION_XML})
#Produces({MediaType.APPLICATION_XML})
public class TestEndpoint implements ItestgEndpoint{
#PUT
#Path("/{version}/test")
#Consumes({MediaType.APPLICATION_XML })
#Produces({MediaType.APPLICATION_XML })
#WebResult(name="testResponse")
public testResponse testEvent(#WebParam(targetNamespace="http://test/web", name="message")
#RequestParam MessageClass message,
#WebParam(name="version") #PathParam("version") String version
) throws TimeoutException, EMSException, ValidationException, AuthenticationException {
logger.info(version);
}
I saw this post SOAP and REST Webservice with one implementation in Java EE, but it didn't solve my problem.
Thanks
SOAP has no "path" like REST has. Depending on the service toolkit you use (CXF, axis, ...) you may need to collect the information in the request that you can extract from the transport mechanism of the soap message. This may or may not be available to you.
Usually, you wire the SOAP service to a path in a configuration. For example in axis, you set the soap servlet dispatcher in the web.xml, same is for cxf. The actual service is then wired in the beans.xml or services.wsdd.
The SOAP handler finds the service by the name of the endpoint and will then send the call to that endpoint which will dispatch it to the right method. The method is in the transmitted SOAP header, not in the URI.
In REST, the identification of the target service/method is in the URI, not in the XML.
So, in my opinion, for SOAP, this is by declaration and the wiring is static, while in REST, you can have the version as a path parameter.
UPDATE: Since everything is possible if you just try hard enough :-) you could rewrite the dispatcher code to allow an extra path parameter on SOAP, maybe the available packages allow some kind of interceptor class, that allows you to rewrite the SOAP header to point to another endpoint, depending on a header attribute that you invent.
Im using enunciate to document my REST API. Im able to generate docs properly and have been using it for quite some time.
My issue now is.. Ive few Service classes with multiple endpoint methods.
public interface UserLoginService {
//This method should be DOcumented
#Path("/login")
#POST
#Consumes({"application/xml", "application/json"})
#Produces({"application/xml", "application/json"})
#Transactional(
readOnly=false,
rollbackForClassName="MyExceptionClass",
propagation=Propagation.REQUIRED
)
#Deprecated
public UserDetails performLogin(LoginObj loginobj);
//This method should be skipped
#Path("/logout")
#GET
#Consumes({"application/xml", "application/json"})
#Produces({"application/xml", "application/json"})
#Transactional(
readOnly=false,
rollbackForClassName="MyException",
propagation=Propagation.REQUIRED
)
public UserDetails logout(#QueryParam("userid") String userid);
}
In those service methods some are public and some are used internally. I wanted to generated doc for only those Rest methods which are supposed to be public. I searched in enunciate couldnt find anyway to skip few methods , though im using exclude api classes to exclude classes.
Is there any way this can be achieved. I dont want to create separate classes only bcoz of documentation. If enunciate dosent do this any other doc tools which can be easily adapted from existing enunciate javadoc written.
FYI, version 1.27 will include the notion of Enunciate Facets that are more flexible in excluding facets of your API.
I was using the old version of enunciate. The latest version has an annotation #ExcludeFromDocumentation. Though I saw this annotation It was written in doc that this is applicable for only Jaxb objs. I saw in their jira a defect raised and fixed for my issue. This annotation now can be used for classes/methods . If used against a REST method then the method will not be documented.