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

Difference between revisions of "Using Spring with ECF Remote Services"

(ECF Spring support)
(ECF Spring support)
Line 351: Line 351:
 
'''NOTE:  This project is currently in incubation, and so is not distributed as part of ECF's upcoming 3.2 release (Feb 19, 2010).  We expect to include this work in our Helios/3.3 release, however.'''
 
'''NOTE:  This project is currently in incubation, and so is not distributed as part of ECF's upcoming 3.2 release (Feb 19, 2010).  We expect to include this work in our Helios/3.3 release, however.'''
  
'''NOTE:  The ECF project is working with the propose [http://www.eclipse.org/proposals/virgo/ Virgo project], to provide integration between ECF' OSGi Remote Services and Virgo.  The work described on this page will very likely be done in concert with the Virgo team.'''
+
'''NOTE:  The ECF project is working with the propose [http://www.eclipse.org/proposals/virgo/ Virgo project], to provide integration between ECF's support for OSGi 4.2 Remote Services and Virgo.  The work described on this page will very likely be done in concert with the Virgo team.'''
  
 
Early/initial documentation of the contents of this project follows:
 
Early/initial documentation of the contents of this project follows:

Revision as of 14:03, 12 February 2010

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  :

  • org.eclipse.ecf.examples.remoteservices.hello.dm.config.log4j : fragment to configure log4j required by Spring Extender bundle..
  • org.eclipse.ecf.examples.remoteservices.hello.dm.consumer : bundle of Consumer Hello sample managed with Spring DM.
  • org.eclipse.ecf.examples.remoteservices.hello.dm.host : bundle of Host Hello sample managed with Spring DM.
  • org.eclipse.ecf.springframework : bundle of ECF Spring support.

This samples works only with ECF 3.2. If you want test with older version of ECF, use the examples stored into ecf_3.0.0 of the zip that you can find into Bug 302113.

How it works?

When you want use Remote Services with ECF on Consumer bundle (aka client side) and Host bundle (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 ECF Spring support :
    <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=*)" />
  • Inject the service to the Thread HelloClientHread and start the Thread :
    <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>

Note you have cardinality="0..1" defined into <osgi:reference. this attribute is very important. Indead by default cardinality="1..1" and it means that the service MUST be retrieved. When you start the consumer bundle the service can be retrieved the first time and if you set not the cardinality to "0..1", the bean HelloClientThread which use this service can be NOT created.

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

ECF provides a very small and simple bundle org.eclipse.ecf.springframework which is a support for Spring to manage:

This bundle is now available in the ECF incubation area. In anonymous CVS, it can be accessed here

protocol: pserver
host: dev.eclipse.org
path: /cvsroot/rt
Eclipse repository: :pserver:anonymous@dev.eclipse.org:/cvsroot/rt
module: org.eclipse.ecf/incubation/bundles/org.eclipse.ecf.springframework

NOTE: This project is currently in incubation, and so is not distributed as part of ECF's upcoming 3.2 release (Feb 19, 2010). We expect to include this work in our Helios/3.3 release, however.

NOTE: The ECF project is working with the propose Virgo project, to provide integration between ECF's support for OSGi 4.2 Remote Services and Virgo. The work described on this page will very likely be done in concert with the Virgo team.

Early/initial documentation of the contents of this project follows:

*ContainerFactoryBean

HostContainerFactoryBean

HostContainerFactoryBean is Spring factory bean to create an ECF IContainer on server side. Here the XML bean declaration to create an ECF IContainer with type "ecf.generic.server"

<bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
  <property name="containerType" value="ecf.generic.server" />
</bean>

This déclaration is the same thing like :

ContainerFactory.getDefault().createContainer("ecf.generic.server");
containerFactory parameter

The ECF IContainerFactory can be retrieved from OSGi services regisrty. With Java, you write this code :

IContainerFactory containerFactory = getContainerFactoryByUsingServiceTracker();
containerFactory.createContainer("ecf.generic.server");

You can manage that with XML Spring declaration like this :

<osgi:reference id="containerFactory"
		interface="org.eclipse.ecf.core.IContainerFactory" timeout="1000" />
 
<bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
  <property name="containerType" value="ecf.generic.server" />
  <property name="containerFactory" ref="containerFactory" />
