[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
| Re: [ecf-dev] Fun with remote services part 1 | 
Hi Eugen,
Eugen Reiswich wrote:
Hi Scott,
thanks for explaining the issues regarding OSGi and credentials for 
authentication.
1) An explicit connect...i.e. the code that you use above to create 
and connect a container. 2) Implementing and registering your own 
IProxyContainerFinder, so that upon discovery (and container 
creation, and connect), that your credentials can be provided.
Say I will choose solution #1. 
Once I've created an IContainer instance, how would I now tell my 
services that they can be used remotely. I've read that I need to 
register the IContainer instance as a service in the host side OSGi 
service registry?
Yes.  The OSGi remote services spec specifies that a remote service is 
one that is registered with values for the following standard-specified 
service properties:
service.exported.interfaces
service.exported.configs
So, a ds component definition like this is detected by ECF remote 
services implementation as a remote service (because of the presence of 
service.exported.interfaces property):
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
enabled="true" immediate="true" 
name="org.eclipse.ecf.examples.remoteservices.hello.ds.host">
  <implementation 
class="org.eclipse.ecf.examples.internal.remoteservices.hello.ds.host.HelloComponent"/>
  <property name="service.exported.interfaces" type="String" value="*"/>
  <property name="service.exported.configs" type="String" 
value="ecf.generic.server"/>
  <property name="org.eclipse.ecf.containerFactoryArgs" type="String" 
value="ecftcp://localhost:30001/server"/>
  <service>
     <provide 
interface="org.eclipse.ecf.examples.remoteservices.hello.IHello"/>
  </service>
Incidently, the above is from the 
org.eclipse.ecf.examples.remoteservices.hello.ds.host/OSGI-INF/hello.xml 
example. 
What happens is that when this service is registered (by ds), the OSGi 
remote services implementation
(on *host*)
a) detects the presence of service.exported.interfaces and 
service.exported.configs
class impl:  
org.eclipse.ecf.internal.osgi.services.distribution.EventHookImpl
b) in the ECF impl calls the IHostContainerFinder.findHostContainer to 
find a host container(s) that can/should export the given remote service. 
class impl:  
org.eclipse.ecf.osgi.services.distribution.DefaultHostContainerFinder
c) If findHostContainers returns a non-null value (i.e. there are some 
host containers that should export this service) then the ECF 
implementation calls 
remoteServiceContainerAdapter.registerRemoteService(...) for you 
automatically.  It *also* then calls 
IDiscoveryAdvertiser.registerService(...) if there is an 
IDiscoveryAdvertiser present.  Which discovery is used is dependent upon 
which is configured and deployed to be used (e.g. zeroconf, slp, apache 
zookeeper, xml-file-based discovery, etc).
class impl:  
org.eclipse.ecf.internal.osgi.services.distribution.EventHookImpl
The IContainer implementation will then listen for service 
registrations containing e.g. the following  properties and publish my 
service: 
   <property name="service.exported.interfaces" type="String" value="*"/>
   <property name="service.exported.configs" type="String" 
value="ecf.xmpp.smack"/>
   <property name="org.eclipse.ecf.containerFactoryArgs" type="String" 
value="jabber-server.de <http://-server.de>"/>
Is this the way it works?
On the consumer side, the OSGi remote service detection is dependent 
upon discovering the remote service via an IServiceListener.  The ECF 
impl of OSGi remote services registers an IServiceListener with the 
installed/configured IDiscoveryLocator.   When the discovery service (on 
the consumer) detects a service, it calls the 
IServiceListener.serviceDiscovered(...), and this triggers the ECF impl 
of OSGi remote services.  In turn, this calls the 
IProxyContainerFinder.findProxyContainers to find (or create) the 
appropriate/relevant containers...and then call 
containerAdapter.getRemoteServiceReferences(...), create the client's 
proxy, and then register that proxy in the consumers *local* OSGi 
service registry.  Once that's done (registering the proxy), any clients 
that should be notified (e.g. via ServiceTracker or ds) are then 
notified about the existence of the new remote service. 
So under normal circumstances, it should not be necessary for you to 
deal with  the ECF IRemoteServiceContainerAdapter API at all.  On the 
host side, this is done automatically on any/all containers returned 
from IHostContainerFinder.findHostContainers, and on the consumer side 
this is done automatically on any/all containers returned from 
IProxyContainerFinder.findProxyContainers.
If, however, you wish to use the IRemoteServiceContainerAdapter API 
directly (in your own code) then you are completely free to do 
this...and in such a case you don't need the OSGi remote services 
implementation at all.  Rather, you can/could implement the calls (as 
you outline below) to directly create containers, get the 
IRemoteServiceContainerAdapter and make calls on that the 
IRemoteServiceContainerAdapter (e.g. registerRemoteService...on 
host...or getRemoteServiceReferences...on consumer) directly/explicitly 
yourself.  Like I say above...if you do this (call the 
IRemoteServiceContainerAdapter on host and client directly), then you do 
not need the OSGi remote services impl at all.
To summarize, the layring of the apis is as described by this diagram:
http://wiki.eclipse.org/OSGi_4.2_Remote_Services_and_ECF
Now, as for the use of DS...ds is essentially a declarative way to 
register *OSGI* services (host), and reference/bind to OSGI services 
(client).  *With* the ECF OSGi remote services implementation, this 
makes it possible to declaratively register remote services...e.g. 
here's the 
org.eclipse.ecf.examples.remoteservices.host.ds/OSGI-INF/hello.xml:
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
enabled="true" immediate="true" 
name="org.eclipse.ecf.examples.remoteservices.hello.ds.host">
  <implementation 
