Java RESTful Web Service - Annotate an Interface instead of a Class - java

I am trying to setup a simple web service (deploy on tomcat) which goes like this:
#Path("/api")
public interface Api {
#GET
#Path("categ")
public String getCateg();
}
and I have the following class implementing the interface:
public class RAPI implements API {
public String getCateg() { ... }
}
My web.xml looks as follows:
<servlet>
<servlet-name>API</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.api.resources</param-value> <!-- THIS IS CORRECT -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>API</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
But when I try to deploy on Tomcat I get the following (rather expected error):
com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
Which (not copied the whole trace here) tells me that although it found the API interface it cannot instantiate it.
How can I declare which of the implementing classes will actually act as the REST web service?

Having an interface annotated with JAX-RS allows you to create remote proxy clients. We do this with Apache CXF, but I haven't tried it with Jersey.
EG in my Spring config I can have;
<jaxrs:client id="myClient" inheritHeaders="true"
address="http://myhost/rs"
serviceClass="com.mycorp.restful.MyServiceInterface">
<jaxrs:headers>
<entry key="Accept" value="application/xml"/>
</jaxrs:headers>
</jaxrs:client>
I can now use this spring bean by just calling the methods. I don't have to create a Client and I don't have to care about the relative paths of the different RS services it defines.

