Fork me on GitHub

6/27/2013

HTTP POST and PUT in REST way

HTTP 的 Get/Post/Delete/Put 對應到 Resource 的 CRUD (Create, Retrieve, Update and Delete),其中 POST 和 PUT 最常讓人摸不著頭緒,不知道什麼時候該用什麼。

今天 survey 了一下網路的資料,大概可以歸納出如下兩張圖,未來在遇到相關的設計時可以參照琢磨琢磨。

http-post

http-put

References

  1. SO: PUT vs POST in REST
  2. PUT or POST: The REST of the Story

6/01/2013

Deploy Jersey in the Openfire plugin

Let's say, the original method to access the openfire service by http is by HttpServlet (ex. presence plugin). So, how to replace it with Jersey for convenient development? In this post, I will detail the steps and the problems I met during achieving this.

The main reference is here. Assuming that you have successfully run a self-made plugin.


Step1. Modify the web-custom.xml

The web-custom.xml tells the container where to search for resource class. From that reference. There is some tricks in the web-custom.xml processing in openfire. So we need a wrapper, and finding the true resource class package there. As below. The class in the <servlet-class> tag is your wrapper class, the path in <url-pattern> is the base url you want.

<!-- Servlets -->
<servlet>
    <servlet-name>BrownyServlet</servlet-name>
    <servlet-class>com.brownylin.openfire.plugin.browny.BrownyServletWrapper</servlet-class>
</servlet>

<!-- Servlet mappings -->
<servlet-mapping>
    <servlet-name>BrownyServlet</servlet-name>
    <url-pattern>/test</url-pattern>
</servlet-mapping>

Step2. Tell Wrapper where to find resource class

The trick in the wrapper class is to find resouce class by PackagesResourceConfig. Below sample is referenced from that link.

The resouce class should be under the same package (or inner) as the wrapper class.

AuthCheckFilter.addExclude(SERVLET_URL); is used for avoiding the need of login to access the api.

    
public class BrownyServletWrapper extends ServletContainer {

    private static final long serialVersionUID = 1L;
    private static final String SERVLET_URL = "browny/test/*";
    private static final String SCAN_PACKAGE_KEY = "com.sun.jersey.config.property.packages";
    private static final String SCAN_PACKAGE_DEFAULT = BrownyServletWrapper.class
        .getPackage().getName();

    private static final String RESOURCE_CONFIG_CLASS_KEY = "com.sun.jersey.config.property.resourceConfigClass";
    private static final String RESOURCE_CONFIG_CLASS = "com.sun.jersey.api.core.PackagesResourceConfig";

    private static Map<String, Object> config;
    private static PackagesResourceConfig prc;

    static {
        config = new HashMap<String, Object>();
        config.put(RESOURCE_CONFIG_CLASS_KEY, RESOURCE_CONFIG_CLASS);
        config.put(SCAN_PACKAGE_KEY, SCAN_PACKAGE_DEFAULT);
        prc = new PackagesResourceConfig(SCAN_PACKAGE_DEFAULT);
        prc.setPropertiesAndFeatures(config);
        prc.getClasses().add(BrownyResources.class);
    }

    public BrownyServletWrapper() {
        super(prc);
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);

        // Exclude this servlet from requering the user to login
        AuthCheckFilter.addExclude(SERVLET_URL);
    }

    @Override
    public void destroy() {
        super.destroy();
        // Release the excluded URL
        AuthCheckFilter.removeExclude(SERVLET_URL);
    }
}

Step3. The Jersey dependent jar library

They are asm-3.3.1.jar, jersey-bundle-1.10-b01.jar, jersey-servlet-1.17.1.jar, jsr311-api-1.1.1.jar. If wrong dependency, the ClassNotFoundException, ClassNotDefException will happen (you could find the error from openfire_src/target/openfire/logs/error.log)


Step4. The Resource class

The trick here is the @Path() should starts from plugin name, later servlet url-pattern and at the end - path of the your resouce. So that is @Path("browny/test/hello")

    
package com.brownylin.openfire.plugin.browny;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("browny/test/hello")
public class BrownyResources {

    @GET
    @Path("/")
    public Response getMsg() {

        String output = "Jersey say Hello";
        return Response.status(200).entity(output).build();

    }
}

Step5. Don't want result shown in openfire admin console

As this thread said. The default configuration is to capture all text/html content from the server and force it into the admin page.

You could add the url pattern you want to exclude in openfire_src/src/web/WEB-INF/decorators.xml as below

<decorators defaultdir="/decorators">
    <decorator name="setup" page="setup.jsp">
        <pattern>/setup/*.jsp</pattern>
    </decorator>
    <decorator name="main" page="main.jsp">
          <pattern>/*.jsp</pattern>
          <pattern>/plugins</pattern>
    </decorator>
    <decorator name="none"/>
    <excludes>
        <pattern>/setup/setup-completed.jsp*</pattern>
        <pattern>/setup/setup-ldap-server_test.jsp*</pattern>
        <pattern>/setup/setup-ldap-user_test.jsp*</pattern>
        <pattern>/setup/setup-ldap-group_test.jsp*</pattern>
        <pattern>/setup/setup-clearspace-integration_test.jsp*</pattern>
        <pattern>/setup/setup-admin-settings_test.jsp*</pattern>
        <pattern>/login.jsp*</pattern>
        <pattern>/plugin-icon.jsp*</pattern>
        <pattern>/js/jscalendar/i18n.jsp*</pattern>
        <pattern>/plugins/browny/test/*</pattern>
    </excludes>
</decorators>

Step6. Test the resource url

Last, test the url localhost:9090/plugins/browny/test/hello. It's done. Hope this will be useful to someone :)

-- EOF --