class="org.eclipse.ecf.examples.internal.remoteservices.hello.ds.host.HelloComponent"/>
  <property name="service.exported.interfaces" type="String" value="*"/>
  <property name="service.exported.configs" type="String" 
value="ecf.generic.server"/>
  <property name="org.eclipse.ecf.containerFactoryArgs" type="String" 
value="ecftcp://localhost:30001/server"/>
  <service>
     <provide 
interface="org.eclipse.ecf.examples.remoteservices.hello.IHello"/>
  </service>
  <reference cardinality="1..1" 
interface="org.eclipse.ecf.core.IContainerFactory" 
name="IContainerFactory" policy="static"/>
</scr:component>
The service properties are then used as described above to a) trigger 
the ECF remote services service registry eventhook; b) Find/create the 
appropriate container (via the IHostContainerFinder), and c) register 
the remote service with both the 
IRemoteServiceContainerAdapter.registerRemoteService and 
IDiscoveryAdvertiser.registerService.
Using DS, on the consumer side is this:
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" 
enabled="true" immediate="true" 
name="org.eclipse.ecf.examples.remoteservices.hello.ds.consumer">
  <implementation 
class="org.eclipse.ecf.examples.internal.remoteservices.hello.ds.consumer.HelloClientComponent"/>
  <reference bind="bindHello" cardinality="0..n" 
