OSGi at REST

Introduction

I’ve been working on a project to make building RESTful OSGi web applications as simple as creating a basic OSGi service, and I think I have a solution that comes pretty close to this goal. The project is currently called restlet-integration-with-equinox as hosted on EclipseLabs. I am in the process of moving the project to GitHub, and it will be renamed to Restlet Extension for OSGi Environments.  This framework integrates the Restlet RESTful web framework with OSGi allowing you to dynamically register resources, filters, routers, and applications as part of a RESTful web application.

The project originally started out supporting the registration of applications and resources as OSGi services or with an Equinox extension point.  This all worked fine until I started adding the ability to register routers and filters.  The code became complicated and ugly, so I branched the repository and started over.  As the second iteration progressed, it too became complicated, and it occurred to me that the core of the problem (delayed dynamic binding) was identical to the problem that OSGi declarative services has to solve.  That’s when I had that AH-HA moment where the problem becomes clear, and you get to delete all of that ugly, complicated, code.  The solution is to use OSGi declarative services and its dynamic binding capabilities to stitch together the resources, filters, routers, and applications.  And, since you are using OSGi services, you can add and remove resources at runtime and it all just works.

Framework

The Restlet Extension for OSGi Enironments framework is organized as a set of service components and interfaces for you to register as declarative services to form your RESTful web application.  Using these service components, it’s possible to implement a simple application with a router and some resources, or a very complex graph of filters, routers, and resources as demonstrated below.

Application

Here is the API specification for the service interfaces:

Service Interfaces

Here is the API specification for the service components (I have omitted the service interfaces that each provider implements):

Service Components

FilterProvider and ResourceProvider are abstract and must be extended as described below.  RouterProvider and ApplicationProvider can be used without extending them.  The remainder of this post will walk you through examples of creating RESTful web applications using this technology.

Setup

Before you begin development of your application, the target platform needs to be crated with the following frameworks:

If you would like to minimize your target platform, the following bundles are the minimum required:

  • javax.servlet
  • org.eclilpse.equinox.common
  • org.eclipse.equinox.ds
  • org.eclispe.equinox.http.jetty
  • org.eclipse.equinox.http.servlet
  • org.eclipse.equinox.util
  • org.eclipse.osgi
  • org.eclipse.osgi.services
  • org.mortbay.jetty.server
  • org.mortbay.jetty.util
  • org.restlet
  • org.restlet.ext.servlet
  • org.eclipselabs.restlet
  • org.eclipselabs.restlet.servlet

If you are not familiar with setting up your target platform, here are the detailed steps:

  1. Download Restlet JEE 2.0.8 and Restlet Extensions for OSGi Environments 0.3.0.  You do not need to download Equinox – we’ll get that from a p2 repository.
  2. Uncompress the downloads into a targets directory.
  3. Launch Eclipse (I’m using Indigo) and select Preferences -> Plug-in Development -> Target Platform.
  4. Click Add…
  5. Choose Nothing: Start with an empty target definition.
  6. Click Next
  7. Name the target platform.
  8. Click Add…
  9. Choose Directory and click Next.
  10. Add <target directory>/restlet-jee-2.0.8/lib
  11. Click Finish.
  12. Click Add…
  13. Choose Directory and click Next.
  14. Add <target directory>/restlet-ext-osgi
  15. Click Finish.
  16. Click Add…
  17. Choose Software Site.
  18. Work with: http://download.eclipse.org/releases/indigo.
  19. Expand EclipseRT Target Platform Components.
  20. Check Equinox Target Components.
  21. Click Finish.
  22. Click Finish.
  23. Check the target definition you just created to make it Active.
  24. Click OK.

Edit Target Definition 1

Let’s test the target definition.  Create an OSGi launch configuration with the following bundles:

  • javax.servlet
  • org.eclipse.equinox.http.jetty
  • org.eclipse.equinox.http.servlet
  • org.eclipse.osgi
  • org.eclipse.osgi.services
  • org.mortbay.jetty.server
  • org.mortbay.jetty.util

Set Default Auto-Start: true (not ideal, but it gets you up and running quickly) and add the following VM argument: -Dorg.osgi.service.http.port=8080.

Run Configurations 3

Run the application and point your web browser to http://localhost:8080/.  You should get a 404 error.  This lets you know that jetty is responding to your request, but there are currently no resources to handle the request.  For more information on setting up OSGi as a web server, see my blog post: OSGi as a Web Application Server.

Hello RESTful World

For this tutorial we are going to create a single resource at the URI http://localhost:8080/hello with a plain text representation.  We need to create and wire together three objects: Application, Router, and ServerResource.  Start by creating a new Plug-in Project called org.eclipselabs.restlet.examples.app.  You do not need a bundle activator.

Open the MANIFEST.MF and add the following imported packages:

  • org.eclipselabs.restlet
  • org.restlet
  • org.restlet.resource

Create a new package: org.eclipselabs.restlet.examples.app and add the following HelloResource:

package org.eclipselabs.restlet.examples.app;

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;