</bean>
containerManager parameter

You can use too the IContainerManager to create a factory into Java :

IContainerManager containerManager = getContainerManagerByUsingServiceTracker();
IContainerFactory containerFactory = containerManager.getContainerFactory();
containerFactory.createContainer("ecf.generic.server");

You can manage that with XML Spring declaration like this :

<osgi:reference id="containerManager"
		interface="org.eclipse.ecf.core.IContainerManager" timeout="1000" />
 
<bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
  <property name="containerType" value="ecf.generic.server" />
  <property name="containerManager" ref="containerManager" />
</bean>
containerId parameter

ECF ID can be used to set the URL server where the client must be connected. Here the java code used to set the server URL at "ecftcp://localhost:3787/server" :

ID containerID = IDFactory.getDefault().createStringID("ecftcp://localhost:3787/server");
IContainer container = ContainerFactory.getDefault().createContainer("ecf.generic.server", containerID);

You can manage that with XML Spring declaration like this :

<bean id="containerId"
  class="org.eclipse.ecf.springframework.identity.StringIDFactoryBean">
  <property name="stringID" value="ecftcp://localhost:3787/server" />
</bean>
 
<bean class="org.eclipse.ecf.springframework.HostContainerFactoryBean">
  <property name="containerType" value="ecf.generic.server" />
  <property name="containerId" ref="containerId" />
</bean>

ConsumerContainerFactoryBean

ConsumerContainerFactoryBean is Spring factory bean to create an ECF IContainer on client side. Here the XML bean declaration to create an ECF IContainer with type "ecf.generic.client"

<bean class="org.eclipse.ecf.springframework.ConsumerContainerFactoryBean">
  <property name="containerType" value="ecf.generic.client" />
</bean>

You can too set the IContainerFactory or the IContainerManager retrieved by OSGi services registry like HostContainerFactoryBean .

targetId parameter

ECF ID can be used to set the URL server where the client must be connected. Here teh java code used to set the server URL at "ecftcp://localhost:3787/server" :

ID targetID = IDFactory.getDefault().createStringID("ecftcp://localhost:3787/server");
IContainer container = ContainerFactory.getDefault().createContainer("ecf.generic.client");
container.connect(targetID ,null);

You can manage that with XML Spring declaration like this :

<bean id="targetId"
  class="org.eclipse.ecf.springframework.identity.StringIDFactoryBean">
  <property name="stringID" value="ecftcp://localhost:3787/server" />
</bean>
 
<bean class="org.eclipse.ecf.springframework.ConsumerContainerFactoryBean">
  <property name="containerType" value="ecf.generic.client" />
  <property name="targetId" ref="targetId" />
</bean>

*IDFactoryBean

StringIDFactoryBean

StringIDFactoryBean is Spring factory bean to create an ECF String ID. Here the XML bean declaration to create an ECF String ID with value ecftcp://localhost:3787/server" :

<bean id="containerId"
  class="org.eclipse.ecf.springframework.identity.StringIDFactoryBean">
  <property name="stringID" value="ecftcp://localhost:3787/server" />
</bean>

This déclaration is the same thing like :

ID stringID = IDFactory.getDefault().createStringID("ecftcp://localhost:3787/server");
idFactory parameter

The ECF IIDFactory can be retrieved from OSGi services regisrty. With Java, you write this code :

NOT SURE FOR THAT!!!

IIDFactory idFactory = getIDFactoryByUsingServiceTracker();
idFactory.createStringID("ecftcp://localhost:3787/server");

You can manage that with XML Spring declaration like this :

<osgi:reference id="idFactory"
		interface="org.eclipse.ecf.core.IIDFactory" timeout="1000" />
 
<bean id="containerId"
  class="org.eclipse.ecf.springframework.identity.StringIDFactoryBean">
  <property name="stringID" value="ecftcp://localhost:3787/server" />
  <property name="idFactory" ref="idFactory" />
</bean>

LongIDFactoryBean

Exist but NOT tested. TODO: create another *IDFactoryBean.

Example Usage: TODO

ConnectContextFactoryBean

ConnectContextFactoryBean is Spring factory bean to create an ECF IConnectContext.

Example Usage: TODO

Back to the top