Home > Web > OSGi, Jetty, CometD, Bayeux and Dojo

OSGi, Jetty, CometD, Bayeux and Dojo

December 15th, 2011 Ed Leave a comment Go to comments

How many keywords can I stuff into that title!! I succeeded to get Jetty, CometD and Dojo working together, without OSGi in the last blog post. However, moving that into an OSGi environment was sadly not as easy as it should have been, but I got there! This post should help you get things up and running – there are bits I understand, and bits I don’t!! It is as cut down as I could make it. Here goes:

In eclipse, make yourself a new Plugin Project, mine’s called com.jellard.comet, choosing the Equinox OSGi framework. Edit MANIFEST.MF to look like below:

Add this to the MANIFEST.MF as well:
Web-ContextPath: /comettest

Put these files in your project, and add them to the build path: cometd-java-server-2.3.1.jar, bayeux-api-2.3.1.jar, cometd-java-common-2.3.1.jar

Copy this file into a new class, com.jellard.comet.BayeuxInitialiser:

package com.jellard.comet;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerChannel;
import org.cometd.bayeux.server.ServerMessage;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.DefaultSecurityPolicy;

public class BayeuxInitialiser extends GenericServlet
{
	private static final long serialVersionUID = 4696913071422149884L;

	public void init() throws ServletException
    {
        BayeuxServer bayeux = (BayeuxServer)getServletContext().getAttribute(BayeuxServer.ATTRIBUTE);
        bayeux.setSecurityPolicy(new DefaultSecurityPolicy(){

			@Override
			public boolean canCreate(BayeuxServer server,
					ServerSession session, String channelId,
					ServerMessage message) {
				return true;
			}

			@Override
			public boolean canHandshake(BayeuxServer server,
					ServerSession session, ServerMessage message) {
				return true;
			}

			@Override
			public boolean canPublish(BayeuxServer server,
					ServerSession session, ServerChannel channel,
					ServerMessage message) {
				return true;
			}

			@Override
			public boolean canSubscribe(BayeuxServer server,
					ServerSession session, ServerChannel channel,
					ServerMessage message) {
				return true;
			}

        });
        new HelloService(bayeux);
    }

    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
    {
        throw new ServletException();
    }
}

Make a new class, com.jellard.comet.HelloService, and paste this in:

package com.jellard.comet;

import java.util.HashMap;
import java.util.Map;

import org.cometd.bayeux.Message;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.AbstractService;

public class HelloService extends AbstractService
{
    public HelloService(BayeuxServer bayeux)
    {
        super(bayeux, "hello");
        addService("/service/hello", "processHello");
    }

    public void processHello(final ServerSession remote, Message message)
    {
        Map input = message.getDataAsMap();
        String name = (String)input.get("name");

        Map output = new HashMap();
        output.put("greeting", "Hello, " + name);
        remote.deliver(getServerSession(), "/hello", output, null);

        Thread t = new Thread(){
        	int count = 0;
			@Override
			public void run() {
				while (true) {
					remote.deliver(getServerSession(), "/reply", count + "", null);
					System.out.println(count);
					count++;
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}

        };
        t.start();
    }
}

Create folders ~/jetty/etc/ and place this file in, as jetty.xml:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<!-- =============================================================== -->
<!-- Configure the Jetty Server                                      -->
<!--                                                                 -->
<!-- Documentation of this file format can be found at:              -->
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml                -->
<!--                                                                 -->
<!-- =============================================================== -->

<Configure id="Server" class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- Set connectors                                              -->
    <!-- =========================================================== -->

    <Call name="addConnector">
      <Arg>
          <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
            <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
          </New>
      </Arg>
    </Call>

    <!-- =========================================================== -->
    <!-- Set handler Collection Structure                            -->
    <!-- =========================================================== -->

    <Set name="handler">
      <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <Set name="handlers">
         <Array type="org.eclipse.jetty.server.Handler">
           <Item>
				<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">