public class HelloResource extends ServerResource
{
  @Get("txt")
  public String sayHello()
  {
    return "Hello RESTful World";
  }
}

In that same package, add the following HelloResourceProvider:

import org.eclipselabs.restlet.ResourceProvider;
import org.restlet.Context;
import org.restlet.resource.Finder;

public class HelloResourceProvider extends ResourceProvider
{
  @Override
  protected Finder createFinder(Context context)
  {
    return new Finder(context, HelloResource.class);
  }
}

At the root of the project create an OSGI-INF folder to hold our service declarations.  In the OSGi-INF folder, create the following declarative service as application.xml:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app">
   <implementation class="org.eclipselabs.restlet.ApplicationProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IApplicationProvider"/>
   </service>
   <property name="alias" type="String" value="/"/>
   <reference bind="bindRouterProvider" cardinality="1..1" interface="org.eclipselabs.restlet.IRouterProvider" name="IRouterProvider" policy="static" unbind="unbindRouterProvider"/>
</scr:component>

Create the following declarative service as router.xml:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app.router">
   <implementation class="org.eclipselabs.restlet.RouterProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IRouterProvider"/>
   </service>
   <reference bind="bindResourceProvider" cardinality="1..n" interface="org.eclipselabs.restlet.IResourceProvider" name="IResourceProvider" policy="dynamic" unbind="unbindResourceProvider"/>
</scr:component>

Create the following declarative service as hello.xml:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app.hello">
   <implementation class="org.eclipselabs.restlet.examples.app.HelloResourceProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IResourceProvider"/>
   </service>
   <property name="paths" type="String">
     /hello
   </property>
</scr:component>

If you are not using the component definition editor to create your declarative services, be sure to add the following to the MANIFEST.MF:

Service-Component: OSGI-INF/application.xml, OSGI-INF/router.xml, OSGI-INF/hello.xml

You are now ready to run and test your web application.  Add the following bundles to the launch configuration you created in the Setup step:

  • org.eclipselabs.restlet.examples.app
  • org.eclipse.equinox.common
  • org.eclipse.equinox.ds
  • org.eclipse.equinox.util
  • org.eclipselabs.restlet
  • org.eclipselabs.restlet.servlet
  • org.restlet
  • org.restlet.ext.servlet

Run the application and point your browser to http://localhost:8080/hello.  You should see “Hello RESTful World”.

Run Configurations 2

One of the neat features of Restlet is that you can specify more than one representation in a resource.  Let’s add an HTML representation to our hello resource:

package org.eclipselabs.restlet.examples.app;

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;

public class HelloResource extends ServerResource
{
  @Get("txt")
  public String sayHello()
  {
    return "Hello RESTful World";
  }

  @Get("html")
  public String getDocument()
  {
    StringBuilder html = new StringBuilder();
    html.append("<html>\n");
    html.append("  <body>\n");
    html.append("    <h2>Hello RESTful World</h2>\n");
    html.append("   </body>\n");
    html.append("</html>\n");
    return html.toString();
  }
}

Now, when you reload http://localhost:8080/hello in your browser, you should see “Hello RESTful World” in a Heading 2 style.  The complete solution for this example application can be downloaded from the Restlet Extensions for OSGi Environments downloads page.

Applications

Your OSGi web application will consist of one or more Restlet Application instances.  An application is created by declaring an OSGi service component of type ApplicationProvider which provides the service IApplicationProvider, and has an “alias” property that specifies the root path of the application.  The alias of each Restlet application in your OSGi application must be unique, start with “/” and not end with “/” with the exception of “/” itself.  It should not be necessary to extend ApplicationProvider, but you must declare the service in your own bundle.  The ApplicationProvider has a dependency on the IRouterProvider service.  This dependency should be declared as static with a cardinality of 1..1 and bind functions (un)bindRouterProvider().

Suppose you wanted to create more than one Application.  Each Application needs its own instance of a Router.  How does OSGi declarative services know which IRouterProvider to bind to which ApplicationProvider?  When you declare the dependency from ApplicationProvider to IRouterProvider, there is a target parameter you can set to specify the exact IRouterProvider to bind against.  Here is an example of declaring two applications each binding to a unique router using the component.name property given to the service instance.

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app1">
   <implementation class="org.eclipselabs.restlet.ApplicationProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IApplicationProvider"/>
   </service>
   <property name="alias" type="String" value="/app1"/>
   <reference bind="bindRouterProvider" cardinality="1..1" interface="org.eclipselabs.restlet.IRouterProvider" name="IRouterProvider" policy="static" target="(component.name=org.eclipselabs.restlet.examples.app.router1)" unbind="unbindRouterProvider"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app2">
   <implementation class="org.eclipselabs.restlet.ApplicationProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IApplicationProvider"/>
   </service>
   <property name="alias" type="String" value="/app2"/>
   <reference bind="bindRouterProvider" cardinality="1..1" interface="org.eclipselabs.restlet.IRouterProvider" name="IRouterProvider" policy="static" target="(component.name=org.eclipselabs.restlet.examples.app.router2)" unbind="unbindRouterProvider"/>
