Customizing and Extending ECF Remote Services

From Eclipsepedia

Jump to: navigation, search

The ECF Remote Service Admin implementation has been made customizable and extensible, so that others can change the default behavior.

Contents

Service Host: Customizing Remote Service Registration

When a remote service is registered, the code will look something like this

Properties props = new Properties();
// add OSGi service property indicated export of all interfaces exposed by service (wildcard)
props.put(IDistributionConstants.SERVICE_EXPORTED_INTERFACES,IDistributionConstants.SERVICE_EXPORTED_INTERFACES_WILDCARD);
// add OSGi service property specifying config
props.put(IDistributionConstants.SERVICE_EXPORTED_CONFIGS, containerType);
// register remote service
helloRegistration = bundleContext.registerService(IHello.class.getName(), new Hello(), props);

The OSGi 4.2 specification defines the meaning and interpretation of the two service properties...i.e. IDistributionConstants.SERVICE_EXPORTED_INTERFACES and IDistributionConstants.SERVICE_EXPORTED_CONFIGS...and the ECF implementation interprets these services properties and does the distribution. The ECF implementation, however, allows the customization/extension of the default behavior, by exposing the following interface:

org.eclipse.ecf.osgi.services.remoteserviceadmin.IHostContainerSelector

/*******************************************************************************
 * Copyright (c) 2010-2011 Composent, Inc. and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Composent, Inc. - initial API and implementation
 ******************************************************************************/
package org.eclipse.ecf.osgi.services.remoteserviceadmin;
 
import java.util.Map;
 
import org.eclipse.ecf.remoteservice.IRemoteServiceContainer;
import org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter;
import org.osgi.framework.ServiceReference;
 
/**
 * Host container selector service contract. When an ECF RemoteServiceAdmin
 * instance is asked to import a service (i.e. via
 * {@link RemoteServiceAdmin#exportService(ServiceReference, java.util.Map)} ),
 * the RSA first gets an instance of this service via the service registry, and
 * then uses it to select an array of ECF host container instances by calling
 * {@link #selectHostContainers(ServiceReference, String[], String[], String[])}
 * .
 * <p>
 * <p>
 * The {@link IRemoteServiceContainer} array returned is then used to actually
 * export the remote service (typically via
 * {@link IRemoteServiceContainerAdapter#registerRemoteService(String[], Object, java.util.Dictionary)}
 * <p>
 * <p>
 * If no other instances of this service have been registered, a default
 * instance of {@link HostContainerSelector} will be used. Note that this
 * default instance is registered with the lowest possible priority, so that if
 * other {@link IHostContainerSelector} instances are registered, they will be
 * preferred/used over the default.
 * 
 */
public interface IHostContainerSelector {
	/**
	 * 
	 * Select host containers to use to export a remote service.
	 * 
	 * @param serviceReference
	 *            the service reference given by the
	 *            {@link RemoteServiceAdmin#exportService(ServiceReference, java.util.Map)}
	 * @param overridingProperties
	 *            the map portion given by the
	 *            {@link RemoteServiceAdmin#exportService(ServiceReference, java.util.Map)}
	 * @param exportedInterfaces
	 *            the exportedInterfaces (typically associated with
	 *            {@link org.osgi.service.remoteserviceadmin.RemoteConstants#SERVICE_EXPORTED_INTERFACES}
	 *            ). Will not be <code>null</code>.
	 * @param exportedConfigs
	 *            the exportedConfigs (typically associated with
	 *            {@link org.osgi.service.remoteserviceadmin.RemoteConstants#SERVICE_EXPORTED_CONFIGS}
	 *            ). May be <code>null</code>.
	 * @param serviceIntents
	 *            the service intents (typically associated with
	 *            {@link org.osgi.service.remoteserviceadmin.RemoteConstants#SERVICE_EXPORTED_INTENTS}
	 *            and
	 *            {@link org.osgi.service.remoteserviceadmin.RemoteConstants#SERVICE_EXPORTED_INTENTS_EXTRA}
	 *            ). May be <code>null</code>.
	 * @return IRemoteServiceContainer[] of remote service containers that
	 *         should be used to export the given remote service (typically via
	 *         {@link IRemoteServiceContainerAdapter#registerRemoteService(String[], Object, java.util.Dictionary)}
	 *         ). Will not be <code>null</code>, but may be empty array.
	 * @throws SelectContainerException
	 *             thrown if the host container selection or
	 *             creation/configuration fails.
	 * @since 2.0
	 */
	IRemoteServiceContainer[] selectHostContainers(
			ServiceReference serviceReference,
			Map<String, Object> overridingProperties,
			String[] exportedInterfaces, String[] exportedConfigs,
			String[] serviceIntents) throws SelectContainerException;
}