interface="org.eclipse.ecf.examples.remoteservices.hello.IHello" 
name="IHello" policy="dynamic"/>
</scr:component>
All this says is that when an implementer of the IHello service is 
*added to the local OSGi registry*, ds will call the 
HelloClientComponent.bindHello(proxy) method and provide the service 
reference to the component code.
As described above, on the consumer, the IHello service instance 
(actually a proxy) will be registered locally (and trigger ds to do the 
binding/call bindHello method) when the following have occurred:
a) The IServiceListener detects a service (using some discovery mechanism)
class impl:  
org.eclipse.ecf.internal.osgi.services.distribution.DiscoveredServiceTrackerImpl
b) The IProxyContainerFinder finds (or creates) a 'compatible' 
local/consumer container instance
class impl:  
org.eclipse.ecf.osgi.services.distribution.DefaultProxyContainerFinder
c) The proxy is created and registered in the consumer's local OSGi 
service registry.
class impl:  
org.eclipse.ecf.internal.osgi.services.distribution.DiscoveredServiceTrackerImpl
Note that b depends upon a (i.e. there has to be a local container that 
is of appropriate config type...either before the discovery or created 
as part of discovery), and c depends upon b (i.e. the container has to 
be able to lookup/find a remote service on the container(s) returned 
from findProxyContainerFinder).
I think the thing to make immediately clear about the use of DS is that 
it is based upon services being registered in the *OSGI* service 
registry.  So on the host side that means that the appropriate standard 
service properties have to be provided (service.exported.interfaces and 
service.exported.configs) for the ECF (or any other) remote services 
impl to 'kick in' and register the remote service (call 
IRemoteServiceContainerAdapter.registerRemoteService and 
IDiscoveryAdvertiser.registerService(...)).  On the consumer side it 
means that the service has to be *discovered* (via some discovery 
protocol or impl...i.e. xml file-based discovery, zookeeper, slp, 
zeroconf, your private impl)...so that the ECF remote services impl can 
find the container(s), lookup and create the reference, and then 
register the proxy in the consumer's local OSGi registry...and thereby 
trigger DS references, ServiceTrackers.  Note that DS doesn't really 
know or care that the service is remote...it's just doing what it's 
designed to do, and in the case of the remote service the actual service 
impl is a proxy rather than a pojo.
The XMPP provider adds some further complexity to this model because (as 
we discussed):
1) There is no way specified in OSGi 4.2 remote services spec to pass in 
credentials information for the establishment of the connection...so 
this has to be done using some ECF mechanism (either directly with the 
container.connect, or via a custom IHost/IProxyContainerFinder.
2) XMPP doesn't have any notion of scope for messaging (essentially a 
flat messaging namespace)...meaning that it's necessary for the host to 
provide  targetIDs for the registration...i.e. 
props.put(Constants.SERVICE_REGISTRATION_TARGETS, targetIDs).  In the 
ECF remote services impl any service properties that are not OSGi 
standard service properties are simply passed through to the call to 
IRemoteServiceContainerAdapter.registerRemoteService(...,props), meaning 
that if you call
Properties props = new Properties();
props.put("service.exported.interfaces","*");  <- This says it's a 
remote service, and will trigger the ECF OSGI 4.2 remote services impl 
(org.eclipse.ecf.osgi.
props.put("service.exported.configs","ecf.xmpps.smack");  <- This tells 
the IHostContainerFinder to find a container with type ecf.xmpps.smack 
to export
props.put(Constants.SERVICE_REGISTRATION_TARGETS,targetIDs);  <- upon 
call to registerRemoteService, this indicates to the ECF XMPP provider 
that the service registration should be sent to the given targets (since 
otherwise the xmpp provider has no context to specify who it should send 
the add registration message to).
// Host register call...which triggers host-side sequence defined above
bundleContext.registerService("fooSvcInterface",new HostImpl(), props);
As for your question about the use of ds and configuration admin to set 
service properties (rather than setting them via DS xml).  I believe the 
answer is 'yes' (i.e. you can use configuration admin to set service 
property values prior to their usage for ds-based service registration), 
but I'm not sure exactly how that is done most easily. 
Though the answer to this question is strictly between configuration 
admin and declarative services and doesn't really involve OSGi remote 
services, or ECF's implementation of remote services.  If you identify a 
simple pattern for doing that (setting service property values prior to 
DS-based service registration) it would be good (I think) to make it 
known to many, as it seems that many wish to do this.  It seems to me, 
though, that if you are setting service property values programmatically 
(rather than declaratively), then it sort of diminishes the value of 
using ds in general, but I may just not be aware of the simplest way to 
set service properties using DS.
Scott
Would the IContainer implementation listen for my custom service 
registrations and check according to the provided properties whether 
to publish a service remotely or not? I've done this so far 
programmatically using the IContainer instance: 
public synchronized void registerRemoteService(String serviceName, 
Object impl,
ID[] targetIDs) {
Dictionary<String, ID[]> props = new Hashtable<String, ID[]>();
props.put(Constants.SERVICE_REGISTRATION_TARGETS, targetIDs);
// register ECF remote service
getRemoteServiceContainerAdapter().registerRemoteService(
new String[] { serviceName }, impl, props);
}
In case this is the way it works: can I provide the properties above 
at runtime using the ConfigAdminService?
On the client side the hello.ds.consumer bundle only describes that 
it requires an IHello service. But I was not able to find out how 
the service is configured: server url, username, password.
The server url and username (for xmpp) would be conveyed in what's 
called the 'endpointID' in discovery...e.g. for the XMPP provider the 
endpoint id would be an XMPP account:  'scottslewis@xxxxxxxxx 
<mailto:%27scottslewis@xxxxxxxxx>'
My question focused on how do consumer retrieve a remote service proxy 
 using DS. So far I wrote a util class that can retrieve remote 
services for a specific XMPP ID in this way:
public synchronized <T> List<T> getRemoteService(Class<T> service, 
ID[] filterIDs,
String filter) throws ECFException, InvalidSyntaxException {
List<T> remoteServices = new ArrayList<T>();
IRemoteServiceContainerAdapter remoteServiceContainerAdapter = 
getRemoteServiceContainerAdapter();
/* 1. get available services */
IRemoteServiceReference[] refs = remoteServiceContainerAdapter
.getRemoteServiceReferences(filterIDs, service.getName(),
filter);
/* 2. get the proxies for service references */
for (int serviceNumber = 0; serviceNumber < refs.length; 
serviceNumber++) {
IRemoteService remoteService = remoteServiceContainerAdapter
.getRemoteService(refs[serviceNumber]);
T castedService = service.cast(remoteService.getProxy());
assert castedService != null : "castedService != null";
remoteServices.add(castedService);
}
return remoteServices;
}
What I did not understand is how does DS retrieve remote service 
proxies? As OSGi has no standard way to provide credentials I guess 
this won't work for me using XMPP. But in case the IContainer solution 
I've mentioned above works, could it be a solution to:
1. create an IContainer instance with credentials
2. register the instance as an OSGi service in a client side service 
registry
2. create DS components with the properties I've mentioned above?
I'm sorry for all these questions but it is still difficult for me to 
understand how ECF works with DS  and whether I can use it for my work.
Regards,
Eugen
Am Jun 14, 2010 um 15:54  schrieb Scott Lewis:
Hi Eugen,
Eugen Reiswich wrote:
Hi Scott, hi Wim,
I've also checked out the hello.ds examples and tried to understand 
how this example works. As I understand the host has to provide 
specific server properties within the service component. And as far 
as I understand these properties will trigger ECF to make a service 
remotely available (I didn't really get what exactly happens here). 
 In my XMPP case I've changed the example to:
<property name="service.exported.interfaces" type="String" value="*"/>
<property name="service.exported.configs" type="String" 
value="ecf.xmpp.smack"/>
<property name="org.eclipse.ecf.containerFactoryArgs" type="String" 
value="jabber-server.de <http://jabber-server.de> <http://-server.de>"/>
If I got you right Scott I do no longer need to create an instance 
of IContainer like I did before:
IContainer container = 
ContainerFactory.getDefault().createContainer(protocol);
XMPPID xmppid = new XMPPID(container.getConnectNamespace(), userName 
+ "@" + server);
IConnectContext connectContext = 
ConnectContextFactory.createUsernamePasswordConnectContext(userName, 
password);
container.connect(xmppid, connectContext);
But how do I now  provide with DS username and password in order to 
be able to connect to a server?
Because OSGi 4.2 provides no standard way to provide credentials for 
connect authentication, and the XMPP provider and service obviously 
require such credentials, it's necessary to use some mechanism not 
exposed by OSGi service properties.  There are a couple of ways 
provided by ECF's implementation:
1) An explicit connect...i.e. the code that you use above to create 
and connect a container. 2) Implementing and registering your own 
IProxyContainerFinder, so that upon discovery (and container 
creation, and connect), that your credentials can be provided.
I would say, that given that you already have the code for 1, that 1 
seems more appropriate for you...since you are already doing it.
Nevertheless, I'll describe 2 a little bit here.  The easiest way to 
customize the proxy container find/creation/connect is to extend 
 org.eclipse.ecf.osgi.services.distribution.DefaultProxyContainerFinder 
