Jersey doesn't honour required=true - java

I have following class annotated with JAX-RS:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Blub {
#XmlElement (required = true)
private String author;
with getter/and setter. I am using this object as parameter to a method:
#Path("/createBlub")
#POST
public ReplyObject createBlub(Blub blub) {
try {
...
//process here
return ReplyObject.success("blub", result);
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
I am expecting Jersey to throw an Exception if in the parameter blub object the field author is not set. However, Jersey doesn't seem to care for the required attribute. I remember that it worked in other projects, but don't see the difference.
I am using jersey 1.12 without anything else:
<servlet>
<servlet-name>JerseyServletContainerAdmin</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>xxx.yyy.zzz.admin</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.DisableXmlSecurity
</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JerseyServletContainerAdmin</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>
thanks in advance
regards
Leon

Jersey is using JAXB for marshaling and unmarshalling which just means transforming the data into a Java object. If you want validation you have to do it yourself.
Proposed solutions for that include creating a custom MessageBodyReader to add the validation on unmarshalling or creating a more reusable implementation by writing a custom ContextResolver as described in this post: Jersey JAX-RS and JAXB Schema Validation.
JSR303 support would have been ideal for this sort of thing (working nicely with JSON data) but looks like that will be available only in 2.x. It should be possible though to adapt the solution from the above post and use JSR303.
And if the implementation gets too complicated you can always let Jersey create the object and then first-thing-first call some validation method of yours on the object, which normally shouldn't be more than a one liner, something like:
#Path("/createBlub")
#POST
public ReplyObject createBlub(Blub blub) {
ValidationUtils.<Blub>validate(blub);
...

Related

Rest JSON with Jackson #type not added in json

I added jackson to my rest jersey project because the default json mapper of jersey gives JSON Objects when list element size is 1 and giving JSON Arrays when element size was 2 or more.
So jackson works well with that part, but the default json mapper of jersey (idk what is the default) can give me a propperty called #type, when implementing jackson the propperty is not added in the JSON return.
before i was ussing: #XmlType(name = "TypeName") in the #XmlRootElement
now i'm trying with #JsonTypeInfo, #JsonTypeName with jackson 2.5.4 and 2.22.2 but no success.
How can i add the #type propperty?
Edit 1: (Adding web.xml)
in web.xml i have this:
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.package.webservice</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
Edit 2: (adding jackson versions)
jackson-jaxrs-json-provider:2.7.4
jersey-media-json-jackson:2.22.2
jackson-annotations:2.7.4
jackson-jaxrs-base:2.7.4
jackson-databind:2.7.4
jackson-core:2.7.4
jackson-module-jaxb-annotations:2.7.4
jersey-entity-filtering:2.22.2
jersey-common:2.22.2
Like #peeskillet said:
Jackson 1.x is the default JSON provider in JBoss 7
I had to remove provided jackson from JBoss AS 7.1.1, got steps here
I changed web.xml servlet like this:
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.package.MyApplication</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
I had to Create the class "my.package.MyApplication":
#ApplicationPath("resources")
public class MyApplication extends PackagesResourceConfig {
public MyApplication() {
super("my.package.webservice;com.fasterxml.jackson.jaxrs.json");
}
}
And jackson 2.x is working now, thank you #peeskillet for helping me.
next was create a propperty called #type like this:
#JsonProperty(value = "#type")
String xmlType = "TypeName";
And JSON returns #type propperty.

A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/json was not found

I am new to using jersy for implementing rest api
I get the below error, when I call the products service.
com.sun.jersey.api.MessageException: A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/json was not found.
Here is my code:
#GET
#Path("/products")
#Produces(MediaType.APPLICATION_JSON)
public Response productSearch(#QueryParam("name") String name)
{
List<Product> products = new ArrayList<>();
products.add(new Product("PRDNAME", "PRDCOST", "PRDMODEL"));
return Response.ok( products).build();
}
I also tried this:
#GET
#Path("/products")
#Produces(MediaType.APPLICATION_JSON)
public List<Product> productSearch(#QueryParam("name") String name)
{
List<Product> products = new ArrayList<>();
products.add(new Product("PRDNAME", "PRDCOST", "PRDMODEL"));
return products;
}
The below is the setup of my environment:
Tomcat 8,
Jersey libraries: jersey-bundle-1.19.1,
Not using maven
web.xml:
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.test.rest</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
I tried using the below init-param also:
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
I tried returning GenericEntity, but had the same issue.
return Response.ok().entity(new GenericEntity<List<Product>>(products) {}).build();
I also tried using
jersey-core-1.8.jar
jersey-json-1.8.jar
jersey-server-1.8.jar
instead of jersey-bundle-1.19.1
I don't have any issues when I return string.
I understand, I am missing the json media type dependency, but could not figure it out.
This error happens on the tomcat server while the client doesn't receive response obviously.
Maybe you forgot to add
#XmlRootElement
annotation on top of model class definition.
For your "Product" model, I guess it would be something like
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Product{
....
}
I had a similar exception and this solved it for me.

Intercept JAX-RS Request: Register a ContainerRequestFilter with tomcat

I am trying to intercept a request to my JAX-RS webservice by a ContainerRequestFilter. I want to use it with a custom annotation, so I can decorate certain methods of the webservice. This should enable me to handle requests to this methods based on the information whether they are made on a secure channel
or not, before the actual method is executed.
I tried different approaches, searched several posts and then implemented mostly based on the answer by Alden in this post.
But I can't get it working.
I have a method test in my webservice decorated with my custom annotation Ssl.
#POST
#Path("/test")
#Ssl
public static Response test(){
System.out.println("TEST ...");
}
The annotation looks like this:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface Ssl {}
Then I setup a filter implementation
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
#Ssl
#Provider
public class SslInterceptor implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
System.out.println("Filter executed.");
}
}
But the filter is never executed nor there occur any error messages or warnings. The test method runs fine anyway.
To resolve it, I tried to register the filter in the web.xml as described here.
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.my.packagewithfilter</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.my.packagewithfilter.SslInterceptor</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.my.packagewithfilter</param-value>
</init-param>
</servlet>
But that also doesn't work. What am I missing? Any ideas how to make that filter work? Any help is really appreciated!
You're using JAX-RS 2.0 APIs (request filters, name binding, ...) in your classes but Jersey 1 proprietary init params in your web.xml (package starting with com.sun.jersey, Jersey 2 uses org.glassfish.jersey). Take a look at this answer and at these articles:
Registering Resources and Providers in Jersey 2
Binding JAX-RS Providers to Resource Methods
Just compiling the answer from Michael Gajdos to help someone who do not want open more tabs:
When you are using Jersey-2 you must use the follow configuration to register your filter into the web.xml
jersey.config.server.provider.classnames
instead of
com.sun.jersey.spi.container.ContainerRequestFilters (jersey-1x)
<!-- This is the config needed -->
<servlet>
//...
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>com.your_package_path.yourClassFilter</param-value>
</init-param>
//...
</servlet>
Have a look at this blog post for the more 'classical' approaches (without using the annotation)

Multiple endpoints with Resteasy

I have two separate handfuls of REST services in one application. Let's say a main "people" service and a secondary "management" service. What I want is to expose them in separate paths on the server. I am using JAX-RS, RESTEasy and Spring.
Example:
#Path("/people")
public interface PeopleService {
// Stuff
}
#Path("/management")
public interface ManagementService {
// Stuff
}
In web.xml I currently have the following set-up:
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/public</param-value>
</context-param>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/public/*</url-pattern>
</servlet-mapping>
The PeopleService and ManagementService implementations are just Spring beans.
Above web.xml configuration will expose them both on /public (so having /public/people and /public/management respectively).
What I want to accomplish is to expose the PeopleService on /public, so that the full path would become /public/people and expose the ManagementService on /internal, so that its full path would become /internal/management.
Unfortunately, I cannot change the value of the #Path annotation.
How should I do that?
actually you can. After few hours of debugging I came up with this:
1) Declare multiple resteasy servlets in your web.xml (two in my case)
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/openrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PublicService</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>private-resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/protectedrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PrivateService</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>private-resteasy-servlet</servlet-name>
<url-pattern>/protectedrest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/openrest/*</url-pattern>
</servlet-mapping>
Please pay attention to the fact that we initialize personal resteasy.servlet.mapping.prefix and resteasy.resources for each our servlet.
Please don't forget to NOT include any botstrap classes as filters or servlets! And disable autoscan as well.
2) Create a filter that cleans up application from the RESTeasy's global information that it saves in context:
public class ResteasyCleanupFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
request.getServletContext().setAttribute(ResteasyProviderFactory.class.getName(), null);
request.getServletContext().setAttribute(Dispatcher.class.getName(), null);
chain.doFilter(request, response);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Register it for any request to your services (here I used it for all requests for simplisity):
<filter>
<filter-name>CleanupFilter</filter-name>
<filter-class>com.mycompany.ResteasyCleanupFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CleanupFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Thats it!Now you have two different REST services which lays under different prefixes : /openrest which meant to service all public requests and /protectedrest that takes care about all the private stuff in the app.
So why does it work (or why it does not work otherwise)?
When you call openrest instance for the first time it tries to initalize itself and when done saves the state in the global servletContext like this :
servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
And if you will let it be your call to your second /protectedrest will get the SAME configuration! That is why you need to clean up this information some where. That is why we used our CleanupFilter which empty the context so brand new rest servlet could initialize itself with all the init parameters we declared.
This is a hack, but it does the trick.
This solution was tested for RESTEasy 2.3.6
EDITED
Works with 3.0.9.final as well!
AFAIK, you cannot have multiple servlet mappins for your JAX-RS implementation.
What you could do is: map RESTEasy to '/' (or '/api' for example if your application has other resources to serve and you don't want the JAX-RS part to interfere), then have the following #Path annotations:
#Path("/public/people")
public interface PeopleService {
// Stuff
}
#Path("/internal/management")
public interface ManagementService {
// Stuff
}

Is there a way to change the output type for restful webservices by a parameter in java?

I wanna use the same restful webservice path to produce xml or json, or a xml with xsl header.
Is it possible using any framework(jersey or resteasy) in java?
Eg:
#Path("/person")
public class PersonService {
#GET
#Path("/find")
public Person find(#QueryParam("name") String name, #QueryParam("outputformat") String outputformat) {
// do some magic to change output format
return dao.findPerson(name);
}
}
Maybe you can write a servlet filter that takes the query string and uses it to set the request's accept header accordingly, then jersey should dispatch to whatever method is annotated with #Consumes that matches.
For example, servlet filter intercepts request "?outputFormat=xml" and sets the Accept header to "application/xml". Then, jersey should dispatch to whichever method in your resource is annotated with: #Consumes("application/xml")
This question might help: REST. Jersey. How to programmatically choose what type to return: JSON or XML?
You could also easily customize Jersey ServletContainer and you won't require another param to pass along. You could negotiate representation using .json or .xml in your URL.
public class MyServletContainer extends ServletContainer {
#Override
protected void configure(ServletConfig servletConfig, ResourceConfig resourceConfig, WebApplication webApplication) {
super.configure(servletConfig, resourceConfig, webApplication);
resourceConfig.getMediaTypeMappings().put("json", MediaType.APPLICATION_JSON_TYPE);
resourceConfig.getMediaTypeMappings().put("xml", MediaType.APPLICATION_XML_TYPE);
}
}
In your web.xml, you could define the custom servlet as shown below.
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.MyServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.sun.jersey.MyWebApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
You could use Jersey and use the annotation #Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}). You would need to add a mapping feature for POJOs in your application as well. The include in the web.xml file would be
<filter>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</filter>
Other configurations would be necessary, but it is all in the documentation http://jersey.java.net/nonav/documentation/latest/user-guide.html

Categories

Resources