Jersey 2.1 - Consuming collection of POJO as JSON string - jersey

I have a collection of entities (List) which I need to convert to/from json.
The POJO:
public class Task {
private Long id;
private String title;
private Boolean done;
(...)
}
Jersey produces the following result
[{"id":1,"title":"T1","done":false},{"id":2,"title":"T2","done":false}]
when I call the method:
#GET
#Override
#Produces("application/json")
public List<Task> findAll() {
(...)
return tasks;
}
So far so good.
Now, I need to consume a similar JSON string. I assumed that the following method would do the trick:
#PUT
#Consumes("application/json")
public void save(List<Task> tasks) {
(...)
}
But instead I get the error below:
SEVERE: line 1:0 no viable alternative at input
'"[{\"id\":1,\"title\":\"T1\",\"done\":true},{\"id\":2,\"title\":\"T2\",\"done\":false}]"'
What am I doing wrong? Is that even possible?
Jersey's dependencies:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.1</version>
</dependency>
Here is the web.xml configuration
<servlet>
<servlet-name>Jersey Servlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>my.rest.package</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Servlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>

It seems that you're sending corrupted JSON as entity in your message body, something like:
POST http://localhost:9998/test/list
Accept: application/json
Content-Type: application/json
"[{\"a\":\"a\",\"b\":1,\"c\":1}]"
I was able to get the same error as you with the entity like this (line 1:0 no viable alternative at input '"[{\"a\":\"a\",\"b\":1,\"c\":1}]"').
Make sure you're sending the valid JSON to your REST service, i.e. by registering a logging filter in your Application:
new ResourceConfig()
.packages("my.rest.package")
// Register logging filter and print entity.
.register(new LoggingFilter(LOGGER, true));

Alternatively, you can accept the incoming JSON as String and parse it within the method using any standard parser.
#PUT
public void save(String s) {
// parse s
}

Type parameters are not available after type erasure.
Try using array instead of list:
#PUT
#Consumes("application/json")
public void save(Task[] taskArray) {
List<Task> tasks = Arrays.asList(taskArray);
(...)
}

Related

Prevent Jersey from returning XML for HTTP OPTIONS

I have a JAX-RS service defined like the below, using Jersey, and is deployed on WebLogic 12.2.1. It works fine.
#Path("/Profile")
public class ProfileServices {
#POST
#Consumes(APPLICATION_JSON)
#Produces(APPLICATION_JSON)
#Path("{ServiceName}")
public Response service(#PathParam("ServiceName") String serviceName, String message) {
...
}
}
However, when calling the service with HTTP OPTIONS, Jersey returned a WADL along with response header Allow: POST,OPTIONS.
<ns0:application xmlns:ns0="http://wadl.dev.java.net/2009/02">
<ns0:doc ns1:generatedBy="Jersey: 2.22.4 2016-11-30 13:33:53" xmlns:ns1="http://jersey.java.net/"/>
<ns0:grammars/>
<ns0:resources base="https://xxx.xxx.xxx.xxx:nnnn/">
<ns0:resource path="XXXXX">
<ns0:method id="service" name="POST">
<ns0:request>
<ns0:representation mediaType="application/json"/>
</ns0:request>
<ns0:response>
<ns0:representation mediaType="application/json"/>
</ns0:response>
</ns0:method>
</ns0:resource>
</ns0:resources>
</ns0:application>
How do I prevent Jersey from returning this WADL? I don't want to disclose the Jersey version to the user. I'm ok with the response status and headers, but I don't want to return the content. If it is not possible, can the Jersey information not be returned?
I am using javax.ws.rs.core.Application and do not require web.xml to specify the servlet.
EDIT
I had in fact override the following method in Application:
#Override
public Map<String, Object> getProperties() {
Map<String, Object> props = new HashMap<>();
props.put("jersey.config.server.wadl.disableWadl", true);
return props;
}
But when I re-deployed, I got the following exception:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=WadlApplicationContext,parent=JaxRsMonitoringListener,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1838803099)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:75)
at org.jvnet.hk2.internal.Utilities.justInject(Utilities.java:1012)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:1008)
at org.jvnet.hk2.internal.ServiceLocatorImpl.inject(ServiceLocatorImpl.java:986)
at org.glassfish.jersey.server.ApplicationHandler.initialize(ApplicationHandler.java:617)
at org.glassfish.jersey.server.ApplicationHandler.access$500(ApplicationHandler.java:184)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:350)
at org.glassfish.jersey.server.ApplicationHandler$3.call(ApplicationHandler.java:347)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.processWithException(Errors.java:255)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:347)
at org.glassfish.jersey.servlet.WebComponent.<init>(WebComponent.java:392)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:177)
at org.glassfish.jersey.servlet.ServletContainer.init(ServletContainer.java:369)
at javax.servlet.GenericServlet.init(GenericServlet.java:244)
...
What worked for me was disabling it in /src/main/webapp/WEB-INF/web.xml like this:
<servlet>
<servlet-name>REST</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.jackson.JacksonFeature;
org.glassfish.jersey.media.multipart.MultiPartFeature;
</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.your.packagedir</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.wadl.disableWadl</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