</scr:component>

Routers

Every Restlet Application instance needs at least one Router instance.  Technically speaking, this isn’t entirely true, but for all practical purposes, you are going to want a Router.  A router is created by declaring an OSGi service component of type RouterProvider which provides the service IRouterProvider.  The RouterProvider has a dependency on IResourceProvider and IRouterProvider services.  The dependency on IResourceProvider should be declared as dynamic with a cardinality of 1..n and bind functions (un)bindResourceProvider().  This allows more than one resource to bind to a router and allows the resources to come and go without taking down the application.  The dependency on IRouterProvider is optional and if declared should be dynamic with a cardinality of 0..1 and bind functions (un)bindDefaultRouterProvider().  The default router is asked to handle the URI routing when none of the resources attached to the router can handle the URI.  The default router will make more sense when I discuss filters below.

When an application contains multiple routers, a target filter must be applied to the IResourceProvider services so that the resources are bound to the correct router.  Since there can be many resources attached to a router, and each resource has a unique component.name, it’s not practical to use the component.name as a filter.  I recommend giving each resource a property with a common value that is unique to a router.  For example, if a resource has the property: type = public, then the router provider can bind to resources with the target (type = public).  For the case of multiple applications, you may want to create a property that identifies the application such as: app = app1.  The binding target is an LDAP filter, so you can combine both the app and type properties: (&(app = app1)(type = public)).  Here is an example of declaring two routers for two different applications:

<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app.router1">
   <implementation class="org.eclipselabs.restlet.RouterProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IRouterProvider"/>
   </service>
   <reference bind="bindResourceProvider" cardinality="1..n" interface="org.eclipselabs.restlet.IResourceProvider" name="IResourceProvider" policy="dynamic" target="(app=app1)" unbind="unbindResourceProvider"/>
</scr:component>
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app.router2">
   <implementation class="org.eclipselabs.restlet.RouterProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IRouterProvider"/>
   </service>
   <reference bind="bindResourceProvider" cardinality="1..n" interface="org.eclipselabs.restlet.IResourceProvider" name="IResourceProvider" policy="dynamic" target="(app=app2)" unbind="unbindResourceProvider"/>
</scr:component>

Resources

Resources are where you define exactly how an external client interacts with your application by responding to HTTP GET, PUT, POST, and DELETE requests.  You must create two classes for every logical resource your application provides: the resource class itself extending ServerResource from the Restlet framework, and a resource provider extending ResourceProvider from the Restlet Extension for OSGi Environments framework.  A resource is created by declaring an OSGi service component of the type you extended from ResourceProvider which provides the service IResourceProvider.  The ResourceProvider does not require dependencies on any other services, but does require a property named paths which is a string array of paths relative to the application alias.  The resource is registered with the router for each path declared in the paths property.  Note that the PDE component definition editor does not support array properties, so you will have to switch to the Source tab to create the paths property.  It is necessary to extend ResourceProvider to provide the classloading “glue” between Restlet and OSGi.  Your resource provider should override Finder createFinder(Context context) and return an instance of a new Finder passing in the Context and the class of your resource.

Here is an example of two resources that respond to http://localhost:8080/hello and http://localhost:8080/example:

package org.eclipselabs.restlet.examples.app;

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;

public class HelloResource extends ServerResource
{
  @Get("txt")
  public String sayHello()
  {
    return "Hello RESTful World";
  }
}
import org.eclipselabs.restlet.ResourceProvider;
import org.restlet.Context;
import org.restlet.resource.Finder;

