GoogleWebToolkit example plugin

What is GoogleWebToolkit?

GoogleWebToolkit (GWT) is a framework, that allows to develop AJAX applications. It provides a compiler that compiles Java code into JavaScript code. You can write your application, under some restrictions, in Java. That enables you to share code between client and server side, and remote calls can easily done by an method call. You can build your web page using several AWT like widgets, which make your webpage look identically, regardless which browser you are using.

I recommend you familiarize with GWT, before trying to use it with Openfire.

Integrating GWT into Openfire

Openfire makes internally use of Jetty, with provides an servlet container. So it is possible to run an GWT servlet with openfire. However, it’s a bit tricky.

I have written an minimal plugin as example. It uses AJAX to retrieve the current date and time from the server. My implementation has probably bad performance (*). If you come up with an better solution, please let me know.

(*) It’s synchronized for thread safety, so it can only process one remote call at once.

Requirements

  • Obviously you will need a current version of GoogleWebToolkit.

    You will need to copy gwt-servlet.jar from your GWT installation into plugins lib directory. It’s not included, because it’s “big” and part of GWT, so you have it already.

  • I supply two scripts for easy compiling client and server side code. Both are written in usual Linux Bash-shell. Nothing complicated, you will be able to translate it easily to your systems shell language. In any case you should take a quick look at it, probably you want to change the path to your GWT installation.

  • You can not licence your plugin under GPLv2, because GWT is under Apache License 2.0. GPLv2 is not compatible with Apache License 2.0. You could licence your plugin for example under GNU General Public License Version 3.

Directory structure

gwt |- lib |   |- gwt-servlet.jar  <- NOT SUPPLIED, copy it from your GWT installation |- scripts              <- (ant script does ignore this directory) |   |- gwt-client       <- compile Java client code to JavaScript and place it in 'src/web/gwt' |   |- gwt-server       <- compile Plugin to JAR file (call 'ant plugins') |- src |   |- gwt/org/jivesoftware/openfire/plugin <- (ant script does ignore this directory) |   |   |- gwt |   |       |- Test.gwt.xml    <- client side module config |   |       |- public          <- content of this directory is copied to 'src/web/gwt' |   |       |   |- Test.html   <- JavaScript code from Test.java will be included here. |   |       |- client          <- directory for client side code |   |           |- Test.java   <- entry point for client |   |           |- DateServiceAsync.java   <- remote service: asynchronous Interface |   |- java/org/jivesoftware/openfire/plugin |   |   |- gwt |   |   |   |- server     <- directory for server side code |   |   |   |   |- DateServiceImpl.java  <- remote service: Servlet           (here is the tricky part...) |   |   |   |- client     <- directory for shared code |   |   |       |- DateService.java      <- remote service: Interface |   |   |- GWT.java       <- basic plugin |   |- web |       |- WEB-INF |       |   |- web-custom.xml <- servlet config |       |- gwt            <- compiled JavaScript code for clients is placed here |- plugin.xml         <- basic plugin config, integration into admin console

Implement File Download in GWT

Client side

  1. Add this line some where in HTML-body of your document:
  <iframe src="" id="__download" style="width:0;height:0;border:0"></iframe>
  1. Put this code where your want to start that download (e.g. ClickListener of an Button)
  String link = GWT.getModuleBaseURL() + "download.srv";
  DOM.setElementAttribute(RootPanel.get("__download").getElement(), "src", link);
  1. You can also send parameters to the servlet. Simply add them to the link, it’s a normal GET-request.
    Server side

  2. Create an usual Servlet which provides the file:

  package org.jivesoftware.openfire.plugin.gwt.server; import javax.servlet.ServletConfig;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import java.io.*;
  import java.util.*; public class DownloadServlet extends HttpServlet {
       protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
       {
            doGet(request, response);
       }      protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
       {
            String attr = request.getParameter("attr");  // process GET attributes           String name = "your_file.xml";
            response.setContentType("text/xml");
            response.setHeader("Content-Disposition", "attachment; filename="+name);           PrintWriter out = response.getWriter();           // write your file to "out"                out.flush();
       }
  }
  1. Link the servlet in your web-custom.xml:
  <servlet>
       <servlet-name>DownloadServlet</servlet-name>
       <servlet-class>org.jivesoftware.openfire.plugin.gwt.server.DownloadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
       <servlet-name>DownloadServlet</servlet-name>
          <url-pattern>/gwt/download.srv</url-pattern>
  </servlet-mapping>

Implement File Upload in GWT