This class is available in the org.eclipse.ecf.osgi.services.remoteserviceadmin package, which is in the bundle of the same name. Javadocs for that package are available here.

This is a service interface which determines what/which IRemoteServiceContainer instances are used for distributing a remote service (described/defined by the serviceReference, serviceExportedIntefaces, serviceExportedConfigs, and serviceIntents parameters). At service host registration time, the ECF remote services implementation requests from the OSGi service registry implementations of this service interface, and then calls the findHostContainers method to have the service find (by creating, reusing, or otherwise making available) any/all relevant IRemoteServiceContainer instances.

There is a default implementation of this service implemented by the class org.eclipse.ecf.osgi.services.remoteserviceadmin.DefaultHostContainerSelector. If no other implementations of the org.eclipse.ecf.osgi.services.remoteserviceadmin.IHostContainerSelector service interface are found in the service registry, then the default one is used. The way this is implemented is that the DefaultHostContainerInstance instance is registered with it's OSGi service ranking set to the minimum possible, meaning that if any other IHostContainerSelector implementations are registered they will be used in preference to the default one.

So, you may provide your own implementation of IHostContainerSelector and register it as a service using the whiteboard pattern, and then the ECF distribution implementation will use your implementation of the service at runtime. Here's a simple example

public class MyHostContainerSelector extends DefaultHostContainerSelector {
 
   public MyHostContainerSelector() {
      // set the auto create flag to true, so that if the container instance with type specified via SERVICE_EXPORTED_CONFIG 
      // does not already exist, then it will be automatically created
      super(true);
   }
 
// any overrides of existing public or protected methods here
 
}
 
// Then simply register your host container finder as an IHostContainerSelector
bundleContext.registerService(IHostContainerSelector.class.getName(), new MyHostContainerSelector(), null);

After this registration, when a remote service host is registered, your host container selector instance will be called rather than the DefaultHostContainerSelector. In this manner, you can customize the behavior of the IHostContainerSelector by extending DefaultHostContainerSelector (or one of its super classes), or implementing IHostContainerSelector directly.

If you would like to implement IHostContainerSelector directly, it would probably be useful to examine the source code for the DefaultHostContainerSelector class to understand what it is doing. See also the section below explaining the basic behavior of the DefaultHostContainerSelector.

DefaultHostContainerSelector

The DefaultHostContainerSelectorhas the following behavior

  1. It looks for existing ECF IContainer instances. These instances must match the constraints provided by the serviceExportedConfigs, and serviceIntents. See the method org.eclipse.ecf.osgi.services.remoteserviceadmin.AbstractHostContainerSelector.matchExistingHostContainer(ServiceReference, IContainer, IRemoteServiceContainerAdapter, ContainerTypeDescription, String[], String[]) for a standards compliant implementation.
  2. If no existing containers are found and the autoCreateContainer flag is set (during construction of the DefaultHostContainerSelector), then the SERVICE_EXPORTED_CONFIGS values are used to automatically create and configure a new container instance. Note that the autoCreateContainer flag is set in the RSA topology manager upon initial start, and the default value is true (automatically create a new container instance if no existing ones are found). The default can be changed by setting the system property org.eclipse.ecf.osgi.services.remoteserviceadmin.hostAutoCreateContainer...e.g.
