Dominik
Dominik

Reputation: 4768

Java Webservice REST proper design best pracice

I have developed a webservice in Java that runs as a servlet on a tomcat, parameters for the application are given to the servlet via the get request (e.g. servlet?method=search&query=searchterm123), the servlet recognizes if the method and the query are defined and in the case of an error gives back a string that is manually wrapped in xml code that I hardcoded via this.writer.println(answer);. If the method is right a new class is instantiated which does the search and then gives back an object which XStream converts into XML for me which I then again send back to the client with println wrapped into my xml overhead that is again hard coded String answer ="<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n".

Obviously this works and also very efficiently but this is far from elegant. I have looked into Apache CXF very briefly and also into things like RESTlet or JBOSS RestEasy, the latter two I found to much for my demands. Every tutorial I find on CXF always includes Spring and Maven which then overwhelms me a little. Do you have suggestions how I should turn my dirty hack into a pretty application?

Upvotes: 2

Views: 709

Answers (2)

Dirk
Dirk

Reputation: 1903

I think using a JAX-RS framework (as CXF and Resteasy, but also Jersey) is the way to go in your case. Regarding serializing to XML, maybe have a look at JAXB (as included in Jersey too for instance). It should help automatically serialize any entity structure.

Regarding the complexity of such application: it should always depend on what infrastructure you use. If its just a simple Java EE server, its possibly best to use the implementation of that vendor (Jersey for Glassfish, Resteasy for JBoss). Otherwise, just use the build system you are familiar and comfortable with. You can easily replace the Maven dependencies with Ant and Ivy for example.

Upvotes: 3

Donal Fellows
Donal Fellows

Reputation: 137557

I can recommend CXF; I found it extremely easy to get going with its tutorial, especially once I bit the bullet and used Maven to manage dependencies (though really it's orthogonal to everything that CXF and Spring do).

But in order to make use of CXF I really recommend using Spring. You don't have to use all of Spring; the simple tutorial on the CXF site gives you enough to get going. This is especially true if you've already got the code that actually implements things already done, and separated out from the code to parse incoming URLS, render responses as XML, etc.; that's the part that CXF (and typically JAXB) will handle for you.

To help, here's a mega-simple example (with imports omitted for brevity). I know it looks complicated, but almost all the second half consists of stuff you write once and then don't really touch again; as you build out to tackle your real code, you can do a lot without paying must attention to the framework code at all. First, the interface definitions (including the XML type model):

public interface Foo {
    @Path("/") @GET @Produces("application/xml");
    FooDesc getDescription(@Context UriInfo ui);
    @Path("{id}")
    FooItem getFoo(@PathParam("id") String id);
}

@Path("/")
public interface FooItem {
    @GET @Produces("application/xml")
    FooItemContents getContents();
    @PUT @Consumes("application/xml")
    void setContents(FooItemContents desc);
    @DELETE
    Response delete();
}

// These classes are purely structural holders; no behavior.

@XmlRootElement @XmlType
public class FooDesc {
    @XmlElement
    public List<URI> foo;
}

@XmlRootElement @XmlType
public class FooItemContents {
    @XmlElement
    String bar;
}

Next, the implementation class:

public class FooImpl implements Foo {
    public FooDesc getDescription(UriInfo ui) {
        FooDesc desc = new FooDesc();
        desc.foo = new ArrayList<URI>();
        UriBuilder ub = ui.getAbsolutePathBuilder().path("{id}");
        for (String id : realGetIdList())  // Hook to your code here!
            desc.foo.add(ub.build(id));
        return desc;
    }
    public FooItem getFoo(String id) {
        final RealFoo r = realGetById(id); // Hook to your code here!
        return new FooItem() {
            public FooItemContents getContents() {
                FooItemContents contents = new FooItemContents();
                contents.bar = r.getBar(); // Hook to your code here!
                return contents;
            }
            public void setContents(FooItemContents desc) {
                r.setBar(desc.bar);        // Hook to your code here!
            }
            public Response delete() {
                r.close();                 // Hook to your code here!
                return Response.noContent().build(); // Return a simple HTTP 204
            }
        };
    }
}

Now, plumbing it together at the Spring level with a WEB-INF/beans.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

  <!-- Instantiate and connect the service framework -->
  <import resource="classpath:META-INF/cxf/cxf.xml" />
  <import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml" />
  <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
  <jaxrs:server id="customerService" address="/">
    <jaxrs:serviceBeans>
      <ref bean="fooBean" />
    </jaxrs:serviceBeans>
  </jaxrs:server>

  <!-- Instantiate your implementation class -->
  <bean id="fooBean" class="your.package.FooImpl" />
</beans>

Now, to hook it all up as a webapp, a web.xml:

<web-app>
  <!-- Magic to make Spring work and build the rest for us -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/beans.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Make CXF implement the servlet for us -->
  <servlet>
    <servlet-name>CXFServlet</servlet-name>
    <display-name>CXF Servlet</display-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!-- You *must* use this servlet mapping or Bad Things Happen. -->
  <servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

Now build a WAR including all the libs listed on the CXF site, putting the bits in the right places (you don't need Maven for this, but in the end it makes it easier) and deploy. That ought to work (i.e., you've now got enough to be dangerous!)

Upvotes: 3

Related Questions