Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Using Spring with ECF Remote Services

Revision as of 05:25, 12 February 2010 by Azerr.redhat.com (Talk | contribs) (Consumer)

UNDER CONSTRUCTION by Angelo Zerr and Scott Lewis

Target

Work is underway to support ECF with Spring Dynamic Module to declare your OSGi Services that you want publish (Host aka server side) and retrieve (Consumer aka client side ). See (for the moment) Bug 302113- you can find several bundles that provide  :

  • ...
  • ...

How it works?

When you want use Remote Services with ECF on Consumer bundle (aka client side) and Host buundle (aka server side) you must :

  1. Create an ECF IContainer with a type (eg : "ecf.generic.client" for Consumer and "ecf.generic.server" for Host).
  2. Publish (for Host)/Retrieve (for Consumer) services by using OSGi services registry.

You can manage that with several means :

With Spring Dynamic Module mean :

  • the Publish/Retrieve services from OSGi service registry is managed by Spring DM by using <osgi:service and <osgi:reference.
  • the creation of ECF IContainer can be managed too with Spring by using ECF Spring support.

To explain ECF with Spring Dynamic Module we will use the Hello example which publish/retrieve a basic service IHello. The sample code is bases on last version of ECF 3.2.

ECF with Java code

You can found Hello example with OSGi Java code into :

  • org.eclipse.ecf.examples.remoteservices.hello.host : host bundle which publish the Hello service implémentation with an ECF IContainer type of "ecf.generic.server".
  • org.eclipse.ecf.examples.remoteservices.hello.consumer : consumer bundle which call one time the service IHello by using an ECF IContainer type of "ecf.generic.client".

ECFRemotingServicesWithJavaCode.png

This scheme show you that :

  • the Host bundle create the ECF IContainer and publish the services into the start method of the Bundle.
  • the Consumer bundle create the ECF IContainer and create an OSGi ServiceTracker to track the IHello service into the start method of the Bundle.

Host

Host bundle :

  • create an IContainer with Java code. Into ECF 3.2 the Icontainer is created lazily (Scott could you confirm that?).
  • publish the service into OSGi services registry :
    // Setup properties for remote service distribution, as per OSGi 4.2 remote services
      // specification (chap 13 in compendium spec)
      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);
      // add ECF service property specifying container factory args
      props.put(IDistributionConstants.SERVICE_EXPORTED_CONTAINER_FACTORY_ARGUMENTS, containerId);
      // register remote service
      helloRegistration = bundleContext.registerService(IHello.class.getName(), new Hello(), props);

    Here the same code whith value of constantes :

    // Setup properties for remote service distribution, as per OSGi 4.2 remote services
      // specification (chap 13 in compendium spec)
      Properties props = new Properties();
      // add OSGi service property indicated export of all interfaces exposed by service (wildcard)
      props.put("service.exported.interfaces", "*");
      // add OSGi service property specifying config
      props.put("service.exported.configs", "ecf.generic.server");
      // add ECF service property specifying container factory args
      props.put("org.eclipse.ecf.containerFactoryArgs", "ecftcp://localhost:3787/server");
      // register remote service
      helloRegistration = bundleContext.registerService(IHello.class.getName(), new Hello(), props);

Consumer