Client side

  1. Add an Upload from in your interface:
  private Form form;
  private FileUpload upload; public void onModuleLoad() {
            // ...      form = new FormPanel();
       form.setAction("upload.srv");
       form.setEncoding(FormPanel.ENCODING_MULTIPART);
       form.setMethod(FormPanel.METHOD_POST);
       form.addFormHandler(fh_import);
       upload = new FileUpload();
       upload.setName("fileupload");
       form.add(upload);
       Button importButton = new Button("Import", cl_import);
       form.add(importButton);      basepanel.add(form);      // ... }
  1. Now specify handlers:
  private final ClickListener cl_import =  new ClickListener() {
       public void onClick(Widget sender) {
            form.submit();
       }
  }; private final FormHandler fh_import = new FormHandler() {
       public void onSubmit(FormSubmitEvent event) {
            String filename = upload.getFilename();
            if (filename == null || filename.length() == 0) {
              setMessage("No file specified.");
              event.setCancelled(true);
            }
            else {                                     setMessage("Upload in progress...");
            }
       }      public void onSubmitComplete(FormSubmitCompleteEvent event) {
            String result = event.getResults();
            try {
              int i = result.indexOf('$');
              int j = result.indexOf('$', i+1);
              String message = result.substring(i+1, j));
              setMessage(message);
            }
            catch (Exception e) {
              setMessage("Internal Error: Illegal result message.");
            }
       }
  };

Server side

  1. Get commons-fileupload.jar and commons-io.jar. Some old versions come with several official Openfire plugins. However, you can also download them here:
  1. Create an new Servlet:
  package org.jivesoftware.openfire.plugin.gwt.server; import javax.servlet.ServletConfig;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItemFactory;
  import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  import org.apache.commons.fileupload.servlet.ServletFileUpload;
  import org.apache.commons.fileupload.FileItem;
  import org.apache.commons.fileupload.FileUploadException; import java.io.*;
  import java.util.*; public class UploadServlet extends HttpServlet {
    private static final int MAX_FILE_SIZE = 256 * 1024;   protected void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException
    {
      if (!ServletFileUpload.isMultipartContent(request)) {
        writeResponse(response, "Internal Error: 'multipart/form-data' required.");
        return;
      }     // Create a factory for disk-based file items and file upload handler
      DiskFileItemFactory factory = new DiskFileItemFactory();
      factory.setSizeThreshold(MAX_FILE_SIZE+1);
      ServletFileUpload upload = new ServletFileUpload(factory);
      upload.setFileSizeMax(MAX_FILE_SIZE);
      upload.setSizeMax(MAX_FILE_SIZE);     String msg = "No file attached?";
      try {
        // Parse the request
        List items = upload.parseRequest(request);
        for (Object objItem : items) {
          FileItem item = (FileItem)objItem;
          if (item.isFormField()) {
            continue;
          }         String fileName = item.getName();
          if (fileName != null) {
            InputStream is = item.getInputStream();
            if (is != null) {             // ...handle the new file...             msg = "File uploaded successful."
              is.close();
            }
            else {
              msg = "Internal Error: Unable to open file stream for uploaded file: " + fileName;
              Log.error(msg);
              break;
            }
          }
          else {
            msg = "Internal Error: No filename specified for file upload.";
            Log.error(msg);
            break;
          }
        }
      }
      catch (FileUploadException e) {
        msg = "Internal Error: Unable to upload one or more profile files.";
        Log.error(msg, e);
      }        writeResponse(response, msg);
    }   protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException
    {
      writeResponse(response, "Internal Error: Only POST is supported.");
    }   private void writeResponse(HttpServletResponse response, String msg) throws IOException {
      response.setContentType("text/plain");
      PrintWriter out = response.getWriter();
      out.println("$" + msg + "$"); // add two "$" so client can easily parse the result.
      out.flush();
    }
  }
  1. Link the servlet in your web-custom.xml:
  <servlet>
       <servlet-name>UploadServlet</servlet-name>
       <servlet-class>org.jivesoftware.openfire.plugin.gwt.server.UploadServlet</servlet-class>
  </servlet>
  <servlet-mapping>
       <servlet-name>UploadServlet</servlet-name>
       <url-pattern>/gwt/upload.srv</url-pattern>
  </servlet-mapping>

See first comment on this document. You need to write the file content into that PrintWriter. So, just use a FileReader (or so) to open and read the file and write everything into the PrintWriter.

Is there an downloadable version of your servlet?

If you mean the GWT servlet itself, it’s attached to this document. If you mean that upload/download stuff, no directly. But I used all this techniques in my Raptor plugin.

Could you post the GWT client code as well.
Again…see my first and second comment!