As for using Interface for REST Service it is a good idea IMHO. But one thing do not annotate Interface itself leave it for implementation. This way you may have more flexibility. For instance,
public Interface Readable {
#GET
#Path("/read/{id}")
public String read(#PathParam("id") Integer id);
}
#Service
#Path("/book")
public class Book implements Readable, ... {
...
public String read(Integer id){...}
}
As for Jersey proxy check this:
https://jersey.java.net/project-info/2.0/jersey/project/jersey-proxy-client/dependencies.html

Related

REST WebServise - how to use service like a singleton?

My rest application contains a service and I need to make this service to act like a singleton to save a state of the service.
a service:
#Path("/script")
public class ScriptEngineProvider {
private AtomicInteger idCounter;
public ScriptEngineProvider() {
System.out.println("before AtomicInteger");
idCounter = new AtomicInteger();
}
#POST
public Response executeScript( String x ) {
idCounter.incrementAndGet();
System.out.println("counter " + idCounter);
...
a client besides all other code has:
WebTarget webTarget = client.target("http://localhost:8080/NashornEngine/rest").path("script");
web.xml
<url-pattern>/rest/*</url-pattern>
With the above configuration the application works but with every request the variable idCounter creates so idCounter is allways 1.
Now I use next class to make the ScriptEngineProvider to be a singleton:
#ApplicationPath("/services")
public class NashornApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
public NashornApplication() {
singletons.add(new ScriptEngineProvider());
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
The problem is that I get The requested resource is not available with request:
//path services was added
WebTarget webTarget = client.target("http://localhost:8080/NashornEngine/rest").path("services").path("script");
What is the problem with this config?
try this:
#Singleton // this is the important line
#Path("/script")
public class ScriptEngineProvider {
private AtomicInteger idCounter;
public ScriptEngineProvider() {
System.out.println("before AtomicInteger");
idCounter = new AtomicInteger();
}
#POST
public Response executeScript( String x ) {
idCounter.incrementAndGet();
System.out.println("counter " + idCounter);
...
#ApplicationPath effectively serves the same purpose as the url-mapping. But only one can be used. You use #ApplicationPath with an Application subclass when you don't want to use web.xml configuration. To go web.xml-less, you need to make sure of two things
You are deploying to a 3.x servlet container.
You have the dependency that supports the servlet pluggability. See more here
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
It's also possible to use an Application subclass without the #ApplicationPath, which will then use the url-mapping to map the Jersey servlet. For example
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.pkg.YourApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Which ever way you choose, either the url-mapping path will be used, or the #ApplicationPath value will be used, but not both. So in your case, if you do with no web.xml configuration, the path would not include the /rest in the url-mapping. And if you go with the above web.xml configuration, then you should get rid of the #ApplicationPath on your Application subclass, that url you would use would with /rest, with no /services.
The reason you keep getting one is because
The default behavior is to create a new resource class for every request
Your Application subclass is not being used, and your web.xml configuration is being used, where you are package scanning.
Review the information from this post, and make the changes accordingly.

ContainerRequestFilter method not being called

I've just started learning how to code REST web services, and I've been stuck with this for several days now. I'm coding an example application with header-based filtering, using Jersey 2 and deployed on Tomee-plus 1.7.2. No matter what I try, the ContainerRequestFilter's filter method is never called.
// TestRequestFilter.java
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
#Provider
#PreMatching
public class TestRequestFilter implements ContainerRequestFilter
{
private final static Logger log = Logger.getLogger(DemoRESTRequestFilter.class.getName());
#Override
public void filter(ContainerRequestContext requestCtx) throws IOException
{
System.out.println("FILTER-REQUEST");
}
}
My web.xml file is empty save for the required headers. The behavior right now is: the filter is recognised as #Provider and instantiated as normal, the test web service I have (just a GET returning an empty Response) can be called normally, but the filter method is never called.
Things I've tried and their effects:
Declare the filter in a class extending Application: Error on deployment.
Register the filter in a class extending ResourceConfig: Filter is instantiated twice, but filter method is still not called.
Use the classes from the com.sun.jersey.spi.container package: No effect.
Add an authentication annotation (#RolesAllowed, #PermitAll,...) to the WS method: No effect.
Add disabled=true to cfx-rs.properties in server configuration: Deployed service cannot be found at usual URL.
Add this to web.xml: No effect.
<servlet>
<servlet-name>CongressAppWS</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>org.glassfish.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.s4w.congressapp.auth.DemoRESTRequestFilter;com.s4w.congressapp.auth.DemoRESTResponseFilter</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.s4w.congressapp.auth;com.s4w.congressapp.resources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Using com.sun.jersey.spi.container prefix instead of org.glassfish.jersey.spi.container in previous code: No effect.
I'm honestly running out of options here. Every time I try something new, either there is no effect or everything stops working. Any help?
I found an alternative to ContainerRequestFilter that actually works! It's javax.servlet.Filter. Extending from this class, all I had to do was annotate it with the following code and the filtering mechanism works like a charm.
#WebFilter(filterName = "AuthenticationFilter", urlPatterns = { "/*" })
The reason for this is that ContainerRequestFilter is part JAX-RS 2.0, but TomEE 1.7.X comes with JAX-RS 1.1. You'll either have to upgrade TomEE to 7.0.0+ or use a different Server (e.g. Glassfish).

Spring WS without xsd

I'trying to create simple WS project in Spring and Spring WS without any XSD. Deploy on jetty.
Is possible to populate WS endpoint and generate WSDL only from java classes (no static XSD or WSDL - I went throught many tutorials but all requiered).
For any help, hint or link highly appreciated.
I have something like this:
1) Request
#XmlRootElement
public class MessageWSRequest {
#XmlElement
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2) Endpoint
#Endpoint
public class MessageWS {
#PayloadRoot(namespace = "http://message.com/ws/message" ,localPart="MessageWSRequest")
public String handleMathServiceRequest(#RequestPayload MessageWSRequest messageWSRequest) {
return "ok";
}
}
3) springContext.xml
<sws:annotation-driven/>
<context:component-scan base-package="com.ws.message"/>
4) web.xml
<servlet>
<servlet-name>webservices</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>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>*.wsdl</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>webservices</servlet-name>
<url-pattern>/endpoints/*</url-pattern>
</servlet-mapping>
Now I would expect URL like this
localhost:8080/messageTest/endpoints/MessageWS.wsdl
with generated WSDL.
Did I miss some configuration or so?
Thanks all
Ok, next day a clear mind revelead me this fact:
Spring WS offers "only" contract-first, starting from an XSD Schema
I'll use CXF instead:
Apache CXF offers both contract-last (starting with Java) and Contract-first (starting with the WSDL) approaches.
As you have noted, Spring WS is designed for contract first services. However I think that you can still achieve what you want to do if you generate the XSD during the build process from your annotated classes. Here is one way to do that:
Generating XSD schemas from JAXB types in Maven?

Confusion between HttpServlet class and using it with Jersey

I am building servlets which implement a RESTful API. I understand the Jersey is a framework for deciphering and using given URL's. How do I use it in conjunction with the HttpServlet class.
I don't understand how the two work with each other. I guess this is a very broadstrokes question but I have done a fair share of reading but am still stuck on this seemingly trivial concept. I have attempted to deploy apps with classes that extend the HttpServletclass AND use Jersey annotations.
#Path("/api")
public class API extends HttpServlet{
#GET
#Path("/{name}")
#Produces("text/hmtl")
public String doGetSayHello(#PathParam("name") String name){
return "Hello" + name;
}
#GET
#Path("/articles")
#Produces("text/json")
public String doGetArticles(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
JSONObject obj = new JSONObject();
obj.put("interns", interns);
obj.put("company", "Stack Overflow");
return obj.toString();
}
}
Any help or informative materials would be greatly appreciated!
Actually you are confused because you don't understand how jersey works. Jersey framework basically uses com.sun.jersey.spi.container.servlet.ServletContainer servlet to intercept all the incoming requests. As we configure in our projects web.xml, that all the incoming rest request should be handled by that servlet. There is an init-param that is configured with the jersey servlet to find your REST service classes. REST service classes are not Servlet and they need NOT to extend the HttpServlet as you did in your code. These REST service classes are simple POJOs annotated to tell the jersey framework about different properties such as path, consumes, produces etc. When you return from your service method, jersey takes care of marshalling those objects in the defined 'PRODUCES' responseType and write it on the client stream. Here is a sample of jersey config in web.xml
<servlet>
<servlet-name>REST</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.rest.services;
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>REST</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Jersey uses a servlet to route URLs to the appropriate service. Your service itself does not need to extend a servlet.
At a high level, Jersey's ServletContainer class accepts the requests, and then based on your Jersey configuration, your web service will be invoked. You configure what url patterns are processed by Jersey. Check out section 5.3 http://www.vogella.com/articles/REST/.

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