public class HelloResourceProvider extends ResourceProvider
{
  @Override
  protected Finder createFinder(Context context)
  {
    return new Finder(context, HelloResource.class);
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.app.hello">
   <implementation class="org.eclipselabs.restlet.examples.app.HelloResourceProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IResourceProvider"/>
   </service>
   <property name="paths" type="String">
     /hello
   </property>
</scr:component>
package org.eclipselabs.restlet.examples.resource;

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;

public class ExampleResource extends ServerResource
{
  @Get
  @Override
  public String toString()
  {
    return "Example";
  }
}
package org.eclipselabs.restlet.examples.resource;

import org.eclipselabs.restlet.ResourceProvider;
import org.restlet.Context;
import org.restlet.resource.Finder;

public class ExampleResourceProvider extends ResourceProvider
{
  @Override
  protected Finder createFinder(Context context)
  {
    return new Finder(context, ExampleResource.class);
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.resource">
   <implementation class="org.eclipselabs.restlet.examples.resource.ExampleResourceProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.IResourceProvider"/>
   </service>
   <property name="paths" type="String">
   	/example
   </property>
</scr:component>

Filters

Filters, just as the name implies, provide a mechanism for filtering a request as it is routed to the resource.  The typical use-case for a filter is to protect resources from unauthorized access.  Filters are created similar to resources by extending both Filter and FilterProvider.  A filter is created by declaring an OSGi service component of the type you extended from FilterProvider which provides the service IFilterProvider.   The FilterProvider does not require dependencies on any other services.  Your filter provider should override Filter createFilter(Context context) and return an instance of a new Filter passing in the Context.

Filters can be wired to routers, resources, and other filters.  The wiring is done by placing a service dependency on IFilterProvider in the component being filtered.  Notice that RouterProvider, ResourceProvider and FilterProvider all extend from RestletProvider which has a bindFilterProvider() function.  When declaring filters, you will probably want to use the component.name property as a target.  Here is an example of an authentication and authorization filter:

package org.eclipselabs.restlet.examples.security;

import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.Cookie;
import org.restlet.security.Authenticator;
import org.restlet.security.User;

public class SessionAuthenticator extends Authenticator
{
  public SessionAuthenticator(Context context)
  {
    super(context, false, new SessionEnroller());
  }

  @Override
  public boolean authenticate(Request request, Response response)
  {
    Cookie sessionCookie = request.getCookies().getFirst("session");
    Account account = AccountManager.getInstance().getAccount(sessionCookie.getSecond());

    if(account != null)
    {
      User user = new User(account.getCredential().getId(), account.getID());
      request.getClientInfo().setUser(user);
      return true;
    }

    return false;
  }
}
package org.eclipselabs.restlet.examples.security.providers;

import org.eclipselabs.restlet.components.FilterProvider;
import org.restlet.Context;
import org.restlet.routing.Filter;

import org.eclipselabs.restlet.examples.security.SessionAuthenticator;

public class SessionAuthenticationFilterProvider extends FilterProvider
{
  @Override
  protected Filter createFilter(Context context)
  {
    return new SessionAuthenticator(context);
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipselabs.restlet.examples.authentication">
   <implementation class="org.eclipselabs.restlet.examples.security.SessionAuthenticationFilterProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.providers.IFilterProvider"/>
   </service>
</scr:component>
package org.eclipselabs.restlet.examples.security;

import org.restlet.Request;
import org.restlet.Response;
import org.restlet.security.Authorizer;
import org.restlet.security.Role;

public class SessionAuthorizer extends Authorizer
{
  @Override
  protected boolean authorize(Request request, Response response)
  {
    String accountID = request.getOriginalRef().getSegments().get(2);
    return accountID != null && accountID.equals(new String(request.getClientInfo().getUser().getSecret()));
  }
}
package org.eclipselabs.restlet.examples.security.providers;

import org.eclipselabs.restlet.components.FilterProvider;
import org.restlet.Context;
import org.restlet.routing.Filter;

import org.eclipselabs.restlet.examples.security.SessionAuthorizer;

public class SessionAuthorizerFilterProvider extends FilterProvider
{
  @Override
  protected Filter createFilter(Context context)
  {
    return new SessionAuthorizer();
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipselabs.restlet.examples.authorization">
   <implementation class="org.eclipselabs.restlet.examples.security.providers.SessionAuthorizerFilterProvider"/>
   <reference bind="bindFilterProvider" cardinality="1..1" interface="org.eclipselabs.restlet.providers.IFilterProvider" name="IFilterProvider" policy="static" target="(component.name=org.eclipselabs.restlet.examples.authentication)" unbind="unbindFilterProvider"/>
   <service>
      <provide interface="org.eclipselabs.restlet.providers.IFilterProvider"/>
   </service>
</scr:component>

Service Dependency Injection

The Restlet Extensions for OSGi Environments framework provides support for dependency injection into your resources.  If your resource extends InjectedServerResource and your resource provider creates an instance of InjectedFinder, you may then use @Inject in your resource to obtain references to other services such as the OSGi LogService.  Here is an example of creating a resource that uses dependency injection with the OSGi LogService.

package org.eclipselabs.restlet.examples.resources;

import javax.inject.Inject;

import org.eclipselabs.restlet.di.eclipse.InjectedServerResource;
import org.osgi.service.log.LogService;

public class MyServerResource extends InjectedServerResource
{
  protected void log(int level, String message)
  {
    log(level, message, null);
  }

  protected void log(int level, String message, Throwable exception)
  {
    LogService logService = this.logService;

    if (logService != null)
      logService.log(level, message, exception);
  }

  @Inject
  private LogService logService;
}

References

  1. Restlet – RESTful web framework
  2. Restlet Extension for OSGi Environments – Restlet integration with OSGi
  3. Restlet in Action – Book on Restlet
  4. OSGi as a Web Application Server – Blog post on setting up OSGi as a web application server

Update Sept 28, 2011: Changed the project name to Restlet Extension for OSGi Environments and updated link to GitHub.

31 thoughts on “OSGi at REST

  1. If you would use the bnd annotations you would never have to use XML and get the injection/uninjection for free …

    But I loved this sentence: “And, since you are using OSGi services, you can add and remove resources at runtime and it all just works.” I think this is so true and not realized by so many people …

    Peter Kriens

  2. Pingback: OSGi at REST | Eclipse | Syngu

  3. Hi, I’m trying to setup a reverse proxy using this extension for osgi. I’ve extended application/applicationprovider, router/routerprovider and resource/resourceprovider. In routerprovider, I override createrouter to setup the redirector. i’ve also included a WEB-INF/web.xml in the osgi bundle to add HTTP to the list of client connectors. I can access my resource no problem, but trying to use the reverse proxy always ends with HTTP protocol not declared in list of client connectors.

    Do you know if there is a missing piece to wire the redirector when using the declarative services?

    • Getting a connector to work in an OSGi environment can be tricky. Here are a couple of options that you can try.

      Try something like this in your application provider…


      public class MyApplicationProvider extends ApplicationProvider
      {
      @Override
      protected Application doCreateApplication(Context context)
      {
      Application application = new MyApplication();
      application.setContext(context);
      return application;
      }

      @Override
      public Dictionary getInitParms()
      {
      Hashtable initParms = new Hashtable();
      // initParms.put("org.restlet.clients", "HTTP");
      initParms.put("org.restlet.clients", "FILE");
      return initParms;
      }
      }

      Try something like this in a bundle activator…


      Engine engine = Engine.getInstance();
      engine.registerHelper(HttpClientHelper.class.getClassLoader(), "org.restlet.ext.httpclient.HttpClientHelper", engine.getRegisteredClients(), Client.class);

  4. overriding getInitParms seems to do the trick! Thx much!

    The constructor for application now takes context, so I didn’t have to setContext separately. Thx again!

  5. I am new to OSGI and I have been trying to follow your example, but I am having some issues I hope you can help me with. I have configured the target platform as you described and I am able to hit http://localhost:8080 and get a 404 error back but I still get a 404 when attempting to reach http://localhost:8080/hello. I have downloaded your sample projects (org.eclipselabs.restlet.examples.app and org.eclipselabs.restlet.examples.resource) and imported them into Eclipse and everything builds and runs (in Eclipse) with no errors. The only problem I can see is that the state of the eclipselabs bundles is ‘RESOLVED’ instead of ‘ACTIVE’. I thought this may have been related to the noShutdown VM option but I have checked to make sure I have the VM option -Dosgi.noShutdown=true in the run configuration. If I lookup the bundle information on the org.eclipselabs.restlet.examples.app bundle it says “No Registered Services” which does not seem right. Do you have any suggestions on how I might resolve this?

      • Both of those bundles are active. Here is a full list:

        Framework is launched.

        id State Bundle
        0 ACTIVE org.eclipse.osgi_3.7.2.v20120110-1415
        Fragments=13, 23, 24
        1 ACTIVE org.apache.commons.el_1.0.0.v201101211617
        2 ACTIVE org.eclipse.equinox.http.jetty_2.0.100.v20110502
        3 RESOLVED org.restlet.ext.osgi.examples.app_0.5.1.qualifier
        4 ACTIVE org.restlet.ext.servlet_2.0.12.0
        5 ACTIVE org.apache.commons.logging_1.0.4.v201101211617
        6 ACTIVE org.mortbay.jetty.util_6.1.23.v201012071420
        7 ACTIVE org.eclipse.equinox.ds_1.3.1.R37x_v20110701
        8 ACTIVE org.apache.commons.logging_1.1.0
        9 ACTIVE javax.servlet_2.5.0.v201103041518
        10 ACTIVE org.restlet.ext.osgi.servlet_0.5.1.201111202205
        11 RESOLVED org.restlet.ext.osgi.examples.resource_0.5.1.qualifier
        12 ACTIVE org.eclipse.equinox.http.servlet_1.1.200.v20110502
        13 RESOLVED org.eclipse.equinox.servletbridge.extensionbundle_1.2.0.v20100503
        Master=0
        14 ACTIVE org.apache.ant_1.8.2.v20120109-1030
        15 ACTIVE org.restlet_2.0.12.0
        16 ACTIVE org.mortbay.jetty.server_6.1.23.v201012071420
        17 ACTIVE org.eclipse.osgi.services_3.3.0.v20110513
        18 ACTIVE org.eclipse.equinox.util_1.0.300.v20110502
        19 ACTIVE org.restlet.ext.osgi_0.5.1.201111202205
        20 ACTIVE org.apache.jasper_5.5.17.v201101211617
        21 ACTIVE javax.servlet.jsp_2.0.0.v201101211617
        22 ACTIVE org.eclipse.equinox.common_3.6.0.v20110523
        23 RESOLVED org.eclipse.equinox.transforms.hook_1.0.300.v20100719
        Master=0
        24 RESOLVED org.eclipse.equinox.weaving.hook_1.0.100.v20110502
        Master=0

    • The example bundles don’t have an activation policy, so you must start them by hand: start 3, and start 11 for your example below. When creating your own bundles, you probably want to set the bundle activation policy to lazy.

      • Starting the bundles manually seems to have no effect (I ran ‘start x’ where x is the bundle id). I do not get an error, but after starting a bundle the status is still ‘Resolved’. I also tried setting the Activation policy to lazy (by adding ‘Bundle-ActivationPolicy: lazy’ to the manfest.mf file) and I am still seeing the same issue.

    • Ok, I’ll dig into this a bit more tomorrow night. If you want to try something until then, turn on tracing on org.eclipse.equinox.ds in your launch configuration. Sometimes that can point you in the right direction.

    • I have figured out what is going on. My extensions require Restlet 2.1. If you add -consoleLog, you should see a NoSuchMethodException when you issue a GET. If you remove Restlet 2.0 from your target platform and add Restlet 2.1 from their p2 repository (http://p2.restlet.org/2.1/) it should work. You can’t use the downloadable version of Restlet 2.1 because that build is missing activators.

      • OK, I have updated to Restlet 2.1. I have also removed to optional imports. I’ve confirmed that the consoleLog switch is included in the program arguments, as far as I know it is there by default. I am not seeing an exception when doing a GET. Here is the state of things now:

        id State Bundle
        0 ACTIVE org.eclipse.osgi_3.7.2.v20120110-1415
        1 ACTIVE org.eclipse.osgi.services_3.3.0.v20110513
        2 ACTIVE javax.servlet_2.5.0.v201103041518
        3 ACTIVE org.eclipse.equinox.ds_1.3.1.R37x_v20110701
        4 ACTIVE org.eclipse.equinox.http.servlet_1.1.200.v20110502
        5 ACTIVE org.eclipse.equinox.util_1.0.300.v20110502
        6 ACTIVE org.restlet.ext.servlet_2.1.0.snapshot-v20120324-2215
        7 ACTIVE org.restlet.ext.osgi_0.5.1.201111202205
        8 ACTIVE org.mortbay.jetty.util_6.1.23.v201012071420
        9 RESOLVED org.restlet.ext.osgi.examples.app_0.5.1.qualifier
        10 ACTIVE org.mortbay.jetty.server_6.1.23.v201012071420
        11 ACTIVE org.restlet_2.1.0.snapshot-v20120324-2215
        12 ACTIVE org.eclipse.equinox.common_3.6.0.v20110523
        13 ACTIVE org.eclipse.equinox.http.jetty_2.0.100.v20110502

        I have tried starting bundle 9, but it has no effect as before.

    • If I’m reading your list of bundles correctly (only running on 4 hours of sleep), you are missing the org.restlet.ext.osgi.servlet bundle. Here’s my list that’s working for me:

      osgi> ss

      Framework is launched.

      id State Bundle
      0 ACTIVE org.eclipse.osgi_3.7.2.v20120110-1415
      1 ACTIVE org.restlet_2.1.0.snapshot-v20120324-2215
      2 ACTIVE org.eclipse.equinox.http.jetty_2.0.100.v20110502
      3 ACTIVE org.mortbay.jetty.util_6.1.23.v201012071420
      4 ACTIVE org.eclipse.equinox.ds_1.3.1.R37x_v20110701
      5 ACTIVE org.restlet.ext.osgi_0.5.1.201111202205
      6 ACTIVE org.eclipse.osgi.services_3.3.0.v20110513
      7 ACTIVE org.restlet.ext.osgi.examples.app_0.5.1.qualifier
      8 ACTIVE org.restlet.ext.servlet_2.1.0.snapshot-v20120324-2215
      9 ACTIVE org.restlet.ext.osgi.servlet_0.5.1.201111202205
      10 ACTIVE javax.servlet_2.5.0.v201103041518
      11 ACTIVE org.mortbay.jetty.server_6.1.23.v201012071420
      12 ACTIVE org.eclipse.equinox.util_1.0.300.v20110502
      13 ACTIVE org.eclipse.equinox.http.servlet_1.1.200.v20110502

      • Looks like we are making some progress. I have made my list of bundles match yours exactly and I can see that the org.restlet.ext.osgi.examples.app bundle is now active. Unfortunately I am still unable to call the /hello service. I am now seeing Exceptions in the console (java.lang.ClassNotFoundException: org.restlet.ext.osgi.examples.app.HelloResourceProvider) This occurs several times.

    • Set the “Clear the configuration area before launching” option on the Settings tab of the launch configuration and re-run. If that doesn’t do it, try starting with a fresh workspace and target platform. If you still get the exception, it might be time for a Skype screen sharing session.

      • Sorry, Im not having any luck. I’ve tried a fresh eclipse install on a different machine and I am still geting the same error. I must be missing a step somewhere. I had a couple of thoughts, I was not using the default location for my workspace. I tried switching back to the default workspace location, but it still didnt work, I’m not sure where Eclipse gets the path for bundle classes from. The framework cannot seem to find the classes in the bundle in my workspace, I am working under the impression that Eclipse automagically deploys the class files when I run the code from the run configurations dialog. Do I have to manually build the bundle (or export them as jar files) and install them somewhere before running the project? I only have the two example bundles in my workspace (org.restlete.ext.osgi.examples.app and org.restlet.ext.osgi.examples.resource), are there any others I need?

        Thats all I was able to come up with. If you are willing have a look at the issue over skype I would really appreciate it.

  6. First, thank you for your detail instructions. It’s a very good tutorial! But i get an exception, when i try to call http://localhost:8080/hello – and i have no clue, what the reason might be.
    Would be so kind and take a look?

    Thank you!

    HTTP ERROR 500

    Problem accessing /hello. Reason:

    org.restlet.routing.Router.attach(Ljava/lang/String;Lorg/restlet/Restlet;)Lorg/restlet/routing/Route;
    Caused by:

    java.lang.NoSuchMethodError: org.restlet.routing.Router.attach(Ljava/lang/String;Lorg/restlet/Restlet;)Lorg/restlet/routing/Route;
    at org.eclipselabs.restlet.RouterProvider.attachResource(RouterProvider.java:113)
    at org.eclipselabs.restlet.RouterProvider.getInboundRoot(RouterProvider.java:58)
    at org.eclipselabs.restlet.ApplicationProvider.createApplication(ApplicationProvider.java:41)
    at org.eclipselabs.restlet.servlet.ApplicationServlet.createApplication(ApplicationServlet.java:40)
    at org.restlet.ext.servlet.ServerServlet.getApplication(ServerServlet.java:772)
    at org.restlet.ext.servlet.ServerServlet.createServer(ServerServlet.java:508)
    at org.restlet.ext.servlet.ServerServlet.getServer(ServerServlet.java:915)
    at org.restlet.ext.servlet.ServerServlet.service(ServerServlet.java:1086)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.eclipse.equinox.http.servlet.internal.ServletRegistration.service(ServletRegistration.java:61)
    at org.eclipse.equinox.http.servlet.internal.ProxyServlet.processAlias(ProxyServlet.java:126)
    at org.eclipse.equinox.http.servlet.internal.ProxyServlet.service(ProxyServlet.java:76)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
    at org.eclipse.equinox.http.jetty.internal.HttpServerManager$InternalHttpServiceServlet.service(HttpServerManager.java:318)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:390)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:924)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:549)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

      • I’m surprised that switching to Equinox 3.7 fixed the problem. I use 3.8 in production with this code. Would you be able to give me a zip of your projects?

  7. Hi Bryan!
    Thank you for your tutorial! Just a quick question. What’s up with other annotations? I was wondering, as annotations like
    @Consumes( MediaType.TEXT_PLAIN )
    @Produces( MediaType.TEXT_PLAIN )
    seems not to work. My goal is it to send a ZIP file via POST Reqest.
    Thanks and best wishes,
    Peter

    • I believe @Consumes() and @Produces() are specific to Jersey and I don’t know they are supported by Restlet. My code is specific to Restlet and should support any of the annotations supported by Restlet.

  8. Hi Brian,

    I know it has been awhile since you posted this article and the Restlet framework has evolved since then (i.e. “org.eclipselabs.restlet.IApplicationProvider” vs “org.restlet.ext.osgi.ApplicationProvider”). However, your AH-HA moment to use OSGi declarative services still seems to be the best approach. I was able to update some of your code with the Restlet 2.3 API and I almost got it working. The Jetty server is up and running when I launch it from Eclipse Kepler and the code reaches the createFinder() method in my ResourceProvider class; which extends BaseResourceProvider. However, I get ClassNotFoundException when it reaches the EngineClassLoader::findClass() method and tries to load the ServletServerAdapter class. Any suggestion or comment? Thanks!

    osgi> ss
    “Framework is launched.”

    id State Bundle
    0 ACTIVE org.eclipse.osgi_3.9.1.v20140110-1610
    1 ACTIVE org.apache.log4j_1.2.15.v201012070815
    2 ACTIVE org.eclipse.jetty.http_8.1.14.v20131031
    3 ACTIVE org.eclipse.equinox.console_1.0.100.v20130429-0953
    4 ACTIVE org.apache.felix.gogo.command_0.10.0.v201209301215
    6 ACTIVE org.eclipse.equinox.http.servlet_1.1.400.v20130418-1354
    7 ACTIVE org.apache.xml.resolver_1.2.0.v201005080400
    11 ACTIVE org.eclipse.equinox.common_3.6.200.v20130402-1505
    12 ACTIVE org.apache.felix.gogo.runtime_0.10.0.v201209301036
    13 ACTIVE org.eclipse.equinox.http.jetty_3.0.100.v20130327-1442
    15 ACTIVE org.eclipse.jetty.util_8.1.14.v20131031
    16 ACTIVE org.eclipse.equinox.registry_3.5.301.v20130717-1549
    17 ACTIVE org.apache.commons.codec_1.4.0.v201209201156
    18 ACTIVE org.eclipse.equinox.http.registry_1.1.300.v20130402-1529
    20 ACTIVE org.apache.xml.serializer_2.7.1.v201005080400
    22 ACTIVE org.eclipse.equinox.app_1.3.100.v20130327-1442
    23 ACTIVE org.eclipse.jetty.security_8.1.14.v20131031
    24 ACTIVE org.restlet.ext.osgi_2.3.0.v20141217-1730
    26 ACTIVE javax.servlet_3.0.0.v201112011016
    27 ACTIVE org.mortbay.jetty.util_6.1.23.v201012071420
    28 ACTIVE org.restlet.ext.servlet_2.3.0.v20141217-1730
    30 ACTIVE org.apache.commons.httpclient_3.1.0.v201012070820
    31 ACTIVE org.apache.felix.gogo.shell_0.10.0.v201212101605
    33 ACTIVE org.eclipse.jetty.continuation_8.1.14.v20131031
    34 ACTIVE org.eclipse.jetty.server_8.1.14.v20131031
    35 ACTIVE org.apache.commons.logging_1.1.1.v201101211721
    36 ACTIVE org.eclipse.jetty.servlet_8.1.14.v20131031
    37 ACTIVE javax.xml_1.3.4.v201005080400
    39 ACTIVE org.apache.xerces_2.9.0.v201101211617
    40 ACTIVE org.eclipse.osgi.services_3.3.100.v20130513-1956
    41 ACTIVE org.restlet.ext.xml_2.3.0.v20141217-1730
    42 ACTIVE org.restlet_2.3.0.v20141217-1730
    45 ACTIVE org.eclipse.jetty.io_8.1.14.v20131031
    46 ACTIVE org.apache.xalan_2.7.1.v201005080400
    47 ACTIVE org.mortbay.jetty.server_6.1.23.v201012071420
    53 ACTIVE org.eclipse.equinox.ds_1.4.101.v20130813-1853
    54 ACTIVE org.eclipse.equinox.util_1.0.500.v20130404-1337
    63 ACTIVE org.eclipse.restlet.example_1.0.0.qualifier

    • I don’t have a solution for you right now. I hope to have time to look into this in a couple weeks. I have another project that needs priority.

      >

  9. I have the same problem… with 2.3.4 and Equinox Mars and Jetty 9.3.3 😦

    Exception trace below:

    Sep 16, 2015 7:08:54 PM org.restlet.engine.adapter.HttpServerHelper getAdapter
    SEVERE: Unable to create the HTTP server adapter
    java.lang.ClassNotFoundException: org.restlet.ext.servlet.internal.ServletServerAdapter
    at org.restlet.engine.util.EngineClassLoader.findClass(EngineClassLoader.java:95)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.restlet.engine.Engine.loadClass(Engine.java:356)
    at org.restlet.engine.adapter.HttpServerHelper.getAdapter(HttpServerHelper.java:102)
    at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:141)
    at org.restlet.ext.servlet.ServerServlet.service(ServerServlet.java:1117)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl$LegacyServlet.service(HttpServiceRuntimeImpl.java:1271)
    at org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration.service(EndpointRegistration.java:162)
    at org.eclipse.equinox.http.servlet.internal.servlet.ResponseStateHandler.processRequest(ResponseStateHandler.java:63)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:413)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:365)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:204)
    at org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet.processAlias(ProxyServlet.java:91)
    at org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet.service(ProxyServlet.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:816)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:224)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1156)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1088)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:119)
    at org.eclipse.jetty.server.Server.handle(Server.java:517)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:306)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:242)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:245)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:75)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:213)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:147)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
    at java.lang.Thread.run(Thread.java:745)

    Sep 16, 2015 7:08:54 PM org.restlet.engine.adapter.HttpServerHelper handle
    WARNING: Error while handling an HTTP server call
    java.lang.NullPointerException
    at org.restlet.engine.adapter.HttpServerHelper.handle(HttpServerHelper.java:141)
    at org.restlet.ext.servlet.ServerServlet.service(ServerServlet.java:1117)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl$LegacyServlet.service(HttpServiceRuntimeImpl.java:1271)
    at org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration.service(EndpointRegistration.java:162)
    at org.eclipse.equinox.http.servlet.internal.servlet.ResponseStateHandler.processRequest(ResponseStateHandler.java:63)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:413)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:365)
    at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:204)
    at org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet.processAlias(ProxyServlet.java:91)
    at org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet.service(ProxyServlet.java:70)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:816)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:583)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:224)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1156)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1088)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:119)
    at org.eclipse.jetty.server.Server.handle(Server.java:517)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:306)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:242)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:245)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
    at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:75)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:213)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:147)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
    at java.lang.Thread.run(Thread.java:745)

  10. After a quick debug I see the Engine is trying to load class “org.restlet.ext.servlet.internal.ServletServerAdapter” but the Engine’s class loader does not see that class… by looking at the source of bundle org.restlet.ext.servlet I did not see any place to register class loader or something like that.. strange to me…

    So to make it work I made a dirty modification – I modified org.restlet.jar/MANIFEST.MF and add update the import package: org.restlet.ext.servlet.internal and org.restlet.ext.servlet. It works.

    I appreciate if someone helps to resolve this problem in a clean way.. Thank you!

Leave a reply to Matthew Cancel reply