Monday, April 16, 2012

Custom services

When implementing RCP applications you sometimes might need to define your own services. For example this could be an abstraction of a database or a core instance providing resources. Often this is done by implementing a singleton which is directly accessed by consumers. Such an implementation does not provide a separation between a service definition and the actual implementation.

With eclipse services we can achieve exactly that with very little overhead. This article is based on the online documentation on services.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Creating the service definition

Start with a new Plug-in Project called com.codeandme.customservice. On the Extensions tab of your plugin.xml add a new extension to org.eclipse.ui.services.

Step 2: Creating the service

We want to declare our service through an interface so we could easily exchange the implementation. Therefore create a new interface com.codeandme.customservice.ICustomService. Declaration of specific methods depends of what your service should provide and is not important for this example.

For the implementation create a new class com.codeandme.customservice.CustomServiceImpl with following content:
package com.codeandme.customservice;

public class CustomServiceImpl implements ICustomService {

 private static CustomServiceImpl fInstance = null;

 public static CustomServiceImpl getInstance() {
  if (fInstance == null)
   fInstance = new CustomServiceImpl();

  return fInstance;
 }

 private CustomServiceImpl() {
 }
}
Go back to your plugin.xml and set the serviceClass to com.codeandme.customservice.ICustomService.

Step 3: Creating a service factory

Create a factory class com.codeandme.customservice.CustomServiceFactory:
package com.codeandme.customservice;

import org.eclipse.ui.services.AbstractServiceFactory;
import org.eclipse.ui.services.IServiceLocator;

public class CustomServiceFactory extends AbstractServiceFactory {

 public CustomServiceFactory() {
 }

 @Override
 public Object create(@SuppressWarnings("rawtypes") Class serviceInterface, IServiceLocator parentLocator, IServiceLocator locator) {
  if (serviceInterface.equals(ICustomService.class)) 
   return CustomServiceImpl.getInstance();

  return null;
 }
}
Head back to your plugin.xml and set factoryClass to com.codeandme.customservice.CustomServiceFactory. Our final service declaration looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
   <extension
         point="org.eclipse.ui.services">
      <serviceFactory
            factoryClass="com.codeandme.customservice.CustomServiceFactory">
         <service
               serviceClass="com.codeandme.customservice.ICustomService"></service>
      </serviceFactory>
   </extension>

</plugin>

Step 4: Using the service

When your application needs a service instance it can directly query the service locator with following snippet (from within a view):
ICustomService customService = (ICustomService) getSite().getService(ICustomService.class);
Or globally with:
ICustomService customService = (ICustomService) PlatformUI.getWorkbench().getService(ICustomService.class);
The source files for this article contain a separate Plug-in using the service.

Final thoughts

At first sight you don't gain a lot with this approach. Real world examples often would separate service definition and implementation into different Plug-ins. When the service is used through its interface and accessed through the ServiceLocator your clients do not have any dependency to the factory or the implementing class. This lets you easily exchange the implementation without altering the clients.

As found in the documentation a service factory might provide different services but for a dedicated serviceClass there may exist only one factory. Such a thing could be done by declaring your own extension points.

It might be a drawback that for services you need a dependency to UI Plug-ins.

No comments:

Post a Comment