			</New>
           </Item>
           <Item>
             <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
           </Item>
           <Item>
             <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
           </Item>
         </Array>
        </Set>
      </New>
    </Set>
    <!-- =========================================================== -->
    <!-- Configure the deployment manager                            -->
    <!--                                                             -->
    <!-- Sets up 2 monitored dir app providers that are configured   -->
    <!-- to behave in a similaraly to the legacy ContextDeployer     -->
    <!-- and WebAppDeployer from previous versions of Jetty.         -->
    <!-- =========================================================== -->
    <Call name="addBean">
      <Arg>
        <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
          <Set name="contexts">
            <Ref id="Contexts" />
          </Set>
          <Call name="setContextAttribute">
            <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
            <Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
          </Call>
          <!-- Providers of OSGi Apps -->
          <Call name="addAppProvider">
            <Arg>
              <New class="org.eclipse.jetty.osgi.boot.OSGiAppProvider">
              <!--
                <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>
              -->
                <Set name="scanInterval">5</Set>
                <Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set>
                <!-- comma separated list of bundle symbolic names that
                    contain custom tag libraries (*.tld files)
                    if those bundles don't exist or can't be loaded no errors or warning will be issued!
                    this default value is to plug the tld files of the reference implementation of JSF -->
                <Set name="tldBundles"><Property name="org.eclipse.jetty.osgi.tldsbundles"
                     default="javax.faces.jsf-impl" /></Set>
              </New>
            </Arg>
          </Call>

        </New>
      </Arg>
    </Call>

    <!-- =========================================================== -->
    <!-- extra options                                               -->
    <!-- =========================================================== -->
    <Set name="stopAtShutdown">true</Set>
    <Set name="sendServerVersion">true</Set>
    <Set name="sendDateHeader">true</Set>
    <Set name="gracefulShutdown">1000</Set>

</Configure>

Create a XML file, WEB-INF/web.xml, in your project:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
	version="2.5">
	<servlet>
		<servlet-name>cometd</servlet-name>
		<servlet-class>org.cometd.server.CometdServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>cometd</servlet-name>
		<url-pattern>/cometd/*</url-pattern>
	</servlet-mapping>
	<servlet>
		<servlet-name>initializer</servlet-name>
		<servlet-class>com.jellard.comet.BayeuxInitialiser</servlet-class>
		<load-on-startup>2</load-on-startup>
	</servlet>
	<filter>
		<filter-name>cross-origin</filter-name>
		<filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>cross-origin</filter-name>
		<url-pattern>/cometd/*</url-pattern>
	</filter-mapping>
</web-app>

Create a html folder in the root of your project. In there, create a js/dojo folder, and extract a build of dojo into there (so you will have a html/dojo/dojox folder, for example).

Also in the html folder, make a file, comet.html, and paste in this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <script type="text/javascript" src="js/dojo/dojo/dojo.js"></script>
    <script type="text/javascript">
        dojo.require("dojox.cometd");
        dojo.addOnLoad(function() {
            // Disconnect when the page unloads
            dojo.addOnUnload(function() {
               dojox.cometd.disconnect(true);
            });

            var cometURL = "/comettest/cometd";
            dojox.cometd.init(cometURL);
        });
        function subscribe() {
        	dojox.cometd.subscribe("/*", console, "log");
        }
        function publish() {
        	dojox.cometd.publish('/service/hello', { name: 'World' })
        }
    </script>
</head>
<body>
    <input type="button" onclick="subscribe()" value="1. Subscribe" />
    <input type="button" onclick="publish();" value="2. Publish" />
</body>
</html>

Right-click your project, Run, Run As… and make a new configuration under OSGi Framework. Select the following bundles, and anything else they require (I ended up with 55 selected!)

In the arguments tab, you need to add this to your VM args:
-Djetty.home=/Users/ed/jetty (if your home dir is /Users/ed)

Run the OSGi framework, and point your browser to http://localhost:8080/comettest/html/comet.html

Open up developer tools/firefox, and you should see a pending request. Click the subscribe and publish button, and as if by magic, every 5 seconds, you should see an object appear in your console.

Categories: Web Tags: , , , ,