Multiple JAX-RS applications in the same WAR - java

I have
#ApplicationPath("/resourcesP")
public class RestfulPrediction extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(PredictionsRS.class);
return set;
}
}
And
#ApplicationPath("/resourcesA")
public class RestfulAdage extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(Adages.class);
return set;
}
}
Two different ApplicationPath and the class are as follows.
#Path("/")
public class service.Adages {}
#Path("/")
public class webservices.PredictionsRS {}
Both of them are declared in different ApplicationPath. I'm using Jersey and the config in web.xml looks like
<servlet>
<servlet-name>jersey</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>
service
webservices
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
And I'm getting
SEVERE: Conflicting URI templates. The URI template / for root
resource class service.Adages and the URI template / transform to the
same regular expression (/.*)?
Why if I have two different ApplicationPath this exception comes at startup ?
If I take out a package in param-value this works, also if I change one of the #Path annotations this works, so it is a problem with my configuration ?
I'm using Jersey 1.10. Thanks all.

You did not define your JAX-RS applications in your web.xml. Try the following:
<servlet>
<servlet-name>full.name.RestfulAdage</servlet-name>
</servlet>
<servlet>
<servlet-name>full.name.RestfulPrediction</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>full.name.RestfulPrediction</servlet-name>
<url-pattern>/resourcesP/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>full.name.RestfulPrediction</servlet-name>
<url-pattern>/resourcesA/*</url-pattern>
</servlet-mapping>
and remove the #ApplicationPAth annotations from code.
I checked the above code with Jersey 2.7, servlet container 3.0 and it works. If still having that bug, try upgrading to Jersey 1.17 (which should not change any behavior from Jersey 1.10, and fix bugs instead) and eventually using also a servlet container 3.0.
UPDATE
After checking the possibilities the configuration below work with Jersey 1.17
<servlet>
<servlet-name>jersey</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>
com.koitoer.webservices
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
It seems there is bug in the spec in older version of Jersey that kind of circle back the references and mark as duplicate endpoints. Using the configuration above both endpoints load without any problem.
8/04/2014 09:13:40 PM
com.sun.jersey.server.impl.container.servlet.JerseyServletContainerInitializer
addServletWithApplication INFO: Registering the Jersey servlet
application, named com.koitoer.webservices.chapter2.service2.RestfulPrediction, at the
servlet mapping, /resourcesP/*, with the Application class of the same
name
8/04/2014 09:13:40 PM com.sun.jersey.server.impl.container.servlet.JerseyServletContainerInitializer
addServletWithApplication INFO: Registering the Jersey servlet
application, named com.koitoer.webservices.chapter2.RestfulAdage, at
the servlet mapping, /resourcesA/*, with the Application class of the
same name

You should have a single subclass of javax.ws.rs.core.Application in your webapp, and then use different #Path annotation values on your service.Adages and webservices.PredictionsRS resource types.
AFAIK, in JEE6 containers, you are not allowed to have 2 such subclasses...

Related

Jersey ContainerResponseFilter not working

I'm using Jersey and I implemented a ContainerRequestFilter.
Now I also want to add a ContainerResponseFilter to add a header to each request but nothing happens when the webservice is accessed.
This is how the filter looks like:
public class ResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
headers.add("Cache-Control", "whatever");
}
}
My jersey dependencies:
compile 'org.glassfish.jersey.core:jersey-client:2.18'
compile 'org.glassfish.jersey.containers:jersey-container-servlet-core:2.18'
compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.18'
I register the providers in an xml like this:
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice</param-value>
</init-param>
//This is the request filter, which is working fine
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>model.filter.AuthenticationFilter</param-value>
</init-param>
//Response filter, does not work
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>model.filter.ResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
This didn't work, so I tried to register the provider with the annotation #Provider and in the webservice like this:
register(TokenModifier.class);
But none of these worked. I read several other posts but I couldn't find an answer to it. Does anyone have a thought on this?
This init-param
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice</param-value>
</init-param>
is used by Jersey to scan package(s) listed for #Path and #Provider annotated classes, and register them. The package(s) listed will be recursively scanned. So for example, with your current configuration, all the following packages will get scanned
controller.webservice
controller.webservice.x
controller.webservice.x.y
controller.webservice.x.y.z
The value of the param can also be multiple values separated by a comma or semi-colon. So if your filter is in a different package base, you can add that package to the list of packages to scan
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>controller.webservice, com.my.filters</param-value>
</init-param>
Personally though, if I'm using web.xml, I will just use one base package, and have all other packages extend from that package. Something like
com.company.app
com.company.app.domain
com.company.app.filters
com.company.app.resources
Then you can just put the com.company.app as the init-param value, and all other sub packages will get scanned also.

Spring 4.1.4 and Jackson 2.5, annotation driven setup not converting REST responses to JSON

I coming back to Spring after spending 5 years doing other things. I have an initial projects which is designed to provide a HTTP REST service that returns JSON.
My problem is that I cannot get the service to convert the response to JSON. Instead I get errors like this:
javax.servlet.ServletException: Circular view path [hello]: would dispatch back to the current handler URL [/hello] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:205) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:145) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
...
My web.xml looks like this:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>au.com.abc.service</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>fxServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>au.com.abc.controller</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And my Controller class looks like this:
#RestController
public class FXRESTController {
#RequestMapping(value = "/hello")
public Map<String,Object> rootContextHandler() {
Map<String,Object> data = new HashMap<>();
data.put("X", "abc");
return data;
}
}
It really cannot get any simpler. I've been passing the request header Accept='application/json', but it's still not working. I have had this type of thing working in the past, but I don't have that code anymore. I can also see this in the logs:
... Invoking request handler method: public java.util.Map au.com.abc.controller.FXRESTController.rootContextHandler()
... Service responding
... Invoking afterPropertiesSet() on bean with name 'hello'
... Rendering view [org.springframework.web.servlet.view.JstlView: name 'hello'; URL [hello]] in DispatcherServlet with name 'fxServlet'
... Added model object 'X' of type [java.lang.String] to request in view with name 'hello'
... Error rendering view [org.springframework.web.servlet.view.JstlView: name 'hello'; URL [hello]] in DispatcherServlet with name 'fxServlet'
Which suggests to me that it's trying to render a JSTL view. Why - I don't know considering I've asked for JSON.
Any ideas what I've done wrong?
I've read a ton of blogs and so far I cannot see any differences between what they have done and what I've done.
Oh and here are my gradle resolved dependencies:
You only have a controller, that doesn't do anything for enabling JSON.
You have to have a #Configuration annotated class which is also annotated with #EnableWebMvc to have automatic JSON conversion enabled. See also this section of the reference guide.
#Configuration
#EnableWebMvc
public class WebConfiguration {}
You are running with the DispatcherServlet defaults which are very basic.
Use #ResponseBody.
ALso donot direct all request to spring
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ALways better to add a layer like this:
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/webapp/*</url-pattern>
</servlet-mapping>
FOr a given project only following request will be directed to spring
http://localhost:8080/Proj/app/hello
Other request can be handled different..thus you can handle request in a different way and not dependent on spring..like
http://localhost:8080/Proj/servlethandler
The above request will not go to spring and can be intercepted by a servlet etc..

should I make two different servlet entry for rest and normal html in web.xml

I am writing spring mvc application.
In my application I have web pages as well as rest web services to handle ajax call.
I have done below entry in web.xml
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring_myapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Should I map my rest url with same servlet like
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Or should I make new servlet entry for rest.
I have done required entries in pom.xml for "org.codehaus.jackson" and also I have made required entries in my spring_myapp-servlet.xml.
For html page I am using below code in my controller
#RequestMapping(value = "/htmlUrl")
public ModelAndView ModifyValiodation(HttpServletRequest request) {
// my code
}
For rest service I am using
#RequestMapping(value = "/restUrl")
public #ResponseBody Map<String, String> restUrl(HttpServletRequest request) {
// my code
}
If I am using only one servlet for two url mapping, then total 4 url will be made.
myapp/htmlUrl.html
myapp/restUrl.html
myapp/rest/htmlUrl
myapp/rest/restUrl
If I am using two different servlet with individual dispacherServlet then will i have to make entry of every component and service of spring in both the servlet.xml?
Please point out the solution for exposing rest web service.
Thanks!
use
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
If you use two DispatcherServlet entries , it will load two ApplicationContext Objects in your application. Since you are using spring mvc to handle all the requests to your app, you should be fine with this configuration. Any request url that ends with .html or any urls that contains /rest/ will be handled by spring.
It is up to you to design the server side of the infrastructure.
Neither the RESTful specifications have any instructions for doing this nor the Servlet specifications enforce anything on this.
On the Applications design I think it is better idea to keep two different servlets to handle different URLs because over time the classes will become complex and long. These to may be used as front controllers and may have common logic class in the backend.

how to return web page within spring webservice context?

Suppose we use tomcat as container and we have implemented a web service server using spring web-service framework.
Now we want to provide a web page for users to access some other functions of our product in a browser instead of the client software.
The logic processing web page requests is similar to that processing web service ones. So we want to write web-page-generating codes with in the same application context as the web service.
This might be a weird need, but it is a need.
I tried to write a servlet and refer to a Service loaded by Spring, but it feels dirty and sometimes does not work:
class ExampleServlet{
private FooService service = FooService.ins;
public doGet(HttpServletRequest request, HttpServletResponse response){
service.doSomething();
// ...
return someResponse;
}
}
#Service
class FooService{
public static FooService ins = null;
public FooService(){
ins = this;
}
#Transactional(rollbackFor = Exception.class)
public void doSomething(){
// ...
}
}
in web.xml :
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>
org.springframework.ws.transport.http.MessageDispatcherServlet
</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<display-name>ExampleServlet</display-name>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>com.somepackage.ExampleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ExampleServlet</servlet-name>
<url-pattern>/do_something</url-pattern>
</servlet-mapping>
I should wrap them up in spring mvc
ref:
http://docs.spring.io/spring-ws/docs/2.2.0.RELEASE/reference/htmlsingle/#d4e950
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-servlet

How to initialize Java EE 5 JAX-WS 2.0 Web Service with Parameters

Application configuration:
Web application using java first method of creating JAX-WS 2.0 Web Services with annotations.
WebLogic 10.3
My Requirements
The requirements I have are to deploy a single web service implementation class, but change logic based on the URL from which the service was accessed.
Question:
I'm assuming a good way to do this is to deploy different mappings in web.xml and initialize them with different parameters. Is there a better way?
What is the best way to switch logic off the URL from which the web service was accessed? Should I try to configure two servlet mappings in web.xml with initialization parameters (tried, but couldn't get it to work), or should I parse the URL in the service impl? Any other alternatives?
What I've Tried (but didn't work)
I have tried adding the <init-param> in the <servlet> element in web.xml. However, can't get to the ServletConfig object inside the web service to retrieve the param. The web service does not have all the functionality of a standard Servlet (even if I implement Servlet or ServletContextListener). I only have access to the WebServiceContext (it seems) and from there I can only get <context-param> elements--but I would need <init-param> elements instead.
In web.xml, I enter two <servlet> elements using the same Java class, but which map to two different URLs as follows. Notice how the "source" param is different in each Servlet mapping.
<servlet>
<servlet-name>Foo</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/Foo</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Bar</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Bar</servlet-name>
<url-pattern>/Bar</url-pattern>
</servlet-mapping>
You very well may have, but did you try using MessageContext at runtime to determine what the source is?
#WebService
public class CalculatorService implements Calculator
{
#Resource
private WebServiceContext context;
#WebMethod
public void getCounter()
{
MessageContext mc = wsContext.getMessageContext();
// you can grab the HttpSession
HttpSession session = (HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
// ...or maybe the path info is enough
String path = mc.get(MessageContext.PATH_INFO);
// the query itself should almost definitely be enough
String query = (String) mc.get(MessageContext.QUERY_STRING);
}
}
I got the idea from http://sirinsevinc.wordpress.com/category/jaxws/. Haven't tried it, though.

Categories