JAX-RS and JAXB_ENCODING - jax-rs

I am using the RestEasy library to do JAX-RS web services. I am not instantiating a JAXBContext in my service methods. Is there a way to make JAX-RS include the "<?xml version...?>" header string in the XML it returns? Here is a sample service method from my code:
#GET
#Path("/patients/{patient_id}/diagnoses/portal_edits")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response getDiagnosisPortalEdits(#Context final UriInfo info,
#PathParam("practice_id") final int practiceId,
#PathParam("patient_id") final long patientId) {
logger.info(SERVICE_NAME + ".getDiagnosisPortalEdits: " + info.getPath());
final DiagnosisPortalEdits diagnosisPortalEdits = new DiagnosisPortalEdits();
diagnosisPortalEdits.getDiagnosisPortalEdits().addAll(DefaultDiagnosisPortalEditService.doLoadForPatientId(practiceId,
patientId));
return Response.ok(diagnosisPortalEdits).build();
}

Add the following annotation to the class:
#XmlHeader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")

Related

Spring SpEL to set RequestMapping path with a list

I want to programmatically set the paths on a rest service. I have this bean method which has all the paths.
public List<String> getSubscribeChannelsForRest() { .. }
This is the rest service
#RestController
public class RestMessageController {
#PostMapping(
path = { "#{somebean.getSubscribeChannelsForRest()[0]}",
"#{somebean.getSubscribeChannelsForRest()[1]}",
"#{somebean.getSubscribeChannelsForRest()[2]}"
})
public String processMessage(#RequestBody String messageBody, HttpServletRequest request) { .. }
The above code works but I want to avoid hard coding the array numbers. There is what I tried.
#PostMapping(
path = { "#{somebean.getSubscribeChannelsForRest()}",
This doesn't work because the spring method RequestMappingHandlerMapping.resolveEmbeddedValuesInPatterns(String[] patterns) takes the above SpEL as a single element array. I've checked the trace logs and all the element in the given list get concatenated .

Bean Validation with JAX-RS (rest-easy): parameter name not recognized

I'm using JAX-RS resources with Bean Validation and integration between these two works as expected.
However, the default error messages generated in case of a validation error report parameter names as arg0, like so
[PARAMETER]
[login.arg0.password]
[password is required]
[]
Corresponding method definition:
#POST //and other JAX-RS annotations
public Response login(
#NotNull
#Valid
LoginBody loginBody) {
[...]
protected static class LoginBody {
#NotNull(message = EMAIL_REQUIRED)
public String email;
#NotNull(message = PASSWORD_REQUIRED)
public String password;
}
While I'm generally fine with this message pattern, what actually is annyoing, is the fact that the original parameter name is not recognized, i. e. I'd rather like to see
login.loginBody.password instead of arg0.
Is there an easy way to fix this, e. g. somehow provide an explicit name for that parameter?
I'm using WildFly Swarm 2017.6.0. From what I found out this means I have resteasy + resteasy-validator + hibernate-validator
Thanks.
You could try to compile your app with -parameters or instruct your IDE to do so, e.g. in case of
eclipse: preferences -> java -> compiler -> "store information about method parameters (usable via reflection)"
With that in place you then need to instruct the Bean Validation infrastructure (e.g. ) hibernate-validator to
use the ReflectiveParameterNamer via META-INF/validation.xml.
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider</parameter-name-provider>
See also Hibernate Validator Configuration
I got something reliably working with the Paranamer library
META-INF/validation.xml:
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/xml/ns/javax/validation/configuration
validation-configuration-1.1.xsd"
version="1.1">
<default-provider>org.hibernate.validator.HibernateValidator
</default-provider>
<message-interpolator>org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator
</message-interpolator>
<traversable-resolver>org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver
</traversable-resolver>
<constraint-validator-factory>org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl
</constraint-validator-factory>
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
To get paranamer working with wildfly I needed to create a parameter-namer jboss-module
and reference that module from the module.xml of the hibernate-validator module.
With that in place I could simply write:
#POST
public Response login(#NotNull #Valid #Named("authRequest") AuthRequest authRequest) {
return Response.ok().build();
}
...
public class AuthRequest {
#NotNull(message = AuthMessages.EMAIL_REQUIRED)
public String email;
#NotNull(message = AuthMessages.PASSWORD_REQUIRED)
public String password;
}
which yields the following response for a request sent via curl:
curl -H "Content-Type: application/json" -H "Accept: application/json" -d '{"email":"foo#bar.com"}' -v http://localhost:8080/javaweb-training/resources/auth
Response:
{"exception":null,"fieldViolations":[],"propertyViolations":[],"classViolations":[],"parameterViolations":[{"constraintType":"PARAMETER","path":"login.authRequest.password","message":"password.required","value":""}],"returnValueViolations":[]}%
... note login.authRequest.password instead of just login.arg0.password
There is a very simple solution: you can set your own error message in the constraint definition as follows
#NotNull(message = "password is required")
If you want a more generic solution based on the JAX-RS parameter annotations you can implement your own simple ParameterNamProvider and register it in validation.xml as follows. This has the advantage of not having to change the jboss module structure. I also didn't have to change any compiler flags...
public class AnnotatedParameterNameProvider implements ParameterNameProvider {
#Override
public List<String> getParameterNames(Constructor<?> constructor) {
return lookupParameterNames(constructor.getParameterAnnotations());
}
#Override
public List<String> getParameterNames(Method method) {
return lookupParameterNames(method.getParameterAnnotations());
}
private List<String> lookupParameterNames(Annotation[][] annotations) {
final List<String> names = new ArrayList<>();
if (annotations != null) {
for (Annotation[] annotation : annotations) {
String annotationValue = null;
for (Annotation ann : annotation) {
annotationValue = getAnnotationValue(ann);
if (annotationValue != null) {
break;
}
}
// if no matching annotation, must be the request body
if (annotationValue == null) {
annotationValue = "requestBody";
}
names.add(annotationValue);
}
}
return names;
}
private static String getAnnotationValue(Annotation annotation) {
if (annotation instanceof HeaderParam) {
return ((HeaderParam) annotation).value();
} else if (annotation instanceof PathParam) {
return ((PathParam) annotation).value();
} else if (annotation instanceof QueryParam) {
return ((QueryParam) annotation).value();
}
return null;
}
}
In validation.xml:
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.1.xsd"
version="1.1">
<parameter-name-provider>com.yourcompany.providers.AnnotatedParameterNameProvider</parameter-name-provider>
</validation-config>
Note that you can also customize how the error message is formatted by implementing your own MessageInterpolator and registering it in the validation.xml
Can you try to implement an exception mapper for ConstraintViolationExceptions and see if the information you have there (the list of constraint violations) can help you to obtain the parameter name?
Updated version of #thomas-darimont for Hibernate Validator 6.X.
Variant#1 - with build in Java 8 (using -parameters compile parameter)
Specify dependencies (gradle example):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
Specify validator(s):
#GET
#Path("user/{userId}")
public Response getUser(#Size(min = 2) #PathParam("userId") String userId) {
return null;
}
Note: org.hibernate.validator.internal.engine.DefaultParameterNameProvider will return parameter names obtained from the Java reflection API.
Variant #2 - use ParaNamer library. (xml configuration)
In case you don't want to be dependant on compilation flag.
Specify dependencies (gradle example):
// Define explicit hibernate validator 6.x
implementation('org.hibernate.validator:hibernate-validator:6.0.13.Final')
implementation('org.jboss.resteasy:resteasy-validator-provider-11:3.6.2.Final') {
// Exclude transitive hibernate validator 5.x
exclude group: 'org.hibernate', module: 'hibernate-validator'
}
// ParaNamer library
implementation('com.thoughtworks.paranamer:paranamer:2.8')
Specify validator(s):
#GET
#Path("user/{userId}")
public Response getUser(#Size(min = 2) #PathParam("userId") String userId) {
return null;
}
Put <project_dir>/src/main/resources/META-INF/validation.xml
<?xml version="1.0" encoding="UTF-8"?>
<validation-config
xmlns="http://xmlns.jcp.org/xml/ns/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/configuration
http://xmlns.jcp.org/xml/ns/validation/configuration/validation-configuration-2.0.xsd"
version="2.0">
<parameter-name-provider>org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider</parameter-name-provider>
</validation-config>
Note: Since Hibernate Validator 6.x org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider is deprecated, use org.hibernate.validator.parameternameprovider.ParanamerParameterNameProvider instead.
Question: Can I configure this with Java-code style only?
Unfortunately, no. (See details here).

How to add http header into WCF channel

I have MVC client that invokes a WCF service. The MVC client needs to pass one custom header in httprequest. The MVC client is also using Unity for DI.
I have already gone through SO POST and others links but they are all suggesting to use message inspector and custom behavior(which might be the correct way) but i'm looking for quick and dirty way because this will be temporary solution.
// Unity type Registration
public static void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IDocumentManagementChannel>(new PerRequestLifetimeManager(),
new InjectionFactory(f=> CreateDocumentManagementChannel()));
}
private static IDocumentManagementChannel CreateDocumentManagementChannel()
{
var factory = new ChannelFactory<IDocumentManagementChannel>("BasicHttpEndPoint");
var channel = factory.CreateChannel();
// How do i add HttpHeaders into channel here?
return channel
}
In the code above How do i add custom header after i create a channel?
1- Below code should send the soap header from MVC
string userName = Thread.CurrentPrincipal.Identity.Name;
MessageHeader<string> header = new MessageHeader<string>(userName);
OperationContext.Current.OutgoingMessageHeaders.Add(
header.GetUntypedHeader("String", "System"));
2- And this code should read it on WCF
string loginName = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("String", "System");
3- As for the channel, i recommend you create your custom System.ServiceModel.ClientBase as follows:
public abstract class UserClientBase<T> : ClientBase<T> where T : class
{
public UserClientBase()
{
string userName = Thread.CurrentPrincipal.Identity.Name;
MessageHeader<string> header = new MessageHeader<string>(userName);
OperationContext.Current.OutgoingMessageHeaders.Add(
header.GetUntypedHeader("String", "System"));
}
}
4- Create a custom client class that inherits from UserClientBase and use the base channel internally to call your IxxService which is the T here.

Possible to do custom method processing with ResteasyClient (Proxy Framework)?

Is it possible to register a DynamicFeature with an ResteasyClient (Proxy Framework) similar to what can be done on server side?
So something similar to this:
final ResteasyClient client = new ResteasyClientBuilder().build();
client.register(new MyDynamicFeature());
Where MyDynamicFeature implements DynamicFeature
I'm trying to figure out how to have a ClientResponseFilter check the http return status depending on the annotation that is present on the resource method, and the DynamicFeature appeared to be the most promising lead to get access to the ResourceInfo.
So essentially, I want to do something like this:
#POST
#Path("some/path/user")
#ExpectedHttpStatus(201) // <- this would have to be passed on somehow as expectedStatus
User createUser(User request);
And then in the ClientResponseFilter (or any other solution) something like this:
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
if (responseContext.getStatus() != expectedStatus) {
// explode
}
}
Cause in the ClientResponseFilter, I don't see any way to know what the resource method is that defined the REST call that the filter is currently analyzing.
And the problem is that the framework right now only checks whether the response status is success, it doesn't check whether it's 200 or 201 and we'd like to refine that.
Here are some articles that seems to explain something very similar, yet this doesn't seem to be working with the ClientResponseFilter / ResteasyClient:
Match Filter with specific Method through NameBinding on RESTeasy
What is the proper replacement of the Resteasy 3.X PreProcessInterceptor?
First of all, I can't take credit for the solution really, but I'm going to paste the answer here.
Also, you could ask why the heck we're doing this? Because we need / want to test that the service returns the right http status, but unfortunately the service we are testing does not always return the same http status for the same http method.
E.g. in the example below, the post returns HttpStatus.OK, and another post method of the same service could return HttpStatus.CREATED.
Here's the solution we ended up with, a combination of ClientResponseFilter:
import java.io.IOException;
import java.util.UUID;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
/**
* {#link ClientResponseFilter} which will handle setting the HTTP StatusCode property for use with
* {#link HttpStatusResponseInterceptor}
*/
public class HttpStatusResponseFilter implements ClientResponseFilter {
public static final String STATUS_CODE = "StatusCode-" + UUID.randomUUID();
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
requestContext.setProperty(STATUS_CODE, responseContext.getStatusInfo());
}
}
And ReaderInterceptor:
import java.io.IOException;
import java.lang.annotation.Annotation;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
/**
* {#link ReaderInterceptor} which will verify the success HTTP status code returned from the server against the
* expected successful HTTP status code {#link SuccessStatus}
*
* #see HttpStatusResponseFilter
*/
public class HttpStatusResponseInterceptor implements ReaderInterceptor {
#Override
public Object aroundReadFrom(ReaderInterceptorContext interceptorContext) throws ServerErrorException, IOException {
Status actualStatus = (Status) interceptorContext.getProperty(HttpStatusResponseFilter.STATUS_CODE);
if (actualStatus == null) {
throw new IllegalStateException("Property " + HttpStatusResponseFilter.STATUS_CODE + " does not exist!");
}
Status expectedStatus = null;
for (Annotation annotation : interceptorContext.getAnnotations()) {
if (annotation.annotationType() == SuccessStatus.class) {
expectedStatus = ((SuccessStatus) annotation).value();
break;
}
}
if (expectedStatus != null && expectedStatus != actualStatus) {
throw new ServerErrorException(String.format("Invalid status code returned. Expected %d, but got %d.",
expectedStatus.getStatusCode(), actualStatus.getStatusCode()), actualStatus);
}
return interceptorContext.proceed();
}
}
We register both those when we create the client:
final ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
client.register(new HttpStatusResponseFilter());
client.register(new HttpStatusResponseInterceptor());
And the SuccessStatus is an annotation that we use to annotate the methods that we want to specifically check, e.g. like that:
#POST
#Path("some/foobar")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#SuccessStatus(Status.OK)
Foobar createFoobar(Foobar foobar);
It's not possible to register a DynamicFeature in your client.
See the DynamicFeature documentation:
A JAX-RS meta-provider for dynamic registration of post-matching
providers during a JAX-RS application setup at deployment time.
Dynamic feature is used by JAX-RS runtime to register providers that
shall be applied to a particular resource class and method and
overrides any annotation-based binding definitions defined on any
registered resource filter or interceptor instance.
Providers implementing this interface MAY be annotated with #Provider
annotation in order to be discovered by JAX-RS runtime when scanning
for resources and providers. This provider types is supported only as
part of the Server API.
The JAX-RS Client API can be utilized to consume any Web service exposed on top of a HTTP protocol, and is not restricted to services implemented using JAX-RS.
Please note the JAX-RS Client API does not invoke the resource classes directly. Instead, it generates HTTP requests to the server. Consequently, you won't be able to read the annotations from your resource classes.
Update 1
I'm not sure if this will be useful for you, but since you would like to access the server resource classes from your client, it would be interesting to mention that Jersey provides a proxy-based client API (org.glassfish.jersey.client.proxy package).
The basic idea is you can attach the standard JAX-RS annotations to an interface, and then implement that interface by a resource class on the server side while reusing the same interface on the client side by dynamically generating an implementation of that using java.lang.reflect.Proxy calling the right low-level client API methods.
This example was extracted from Jersey documentation:
Consider a server which exposes a resource at http://localhost:8080. The resource can be described by the following interface:
#Path("myresource")
public interface MyResourceIfc {
#GET
#Produces("text/plain")
String get();
#POST
#Consumes("application/xml")
#Produces("application/xml")
MyBean postEcho(MyBean bean);
#GET
#Path("{id}")
#Produces("text/plain")
String getById(#PathParam("id") String id);
}
You can use WebResourceFactory class defined in this package to access the server-side resource using this interface. Here is an example:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/");
MyResourceIfc resource = WebResourceFactory.newResource(MyResourceIfc.class, target);
String responseFromGet = resource.get();
MyBean responseFromPost = resource.postEcho(myBeanInstance);
String responseFromGetById = resource.getById("abc");
I'm not sure if RESTEasy provides something similar to it.
Update 2
RESTEasy also provides a proxy framework. See the documentation:
RESTEasy has a client proxy framework that allows you to use JAX-RS annotations to invoke on a remote HTTP resource. The way it works is that you write a Java interface and use JAX-RS annotations on methods and the interface. For example:
public interface SimpleClient {
#GET
#Path("basic")
#Produces("text/plain")
String getBasic();
#PUT
#Path("basic")
#Consumes("text/plain")
void putBasic(String body);
#GET
#Path("queryParam")
#Produces("text/plain")
String getQueryParam(#QueryParam("param") String param);
#GET
#Path("matrixParam")
#Produces("text/plain")
String getMatrixParam(#MatrixParam("param") String param);
#GET
#Path("uriParam/{param}")
#Produces("text/plain")
int getUriParam(#PathParam("param") int param);
}
RESTEasy has a simple API based on Apache HttpClient. You generate a proxy then you can invoke methods on the proxy. The invoked method gets translated to an HTTP request based on how you annotated the method and posted to the server. Here's how you would set this up:
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget) target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
simple.putBasic("hello world");
Alternatively you can use the RESTEasy client extension interfaces directly:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/base/uri");
SimpleClient simple = target.proxy(SimpleClient.class);
simple.putBasic("hello world");
[...]
The framework also supports the JAX-RS locator pattern, but on the client side. So, if you have a method annotated only with #Path, that proxy method will return a new proxy of the interface returned by that method.
[...]
It is generally possible to share an interface between the client and server. In this scenario, you just have your JAX-RS services implement an annotated interface and then reuse that same interface to create client proxies to invoke on the client-side.
Update 3
Since you are already using RESTEasy Proxy Framework and assuming your server resources implement the same interfaces you are using to create your client proxies, the following solution should work.
A ProxyFactory from Spring AOP, which is already packed with RESTEasy Client will do trick. This solution, basically, creates a proxy of the proxy to intercept the method that is being invoked.
The following class stores the Method instance:
public class MethodWrapper {
private Method method;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
And the following code makes the magic:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/api");
ExampleResource resource = target.proxy(ExampleResource.class);
MethodWrapper wrapper = new MethodWrapper();
ProxyFactory proxyFactory = new ProxyFactory(resource);
proxyFactory.addAdvice(new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
wrapper.setMethod(invocation.getMethod());
return invocation.proceed();
}
});
ExampleResource resourceProxy = (ExampleResource) proxyFactory.getProxy();
Response response = resourceProxy.doSomething("Hello World!");
Method method = wrapper.getMethod();
ExpectedHttpStatus expectedHttpStatus = method.getAnnotation(ExpectedHttpStatus.class);
int status = response.getStatus();
int expectedStatus = annotation.status();
For more information, have a look at the documentation:
MethodInterceptor
ProxyFactory
MethodInvocation

How do I achieve through Jboss Resteasy interceptors?

I am working on the Jboss Resteasy API to implement the REST services on Jboss server.I am new to this area. Can someone help me out here...
There is a Rest Service method with custom annotation(VRestAuto) like below.
#POST
#Produces("text/json")
#Path("/qciimplinv")
#Interceptors(VRestInterceptor.class)
public String getInvSummary(#VRestAuto("EnterpriseId") String enterpriseId,String circuitType){
....
businessMethod(enterpriseId,circuitType);
....
}
#VRestAuto annotation tell us 'enterpriseId' value is available in the user session.
User pass the circuitType alone as the POST parameter in the Rest Client tool.Should ideally read the enterpriseid from session and invoke the Rest service with these two parameters(enterpriseid,circuitType).
To achieve the above functionality, implemented the Interceptors class (VRestInterceptor) like below:
public class VRestInterceptor implemnets PreProcessInterceptor,AcceptedByMethod {
public boolean accept(Class declaring, Method method) {
for (Annotation[] annotations : method.getParameterAnnotations()) {
for (Annotation annotation : annotations) {
if(annotation.annotationType() == VRestAuto.class){
VRestAuto vRestAuto = (VRestAuto) annotation;
return vRestAuto.value().equals("EnterpriseId");
}
}
}
return false;
}
Override
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException { ......}
}
I was able to verify the VRestAuto annotation in the accept method. But in the preProcess Method, how can I call the REST method with two parameters(enterpriseid, circuitType)?
if these interceptors are not suits, Are there any other interceptors best to this functionality?
Your help is highly appreciated .
Why not forget setting the enterpriseId value when the method is called and instead just inject the HttpServletRequest and use that to grab the session and value?
#POST
#Produces("text/json")
#Path("/qciimplinv")
public String getInvSummary(String circuitType, #Context HttpServletRequest servletRequest) {
HttpSession session = servletRequest.getSession();
String enterpriseId = session.getAttribute("EnterpriseId").toString();
....
businessMethod(enterpriseId,circuitType);
....
}