blob: 5a4512f37223c8cffae1ab9fd87c5a7f2708e822 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Jan S. Rellermeyer, 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:
* Jan S. Rellermeyer - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.internal.provider.r_osgi;
import ch.ethz.iks.r_osgi.*;
import ch.ethz.iks.r_osgi.channels.ChannelEndpointManager;
import java.io.IOException;
import java.util.*;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ecf.core.*;
import org.eclipse.ecf.core.events.*;
import org.eclipse.ecf.core.identity.*;
import org.eclipse.ecf.core.security.IConnectContext;
import org.eclipse.ecf.core.util.*;
import org.eclipse.ecf.provider.r_osgi.identity.R_OSGiID;
import org.eclipse.ecf.provider.r_osgi.identity.R_OSGiNamespace;
import org.eclipse.ecf.remoteservice.*;
import org.eclipse.ecf.remoteservice.events.IRemoteServiceRegisteredEvent;
import org.eclipse.ecf.remoteservice.events.IRemoteServiceUnregisteredEvent;
import org.osgi.framework.*;
import org.osgi.framework.Constants;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* The R-OSGi remote service container adapter. Implements the adapter and the
* container interface.
*
* @author Jan S. Rellermeyer, ETH Zurich
*/
final class R_OSGiRemoteServiceContainer implements IRemoteServiceContainerAdapter, IContainer, RemoteServiceListener {
// the bundle context.
private BundleContext context;
// the R-OSGi remote service instance.
private RemoteOSGiService remoteService;
// the EndpointManager for the endpoint.
private ChannelEndpointManager endpointMgr;
// the list of subscribed container listeners.
private final List containerListeners = new ArrayList(0);
// the ID of this container.
R_OSGiID containerID;
// the ID of the remote peer to which the container is connected to, or
// null, if not yet connected.
private R_OSGiID connectedID;
// tracks the lifecycle of remote services.
private ServiceTracker remoteServicesTracker;
// service reference -> service registration
private Map remoteServicesRegs = new HashMap(0);
// the map of remote service listeners. Maps the listener to the service
// registration of the internal R-OSGi remote service listener service.
private Map remoteServiceListeners = new HashMap(0);
/**
* @throws IDCreateException
*/
public R_OSGiRemoteServiceContainer() throws IDCreateException {
context = Activator.getDefault().getContext();
remoteService = Activator.getDefault().getRemoteOSGiService();
if (remoteService == null) {
throw new IDCreateException("R-OSGi not running. Cannot create local container ID"); //$NON-NLS-1$
}
}
public R_OSGiRemoteServiceContainer(final ID containerID) throws IDCreateException {
this();
if (containerID instanceof StringID) {
this.containerID = new R_OSGiID(((StringID) containerID).getName());
} else if (containerID instanceof R_OSGiID) {
this.containerID = (R_OSGiID) containerID;
} else {
throw new IDCreateException("Incompatible ID " + containerID); //$NON-NLS-1$
}
startRegTracker();
}
private void startRegTracker() {
try {
final String filter = "(" + org.eclipse.ecf.remoteservice.Constants.REMOTE_SERVICE_CONTAINER_ID + "=" + containerID + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
remoteServicesTracker = new ServiceTracker(context, context.createFilter(filter), new ServiceTrackerCustomizer() {
public Object addingService(ServiceReference reference) {
return reference;
}
public void modifiedService(ServiceReference reference, Object service) {
// service got modified
return;
}
public void removedService(ServiceReference reference, Object service) {
// service got removed
}
});
remoteServicesTracker.open();
} catch (InvalidSyntaxException e) {
e.printStackTrace();
}
}
/**
* add a remote service listener. This method accepts an ECF remote service
* listener and registers a R-OSGi listener service as an adapter.
*
* @param listener
* the ECF remote service listener.
*
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#addRemoteServiceListener(org.eclipse.ecf.remoteservice.IRemoteServiceListener)
*/
public void addRemoteServiceListener(final IRemoteServiceListener listener) {
Assert.isNotNull(listener);
final RemoteServiceListener l = new RemoteServiceListener() {
public void remoteServiceEvent(final RemoteServiceEvent event) {
switch (event.getType()) {
case RemoteServiceEvent.REGISTERED :
listener.handleServiceEvent(new IRemoteServiceRegisteredEvent() {
public String[] getClazzes() {
return (String[]) event.getRemoteReference().getProperty(Constants.OBJECTCLASS);
}
public ID getContainerID() {
return containerID;
}
public IRemoteServiceReference getReference() {
return new RemoteServiceReferenceImpl(containerID, event.getRemoteReference());
}
public String toString() {
return "RemoteServiceRegisteredEvent(" + containerID + "," + getReference(); //$NON-NLS-1$ //$NON-NLS-2$
}
});
return;
case RemoteServiceEvent.UNREGISTERING :
listener.handleServiceEvent(new IRemoteServiceUnregisteredEvent() {
public String[] getClazzes() {
return (String[]) event.getRemoteReference().getProperty(Constants.OBJECTCLASS);
}
public ID getContainerID() {
return containerID;
}
public IRemoteServiceReference getReference() {
return new RemoteServiceReferenceImpl(containerID, event.getRemoteReference());
}
public String toString() {
return "RemoteServiceUnregisteredEvent(" + containerID + "," + getReference(); //$NON-NLS-1$ //$NON-NLS-2$
}
});
return;
}
}
};
// register the listener as a service (whiteboard pattern)
final ServiceRegistration reg = context.registerService(RemoteServiceListener.class.getName(), l, null);
// keep track of the listener so that it can be removed when requested.
remoteServiceListeners.put(listener, reg);
}
/**
* get a remote service by its remote service reference.
*
* @param reference
* the remote service reference.
* @return the IRemoteService object, encapsulating the service proxy and
* additional methods for asynchronous and other access methods.
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#getRemoteService(org.eclipse.ecf.remoteservice.IRemoteServiceReference)
*/
public IRemoteService getRemoteService(final IRemoteServiceReference reference) {
Assert.isNotNull(reference);
final RemoteServiceReferenceImpl impl = (RemoteServiceReferenceImpl) reference;
return new RemoteServiceImpl(impl, remoteService.getRemoteService(impl.getR_OSGiServiceReference()));
}
/**
* get remote service references.
*
* @param idFilter
* a filter that limits the results to services registered by one
* of the IDs.
* @param clazz
* the interface name of the remote service.
* @param filter
* LDAP filter string that is matched against the service
* properties.
* @return the matching remote service references.
* @throws InvalidSyntaxException
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#getRemoteServiceReferences(org.eclipse.ecf.core.identity.ID[],
* java.lang.String, java.lang.String)
*/
public IRemoteServiceReference[] getRemoteServiceReferences(final ID[] idFilter, final String clazz, final String filter) throws InvalidSyntaxException {
Assert.isNotNull(clazz);
final RemoteServiceReference[] refs = remoteService.getRemoteServiceReferences(connectedID.getURI(), clazz, filter == null ? null : createRemoteFilter(filter));
if (refs == null) {
return null;
}
final IRemoteServiceReference[] result = new IRemoteServiceReference[refs.length];
for (int i = 0; i < refs.length; i++) {
result[i] = new RemoteServiceReferenceImpl(containerID, refs[i]);
}
return result;
}
/**
* register a service object as a remote service.
*
* @param clazzes
* the names of the service interfaces under which the service
* will be registered.
* @param service
* the service object.
* @param properties
* the service properties.
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#registerRemoteService(java.lang.String[],
* java.lang.Object, java.util.Dictionary)
*/
public IRemoteServiceRegistration registerRemoteService(final String[] clazzes, final Object service, final Dictionary properties) {
if (containerID == null) {
throw new IllegalStateException("Container is not connected"); //$NON-NLS-1$
}
final Dictionary props = properties == null ? new Hashtable() : clone(properties);
// add the hint property for R-OSGi that this service is intended to be
// accessed remotely.
props.put(RemoteOSGiService.R_OSGi_REGISTRATION, Boolean.TRUE);
props.put(org.eclipse.ecf.remoteservice.Constants.REMOTE_SERVICE_CONTAINER_ID, containerID);
// register the service with the local framework
final ServiceRegistration reg = context.registerService(clazzes, service, props);
remoteServicesRegs.put(reg.getReference(), reg);
return new RemoteServiceRegistrationImpl(containerID, reg);
}
/**
* remove a registered remote service listener.
*
* @param listener
* the remote service listener.
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#removeRemoteServiceListener(org.eclipse.ecf.remoteservice.IRemoteServiceListener)
*/
public void removeRemoteServiceListener(final IRemoteServiceListener listener) {
final ServiceRegistration reg = (ServiceRegistration) remoteServiceListeners.remove(listener);
if (reg == null) {
return;
}
reg.unregister();
}
/**
*
* @see org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter#ungetRemoteService(org.eclipse.ecf.remoteservice.IRemoteServiceReference)
*/
public boolean ungetRemoteService(IRemoteServiceReference reference) {
remoteService.ungetRemoteService(((RemoteServiceReferenceImpl) reference).getR_OSGiServiceReference());
return true;
}
/**
* returns an adapter for a given class. In this particular case, only the
* IRemoteServiceContainerAdapter interface and the IContainer interface are
* supported.
*
* @param adapter
* the class to adapt to.
* @return the adapter or null if the adaptation is not supported.
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(final Class adapter) {
if (adapter.equals(IRemoteServiceContainerAdapter.class)) {
return this;
} else if (adapter.equals(IContainer.class)) {
return this;
}
return null;
}
// container part
/**
* add a container listener.
*
* @param listener
* the container listener.
*
* @see org.eclipse.ecf.core.IContainer#addListener(org.eclipse.ecf.core.IContainerListener)
*/
public void addListener(final IContainerListener listener) {
containerListeners.add(listener);
}
/**
* connect the container to a remote container instance.
*
* @param targetID
* the target ID to connect to.
* @param connectContext
* the connection context.
* @throws ContainerConnectException
* if the connecting fails.
* @see org.eclipse.ecf.core.IContainer#connect(org.eclipse.ecf.core.identity.ID,
* org.eclipse.ecf.core.security.IConnectContext)
*/
public void connect(final ID targetID, final IConnectContext connectContext) throws ContainerConnectException {
Assert.isNotNull(targetID);
//Assert.isNotNull(connectContext);
if (containerID != null) {
throw new ContainerConnectException("Container is already connected to " + containerID); //$NON-NLS-1$
}
final R_OSGiID target;
try {
if (targetID instanceof StringID) {
target = new R_OSGiID(((StringID) targetID).getName());
} else if (targetID instanceof R_OSGiID) {
target = (R_OSGiID) targetID;
} else {
throw new ContainerConnectException("Incompatible target id " + targetID); //$NON-NLS-1$
}
fireListeners(new ContainerConnectingEvent(containerID, connectedID));
final RemoteServiceReference[] refs = remoteService.connect(target.getURI());
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
checkImport(refs[i]);
}
}
connectedID = target;
endpointMgr = remoteService.getEndpointManager(target.getURI());
containerID = (R_OSGiID) IDFactory.getDefault().createID(R_OSGiNamespace.NAME, endpointMgr.getLocalAddress().toString());
startRegTracker();
} catch (IOException ioe) {
throw new ContainerConnectException(ioe);
} catch (IDCreateException e) {
throw new ContainerConnectException(e);
}
fireListeners(new ContainerConnectedEvent(containerID, connectedID));
}
/**
* disconnect from the remote container.
*
* @see org.eclipse.ecf.core.IContainer#disconnect()
*/
public void disconnect() {
if (connectedID != null) {
fireListeners(new ContainerDisconnectingEvent(containerID, connectedID));
remoteService.disconnect(connectedID.getURI());
connectedID = null;
fireListeners(new ContainerDisconnectedEvent(containerID, connectedID));
}
}
/**
* dispose the container.
*
* @see org.eclipse.ecf.core.IContainer#dispose()
*/
public void dispose() {
remoteService = null;
// unregister remote services
if (remoteServicesTracker != null) {
final ServiceReference[] refs = remoteServicesTracker.getServiceReferences();
if (refs != null) {
for (int i = 0; i < refs.length; i++) {
final ServiceRegistration reg = (ServiceRegistration) remoteServicesRegs.get(refs[i]);
if (reg != null) {
reg.unregister();
}
}
}
remoteServicesTracker.close();
remoteServicesTracker = null;
}
// unregister remote listeners
final ServiceRegistration[] lstn = (ServiceRegistration[]) remoteServiceListeners.values().toArray(new ServiceRegistration[remoteServiceListeners.size()]);
for (int i = 0; i < lstn.length; i++) {
try {
lstn[i].unregister();
} catch (Throwable t) {
// ignore and continue
}
}
if (connectedID != null) {
disconnect();
}
fireListeners(new ContainerDisposeEvent(containerID));
containerListeners.clear();
}
/**
* get the connect namespace.
*
* @return the connect namespace.
* @see org.eclipse.ecf.core.IContainer#getConnectNamespace()
*/
public Namespace getConnectNamespace() {
return R_OSGiNamespace.getDefault();
}
/**
* get the ID to which the container is connected to. Can be
* <code>null</code> if the container is not yet connected.
*
* @return the ID or null.
* @see org.eclipse.ecf.core.IContainer#getConnectedID()
*/
public ID getConnectedID() {
return connectedID;
}
/**
* remove a registered container listener.
*
* @param listener
* the container listener.
* @see org.eclipse.ecf.core.IContainer#removeListener(org.eclipse.ecf.core.IContainerListener)
*/
public void removeListener(final IContainerListener listener) {
containerListeners.remove(listener);
}
/**
* get the ID of this container instance.
*
* @return the ID of this container.
* @see org.eclipse.ecf.core.identity.IIdentifiable#getID()
*/
public ID getID() {
return containerID;
}
/**
* fire the listeners.
*
* @param event
* the event.
*/
private void fireListeners(final IContainerEvent event) {
final IContainerListener[] listeners = (IContainerListener[]) containerListeners.toArray(new IContainerListener[containerListeners.size()]);
new Thread() {
public void run() {
for (int i = 0; i < listeners.length; i++) {
listeners[i].handleEvent(event);
}
}
}.start();
}
/**
* get the events from R-OSGi received through a RemoteServiceListener
* instance.
*
* @see ch.ethz.iks.r_osgi.RemoteServiceListener#remoteServiceEvent(ch.ethz.iks.r_osgi.RemoteServiceEvent)
*/
public void remoteServiceEvent(final RemoteServiceEvent event) {
if (event.getType() == RemoteServiceEvent.REGISTERED) {
checkImport(event.getRemoteReference());
}
}
/**
* check if the remote service should be automatically imported by this
* container.
*
* @param ref
* the remote service reference to check.
*/
private void checkImport(final RemoteServiceReference ref) {
final Object target = ref.getProperty(org.eclipse.ecf.remoteservice.Constants.SERVICE_REGISTRATION_TARGETS);
if (target instanceof ID && ((ID) target).equals(containerID)) {
remoteService.getRemoteService(ref);
} else if (target instanceof ID[]) {
final ID[] targets = (ID[]) target;
for (int i = 0; i < targets.length; i++) {
if (targets[i].equals(containerID)) {
remoteService.getRemoteService(ref);
}
}
}
}
/**
* Clone a dictionary instance to avoid modification by the caller of the
* registration method.
*
* @param props
* the dictionary instance.
* @return a clone.
*/
private Hashtable clone(final Dictionary props) {
final Hashtable clone = new Hashtable();
for (Enumeration e = props.keys(); e.hasMoreElements();) {
final Object key = e.nextElement();
clone.put(key, props.get(key));
}
return clone;
}
public IFuture asyncGetRemoteServiceReferences(final ID[] idFilter, final String clazz, final String filter) {
IExecutor executor = new IExecutor() {
public void execute(Runnable runnable) {
new Thread(runnable, "asyncGetRemoteServiceReferences").start(); //$NON-NLS-1$
}
};
SingleOperationFuture future = new SingleOperationFuture();
executor.execute(future.setter(new IProgressRunnable() {
public Object run(IProgressMonitor monitor) throws Throwable {
return getRemoteServiceReferences(idFilter, clazz, filter);
}
}));
// Create and start thread for actually calling getRemoteServiceReferences
return future;
}
public Namespace getRemoteServiceNamespace() {
return getConnectNamespace();
}
public IRemoteFilter createRemoteFilter(String filter) throws InvalidSyntaxException {
return new RemoteFilterImpl(context, filter);
}
}