CORS Response Filter not invoked Resteasy / JAX-RS 2.0

I am trying to implement a CORS response filer to allow cross-domain reference from my JavaScript front-end. I am using Wildfly 10.0.final which comes with Resteasy that is JAX-RS 2.0 compliment if I understand correctly.
EDIT: added #Provider to the CorsResponseFilter, and as a singleton to the RestServiceConfig.
What do I need to do to get my CorsResponseFilter invoked?
PS. Read these posts, but they didn't help solving the problem.
ContainerRequestFilter ContainerResponseFilter dosent get called
ResourceConfig and Application
CorsResponseFilter.java
#Provider
public class CorsResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Access-Control-Allow-Origin", "*");
//headers.add("Access-Control-Allow-Origin", "http://podcastpedia.org"); //allows CORS requests only coming from podcastpedia.org
headers.add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
headers.add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia");
headers.add("Access-Control-Max-Age", "1209600");
}
}
RestServiceConfig.java
public class RestServiceConfig extends Application {
private final Set<Object> singletons = new HashSet<>();
public RestServiceConfig() {
singletons.add(new CorsResponseFilter());
singletons.add(new ApplicationService());
singletons.add(new TweetObsService());
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
web.xml
...
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.mycorp.myapp.service.RestServiceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>org.clearbyte.obs.service.CorsResponseFilter</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/service</param-value>
</context-param>
...
Wildfly log console
13:56:01,672 INFO [org.jboss.resteasy.resteasy_jaxrs.i18n] RESTEASY002225: Deploying javax.ws.rs.core.Application: class org.clearbyte.obs.service.RestServiceConfig
13:56:01,672 INFO [org.jboss.resteasy.resteasy_jaxrs.i18n] RESTEASY002200: Adding class resource org.clearbyte.obs.service.TweetObsService from Application class org.clearbyte.obs.service.RestServiceConfig
13:56:01,672 INFO [org.jboss.resteasy.resteasy_jaxrs.i18n] RESTEASY002200: Adding class resource org.clearbyte.obs.service.ApplicationService from Application class org.clearbyte.obs.service.RestServiceConfig
So I've started over from scratch with a new project to eliminate error sources. Thanks for the input on using #Provider and adding OPTIONS. Plus I removed all configuration REST from the web.xml.
#Provider is essential for the Filter to work
ServiceCorsFilter.java
#Provider
public class ServiceCorsFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().putSingle("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().putSingle("Access-Control-Allow-Methods", "OPTIONS, GET, POST, PUT, DELETE");
responseContext.getHeaders().putSingle("Access-Control-Allow-Headers", "Content-Type");
}
}
#ApplicationPath makes web.xml configuration obsolete
ServiceConfig.java
#ApplicationPath("service")
public class ServiceConfig extends Application {
private Set<Object> singletons = new HashSet<>();
public ServiceConfig() {
singletons.add(new UserServiceV1());
singletons.add(new ServiceCorsFilter());
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
This is what is left in the web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<display-name>MyApp</display-name>
<!-- No REST related config due the the #Provider and inheritance of Application-->
</web-app>
I would try declaring it like a standard filter, not a param of the servlet dispatcher:
<filter>
<filter-name>CorsHeadersFilter</filter-name>
<filter-class>org.clearbyte.obs.service.CorsResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsHeadersFilter</filter-name>
<url-pattern>/service/*</url-pattern>
</filter-mapping>
Some browsers (namely: Chrome) send an OPTION request before issuing their request.
Since you explicitly specify 'GET' 'PUT' 'POST' 'DELETE', the 'OPTION' call does not get the headers information :)
Adding OPTION to your list should solve the issue

RestEasy with Spring renders no answer

I can see that the FilterDispatcher is called (by debugger), but it doesn't seem to find the service to call. I've got trouble grasping how RestEasy actually maps between resources defined in Spring and RestEasy.
Main story: Getting http://my.local.no:8087/rest/typeaheads/h only renders 404
web.xml:
...
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>restFilterDispatcher</filter-name>
<filter-class>org.jboss.resteasy.plugins.server.servlet.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>restFilterDispatcher</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
...
resteasy resource is set up by bean:
#Configuration
#ComponentScan(basePackageClasses = TypeaheadsRestService.class)
public class SpringConfig {
}
TypeaheadsRestService.java:
#Resource
#Path("/typeaheads")
public class TypeaheadsRestService {
#GET
#Path("/{search}")
#Produces(MediaType.APPLICATION_JSON)
public List<NameUrl> get(#PathParam("search") String search) {
...
}
}
The RestEasy SpringContextLoaderListener seem to be the missing part. I created a stripped down problem from RestEasy example and used it. For my somewhat more complex application however it would not work. That is probably because it overrides the deprecated createContextLoader-method. In Spring 3 ContextLoaderListener is an instance of ContextLoader. So I reimplemented it like this:
public class MyContextLoaderListener extends ContextLoaderListener {
private SpringContextLoaderSupport springContextLoaderSupport = new SpringContextLoaderSupport();
#Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
this.springContextLoaderSupport.customizeContext(servletContext, applicationContext);
}
}
I originally tried to do the customizeContext(...) in a bean initialisation. That worked in RestEasy 2.2.1.GA, but not in 2.3.4.FINAL.

NoSuchBeanDefinitionException with spring and gwt (requestFactory)

I get this error with a gwt (using requestfactory) and spring
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.calibra.server.service.AccountService] is defined: expected single bean but found 0:
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:271)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at org.calibra.server.SpringServiceLocator.getInstance(SpringServiceLocator.java:24)
at com.google.web.bindery.requestfactory.server.LocatorServiceLayer.createServiceInstance(LocatorServiceLayer.java:56)
My service locator
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(
RequestFactoryServlet.getThreadLocalServletContext());
return context.getBean(clazz);
}
}
My spring service
#Service
public class AccountServiceImpl implements AccountService{
#Override
public void addNewAccount(Account account) {
...
}
#Override
public List<Account> loadAllAccounts() {
...
}
}
Gwt requestContext, reference my spring service
#Service(value=AccountService.class, locator=SpringServiceLocator.class)
public interface AccountRequest extends RequestContext {
Request<Void> addNewAccount(AccountProxy account);
Request<List<AccountProxy>> loadAllAccounts();
}
my web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>gwtRequest</servlet-name>
<servlet-class>com.google.web.bindery.requestfactory.server.RequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gwtRequest</servlet-name>
<url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>welcomeGWT.html</welcome-file>
</welcome-file-list>
I don't understand how i can have 0 AccountService beans ?
i tried to add in the dispatcher-servlet
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
I got the same result
Any idea?
edit: if somebody have a full complete example, that could be useful.
I think using the ContextLoaderListener alone is not enough as you don't seem to have the DispatcherServlet in use (have you?).
The following lines work for me:
<filter>
<filter-name>springRequestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springRequestContextFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
I've seen this question in a couple of other places. You should try explicity defining the AccountServiceImpl as a bean in your applicationContext.xml (not the dispatch-servlet.xml) first and see if you still get the error, if you don't then you know it's that you're missing the component-scan in your application context xml which is what I think is the case.
hope this helps

Spring Extension REST Resource #RequestParam annotation not detected

trying to use the spring extension of Restlet ,
have configured as per the example http://wiki.restlet.org/docs_2.1/13-restlet/28-restlet/70-restlet/196-restlet.html
In addition to that trying to capture the request parameters using the #RequestParam annotation but end up getting the parameter value as null.
Resource looks like,
class MyResource extends ServerResource implements IResource {
#Get
#RequestMapping(value="/id")
public void get(#RequestParam(value="name") String name) {
...
}
}
HTTP Request http://localhost:8080/messages/id?name=XXX
Web.xml looks like
<servlet>
<servlet-name>test-servlet</servlet-name>
<servlet-class>org.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
</servlet>
<!-- Catch all requests -->
<servlet-mapping>
<servlet-name>test-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
In the end the name value is 'null'. I think the spring based annotations are not detected. I have no clue why this is happening?

Resources