<command line>  -Dorg.eclipse.ecf.osgi.services.remoteserviceadmin.hostAutoCreateContainer=false

Service Consumer: Customizing Remote Service container creation, configuration, connect, and remote service lookup

Analogous to the IHostContainerSelector described above, is the IConsumerContainerSelector service interface

/**
 * Consumer container selector service contract. When an ECF RemoteServiceAdmin
 * instance is asked to import a service (i.e. via
 * {@link RemoteServiceAdmin#importService(org.osgi.service.remoteserviceadmin.EndpointDescription)}
 * ), the RSA first gets an instance of this service via the service registry,
 * and then uses it to select an ECF consumer container instance by calling
 * {@link #selectConsumerContainer(EndpointDescription)}.
 * <p>
 * <p>
 * The {@link IRemoteServiceContainer} returned is then used on the consumer
 * side, to actually import the remote service.
 * <p>
 * <p>
 * If no other instances of this service have been registered, a default
 * instance of {@link ConsumerContainerSelector} will be used. Note that this
 * default instance is registered with the lowest possible priority, so that if
 * other {@link IConsumerContainerSelector} instances are registered, they will
 * be preferred/used over the default.
 * 
 */
public interface IConsumerContainerSelector {
 
	/**
	 * Select (or create and initialize) a consumer remote service container.
	 * 
	 * @param endpointDescription
	 *            the endpoint description that has been discovered.
	 * @return IRemoteServiceContainer to be used for importing the remote
	 *         service. May be <code>null</code> if not container is available
	 *         for use as a consumer for the given endpointDescription.
	 * @throws SelectContainerException
	 *             thrown if the host container selection or
	 *             creation/configuration fails.
	 */
	public IRemoteServiceContainer selectConsumerContainer(
			EndpointDescription endpointDescription)
			throws SelectContainerException;
 
}

This class is available in the org.eclipse.ecf.osgi.services.remoteserviceadmin package, which is in the bundle of the same name. Javadocs for that package are available here.

At service discovery time, the single service with the highest OSGi service ranking will be called (i.e. the selectConsumerContainermethod), to determine what IRemoteServiceContainer instances are used to connect to, lookup, and use the remote service described by the given EndpointDescription.

As with the IHostContainerSelector, there is a default implementation of the IConsumerContainerSelector called org.eclipse.ecf.osgi.services.remoteserviceadmin.DefaultConsumerContainerSelector. This default is registered with the lowest service ranking possible, so any IProxyContainerFinders registered will supercede the default.

Here is an example of creating and registering a custom IConsumerContainerSelector

public class MyConsumerContainerSelector extends DefaultConsumerContainerSelector {
 
   public MyConsumerContainerSelector () {
      // set the auto create flag to true, so that if an appropriate container instance 
      // does not already exist, then it will be automatically created
      super(true);
   }
 
// any overrides of existing public or protected methods here
}
 
// Then simply register your host container finder as an IConsumerContainerSelector 
bundleContext.registerService(IConsumerContainerSelector.class.getName(), new MyConsumerContainerSelector(), null);

After this registration, when a remote service is discovered, the custom instance of IProxyContainerFinder (MyConsumerContainerSelector) will be called.

DefaultConsumerContainerSelector

The DefaultConsumerContainerSelector has the following behavior

  1. It looks for existing ECF IContainer instances. It uses the method org.eclipse.ecf.core.ContainerTypeDescription.getImportedConfigs(String[]) to get the relevant imported configs for the local container type, given the configs exported by the remote service host (which are passed as a parameter to getImportedConfigs)
  2. If no existing containers are found and the autoCreateContainer flag is set (during construction of the DefaultHostContainerFinder), then a new container instance is created and configured.

The default can be changed to false by setting the system property org.eclipse.ecf.osgi.services.remoteserviceadmin.consumerAutoCreateContainer...e.g.

<command line>  -Dorg.eclipse.ecf.osgi.services.remoteserviceadmin.consumerAutoCreateContainer=false