[
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