and override this method (actually declared in 
AbstractProxyContainerFinder):
  protected IConnectContext getConnectContext(IContainer container,
          ID connectTargetID) {
      return null;
  }
Then, to have your proxy container finder used rather than the 
default one, simply register your instance as an OSGi service using 
the whiteboard pattern:
context.registerService(IProxyContainerFinder.class.getName(), new 
MyProxyContainerFinder(), null);
(you can do the registration with ds if you prefer)
By overriding this method (getConnectContext) you can provide the 
appropriate credentials for the connect that happens within the proxy 
container finder during the OSGi 4.2 consumer-side discovery.  So, 
for example
public class MyProxyContainerFinder extends DefaultProxyContainerFinder {
private String username;
private String password;
public MyProxyContainerFinder(String username, String password) {
 this.username = username;
 this.password = password;
}
protected IConnectContext getConnectContext(IContainer container, ID 
connectTargetID) {
 if (container and connectTargetID are appropriate types/values)
    return 
ConnectContextFactory.createUsernamePasswordConnectContext(this.username, 
this.password);
}
}
There is a little more documentation on the host and proxy container 
finder...and customizing the ECF impl of OSGi remote services:
http://wiki.eclipse.org/Customizing_and_Extending_ECF_Remote_Services
Note that depending upon how you are doing host registration and 
discovery, you will also have to specify (in service properties) the 
connectTargetID...which is used by the 
DefaultProxyContainerFinder.findProxyContainers method (which 
ultimately calls the getConnectContext when creating and then 
connecting a container).
So like I mentioned before, it seems to me that method 1 may be the 
easiest/quickest route for you (rather than method 2), but I wanted 
to use the opportunity to explain method 2 a little, since it 
provides quite a lot of flexibility for use cases that don't fit into 
the default OSGi 4.2 rs mold...like the use of XMPP.
Incidently...another thing to mention...it's also possible to 
override DefaultProxyContainerFinder.findProxyContainers and change 
the entire implementation of how an ECF container is 
selected/created/connected during OSGi 4.2 discovery.  For some use 
cases, this might be desired.
On the client side the hello.ds.consumer bundle only describes that 
it requires an IHello service. But I was not able to find out how 
the service is configured: server url, username, password.
The server url and username (for xmpp) would be conveyed in what's 
called the 'endpointID' in discovery...e.g. for the XMPP provider the 
endpoint id would be an XMPP account:  'scottslewis@xxxxxxxxx'
Scott
_______________________________________________
ecf-dev mailing list
ecf-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/ecf-dev
------------------------------------------------------------------------
_______________________________________________
ecf-dev mailing list
ecf-dev@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/ecf-dev