Consumer bundle :

  • create an ECF IContainer with Java code :
    getContainerFactory().createContainer("ecf.generic.client");
    ...
     
    private IContainerFactory getContainerFactory() {
      if (containerFactoryServiceTracker == null) {
        containerFactoryServiceTracker = new ServiceTracker(bundleContext, IContainerFactory.class.getName(), null);
        containerFactoryServiceTracker.open();
      }
      return (IContainerFactory) containerFactoryServiceTracker.getService();
    }
  • Retrieve the services with OSGi ServiceTracker :
    // Create service tracker to track IHello instances that have the 'service.imported'
      // property set (as defined by OSGi 4.2 remote services spec).
      helloServiceTracker = new ServiceTracker(bundleContext, createRemoteFilter(), this);
      helloServiceTracker.open();
      ...
     
      private Filter createRemoteFilter() throws InvalidSyntaxException {
        // This filter looks for IHello instances that have the 
        // 'service.imported' property set, as specified by OSGi 4.2
        // remote services spec (Chapter 13)
        return bundleContext.createFilter("(&(" + org.osgi.framework.Constants.OBJECTCLASS + "=" + IHello.class.getName() + ")(" + SERVICE_IMPORTED + "=*))");
      }
      ...
     
      public Object addingService(ServiceReference reference) {
    		System.out.println("IHello service proxy being added");
    		// Since this reference is for a remote service,
    		// The service object returned is a proxy implementing the
    		// IHello interface
    		IHello hello = (IHello) bundleContext.getService(reference);
    		// This makes a remote 'hello' call
    		hello.hello(CONSUMER_NAME);
       ...
       }

    Here the same code of creation of OSGi Filter with constant value :

    private Filter createRemoteFilter() throws InvalidSyntaxException {
      return bundleContext.createFilter("(&(objectClass=org.eclipse.ecf.examples.remoteservices.hello.IHello)(service.imported=*))");

ECF with Spring DM

You can found Hello example with Spring DM into :

  • org.eclipse.ecf.examples.remoteservices.hello.dm.host : host bundle which publish the Hello service implémentation with an ECF IContainer type of ecf.generic.server".
  • org.eclipse.ecf.examples.remoteservices.hello.dm.consumer : consumer bundle which create a Thread HelloClientThread and call the service IHello every second by using an ECF IContainer type of ecf.generic.client".

Here a scheme which explains how ECF works with Spring DM :

ECFRemotingServicesWithSpringDM.png

This scheme show you that ECF IContainer creation and Publish/retrieve services can be totally declarative by using Spring DM. Host and Consumer bundle define a XML Spring file (in this sample module-context.xml is used but you can used any name and it's better to split files for simpel bean definition and osgi definition (cf Spring DM Doc).

The Spring bundle Extender load each XML Spring files stored into META-INF/spring.

  • Host bundle define module-context.xml which publish the services (with ECF 3.2 no need to create the IContainer) by using <osgi:service with classic Spring DM.
  • Consumer bundle define module-context.xml which create the ECF Container by using ECF Spring support and retrieve the service by using <osgi:reference with classic Spring DM.

The Consumer module-conext.xml define a Spring bean which create and start a Thread which use the service IHello getted. The IHello service is filled to this Thread by using Spring Dependency Injection. Here the cod eof this Thread :

package org.eclipse.ecf.examples.internal.remoteservices.hello.dm.consumer;
 
import org.eclipse.ecf.examples.remoteservices.hello.IHello;
 
public class HelloClientThread extends Thread {
 
  private IHello hello;
 
  public void setHello(IHello hello) {
    this.hello = hello;
  }
 
  @Override
  public void run() {
    while (true) {
      try {
        hello.hello("HelloClientThread (Spring DM)");
	System.out.println("IHello service called with HelloClientThread (Spring DM).");
        Thread.sleep(1000);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

When you will launch this consumer you will see into the console th eerror :

org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(&(objectClass=org.eclipse.ecf.examples.remoteservices.hello.IHello)(service.imported=*))] unavailable
	at org.springframework.osgi.service.importer.support.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:419)

which mean Consumer is searching IHello service from the OSGi services registry and once the service can be getted, teh console display each seconde :

IHello service called with HelloClientThread (Spring DM).

Host

  • Create an ECF (server) IContainer with declarative mean by using Spring Dynamic Module :
    <bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
    	<property name="containerType" value="ecf.generic.server" />
    </bean>

    Into ECF 3.2, this declaration is NOT required.

  • Publish you services with declarative mean by using Spring Dynamic Module :
    <bean id="helloService" class="org.eclipse.ecf.examples.remoteservices.hello.impl.Hello" />
     
    <osgi:service ref="helloService"
    	interface="org.eclipse.ecf.examples.remoteservices.hello.IHello">
    	<osgi:service-properties>
    		<entry key="service.exported.interfaces" value="*" />
    		<entry key="service.exported.configs" value="ecf.generic.server" />
                    <entry key="org.eclipse.ecf.containerFactoryArgs" value="ecftcp://localhost:3787/server" />
    	</osgi:service-properties>
    </osgi:service>

Here the full XML file spring module-context.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
	xsi:schemaLocation="http://www.springframework.org/schema/osgi  
       http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
        <!-- 1. Create an ECF (server) IContainer -->
	<bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
		<property name="containerType" value="ecf.generic.server" />
	</bean>
 
	<!-- 2. Publish Hello Service implementation -->
	<bean id="helloService" class="org.eclipse.ecf.examples.remoteservices.hello.impl.Hello" />
 
	<osgi:service ref="helloService"
		interface="org.eclipse.ecf.examples.remoteservices.hello.IHello">
		<osgi:service-properties>
			<entry key="service.exported.interfaces" value="*" />
			<entry key="service.exported.configs" value="ecf.generic.server" />
			<entry key="org.eclipse.ecf.containerFactoryArgs" value="ecftcp://localhost:3787/server" />
		</osgi:service-properties>
	</osgi:service>
 
</beans>

Consumer

  • Create an ECF (client) IContainer with declarative mean by using ECF Spring support :
    <bean class="org.eclipse.ecf.springframework.ConsumerContainerFactoryBean">
    	<property name="containerType" value="ecf.generic.client" />
    </bean>
  • Retrieve the service with declarative mean by using Spring Dynamic Module :
    <osgi:reference id="helloService"
    	interface="org.eclipse.ecf.examples.remoteservices.hello.IHello"
    	timeout="1000" cardinality="0..1" filter="(service.imported=*)" />

Here the full XML file spring module-context.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:osgi="http://www.springframework.org/schema/osgi"
	xsi:schemaLocation="http://www.springframework.org/schema/osgi  
       http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
        <!-- 1. Create an ECF (client) IContainer -->
	<bean class="org.eclipse.ecf.springframework.ConsumerContainerFactoryBean">
		<property name="containerType" value="ecf.generic.client" />
	</bean>
 
        <!-- 2. Retrieve service from OSGi services registry -->
	<osgi:reference id="helloService"
		interface="org.eclipse.ecf.examples.remoteservices.hello.IHello"
		timeout="1000" cardinality="0..1" filter="(service.imported=*)" />
 
        <!-- 3. Create Thread HelloClientThread which call the IHello service which is retrieved from OSGI services and filled with Spring Dependency Injection-->
	<bean id="helloClient"
		class="org.eclipse.ecf.examples.internal.remoteservices.hello.dm.consumer.HelloClientThread"
		init-method="start" destroy-method="interrupt">
		<property name="hello" ref="helloService" />
	</bean>
 
</beans>

ECF Spring support

With ECF 3.2 you can NOT use this support.

Back to the top