blob: 2b6d0d7891375e1a0ef16d8641fb0a7829ec09de [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2011 Composent, Inc. 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:
* Composent, Inc. - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.osgi.services.remoteserviceadmin;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.ecf.core.ContainerConnectException;
import org.eclipse.ecf.core.ContainerCreateException;
import org.eclipse.ecf.core.ContainerTypeDescription;
import org.eclipse.ecf.core.IContainer;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.identity.StringID;
import org.eclipse.ecf.core.util.ECFException;
import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.Activator;
import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.DebugOptions;
import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.LogUtility;
import org.eclipse.ecf.internal.osgi.services.remoteserviceadmin.PropertiesUtil;
import org.eclipse.ecf.remoteservice.IExtendedRemoteServiceRegistration;
import org.eclipse.ecf.remoteservice.IOSGiRemoteServiceContainerAdapter;
import org.eclipse.ecf.remoteservice.IRSAConsumerContainerAdapter;
import org.eclipse.ecf.remoteservice.IRemoteService;
import org.eclipse.ecf.remoteservice.IRemoteServiceContainer;
import org.eclipse.ecf.remoteservice.IRemoteServiceContainerAdapter;
import org.eclipse.ecf.remoteservice.IRemoteServiceID;
import org.eclipse.ecf.remoteservice.IRemoteServiceListener;
import org.eclipse.ecf.remoteservice.IRemoteServiceReference;
import org.eclipse.ecf.remoteservice.IRemoteServiceRegistration;
import org.eclipse.ecf.remoteservice.events.IRemoteServiceEvent;
import org.eclipse.ecf.remoteservice.events.IRemoteServiceUnregisteredEvent;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceException;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
import org.osgi.framework.hooks.service.EventListenerHook;
import org.osgi.framework.hooks.service.ListenerHook.ListenerInfo;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.remoteserviceadmin.EndpointPermission;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
import org.osgi.util.tracker.ServiceTracker;
/**
* ECF implementation of <a
* href="http://www.osgi.org/download/r4v42/r4.enterprise.pdf">OSGI 4.2 Remote
* Service Admin service</a>. This service can be used by topology managers to
* to export and/or import remote services with any <a
* href="http://wiki.eclipse.org/ECF_Connection_Creation_and_Management">ECF
* container</a> that implements the <a
* href="http://wiki.eclipse.org/ECF/API_Docs#Remote_Services_API">ECF remote
* service API</a>.
*/
public class RemoteServiceAdmin implements
org.osgi.service.remoteserviceadmin.RemoteServiceAdmin {
public static final String SERVICE_PROP = "org.eclipse.ecf.rsa"; //$NON-NLS-1$
private static final boolean disableServiceVersionChecking = new Boolean(
System.getProperty(
"org.eclipse.ecf.osgi.services.remoteserviceadmin.disableServiceVersionChecking", //$NON-NLS-1$
"false")).booleanValue(); //$NON-NLS-1$
private Bundle clientBundle;
private boolean hostAutoCreateContainer = new Boolean(
System.getProperty(
"org.eclipse.ecf.osgi.services.remoteserviceadmin.hostAutoCreateContainer", //$NON-NLS-1$
"true")).booleanValue(); //$NON-NLS-1$
private String[] hostDefaultConfigTypes = new String[] { System
.getProperty(
"org.eclipse.ecf.osgi.services.remoteserviceadmin.hostDefaultConfigType", //$NON-NLS-1$
"ecf.generic.server") }; //$NON-NLS-1$
private boolean consumerAutoCreateContainer = new Boolean(
System.getProperty(
"org.eclipse.ecf.osgi.services.remoteserviceadmin.consumerAutoCreateContainer", //$NON-NLS-1$
"true")).booleanValue(); //$NON-NLS-1$
private Object eventAdminTrackerLock = new Object();
private ServiceTracker eventAdminTracker;
private Object remoteServiceAdminListenerTrackerLock = new Object();
private ServiceTracker remoteServiceAdminListenerTracker;
private HostContainerSelector defaultHostContainerSelector;
private ServiceRegistration defaultHostContainerSelectorRegistration;
private ConsumerContainerSelector defaultConsumerContainerSelector;
private ServiceRegistration defaultConsumerContainerSelectorRegistration;
private Collection<org.osgi.service.remoteserviceadmin.ExportRegistration> exportedRegistrations;
private Collection<org.osgi.service.remoteserviceadmin.ImportRegistration> importedRegistrations;
private Collection<ExportRegistration> localExportedRegistrations = new ArrayList<ExportRegistration>();
private Collection<ImportRegistration> localImportedRegistrations = new ArrayList<ImportRegistration>();
private ServiceRegistration eventListenerHookRegistration;
/**
* @since 4.2
*/
public List<ExportRegistration> getExportedRegistrations() {
synchronized (exportedRegistrations) {
return new ArrayList(exportedRegistrations);
}
}
/**
* @since 4.2
*/
public List<ImportRegistration> getImportedRegistrations() {
synchronized (importedRegistrations) {
return new ArrayList(importedRegistrations);
}
}
public RemoteServiceAdmin(Bundle clientBundle) {
this(
clientBundle,
new ArrayList<org.osgi.service.remoteserviceadmin.ExportRegistration>(),
new ArrayList<org.osgi.service.remoteserviceadmin.ImportRegistration>());
}
/**
* @since 4.1
*/
public RemoteServiceAdmin(Bundle clientBundle, Collection<org.osgi.service.remoteserviceadmin.ExportRegistration> exportedRegistrations, Collection<org.osgi.service.remoteserviceadmin.ImportRegistration> importedRegistrations) {
this.clientBundle = clientBundle;
Assert.isNotNull(this.clientBundle);
this.exportedRegistrations = exportedRegistrations;
this.importedRegistrations = importedRegistrations;
// Only setup defaults if it hasn't already been done by some other
// Remote Service Admin instance
Properties props = new Properties();
props.put(org.osgi.framework.Constants.SERVICE_RANKING, new Integer(
Integer.MIN_VALUE));
// host container selector
ServiceReference[] hostContainerSelectorRefs = null;
BundleContext rsaBundleContext = getRSABundleContext();
try {
hostContainerSelectorRefs = rsaBundleContext.getServiceReferences(
IHostContainerSelector.class.getName(), null);
} catch (InvalidSyntaxException e) {
// will not happen
}
// register a default only if no others already exist
if (hostContainerSelectorRefs == null
|| hostContainerSelectorRefs.length == 0) {
defaultHostContainerSelector = new HostContainerSelector(
hostDefaultConfigTypes, hostAutoCreateContainer);
defaultHostContainerSelectorRegistration = rsaBundleContext
.registerService(IHostContainerSelector.class.getName(),
defaultHostContainerSelector, (Dictionary) props);
}
// consumer container selector
ServiceReference[] consumerContainerSelectorRefs = null;
try {
consumerContainerSelectorRefs = rsaBundleContext
.getServiceReferences(
IConsumerContainerSelector.class.getName(), null);
} catch (InvalidSyntaxException e) {
// will not happen
}
// register a default only if no others already exist
if (consumerContainerSelectorRefs == null
|| consumerContainerSelectorRefs.length == 0) {
defaultConsumerContainerSelector = new ConsumerContainerSelector(
consumerAutoCreateContainer);
defaultConsumerContainerSelectorRegistration = rsaBundleContext
.registerService(
IConsumerContainerSelector.class.getName(),
defaultConsumerContainerSelector,
(Dictionary) props);
}
eventListenerHookRegistration = rsaBundleContext.registerService(
EventListenerHook.class.getName(), new RSAEventListenerHook(),
null);
}
private void handleServiceUnregistering(ServiceReference serviceReference) {
List<ExportRegistration> ers = getExportedRegistrations();
for (ExportRegistration exportedRegistration : ers) {
if (exportedRegistration.match(serviceReference)) {
trace("handleServiceUnregistering", "closing exportRegistration for serviceReference=" //$NON-NLS-1$ //$NON-NLS-2$
+ serviceReference);
exportedRegistration.close();
}
}
}
class RSAEventListenerHook implements EventListenerHook {
public void event(ServiceEvent event,
Map<BundleContext, Collection<ListenerInfo>> listeners) {
switch (event.getType()) {
case ServiceEvent.UNREGISTERING:
handleServiceUnregistering(event.getServiceReference());
break;
default:
break;
}
}
}
private boolean validExportedInterfaces(ServiceReference serviceReference,
String[] exportedInterfaces) {
if (exportedInterfaces == null || exportedInterfaces.length == 0)
return false;
List<String> objectClassList = Arrays
.asList((String[]) serviceReference
.getProperty(org.osgi.framework.Constants.OBJECTCLASS));
for (int i = 0; i < exportedInterfaces.length; i++)
if (!objectClassList.contains(exportedInterfaces[i]))
return false;
return true;
}
// RemoteServiceAdmin service interface impl methods
public Collection<org.osgi.service.remoteserviceadmin.ExportRegistration> exportService(
final ServiceReference<?> serviceReference, Map<String, ?> op) {
trace("exportService", "serviceReference=" + serviceReference //$NON-NLS-1$ //$NON-NLS-2$
+ ",properties=" + op); //$NON-NLS-1$
final Map<String, ?> overridingProperties = PropertiesUtil
.mergeProperties(serviceReference,
op == null ? Collections.EMPTY_MAP : op);
// get exported interfaces
final String[] exportedInterfaces = PropertiesUtil
.getExportedInterfaces(serviceReference, overridingProperties);
if (exportedInterfaces == null)
throw new IllegalArgumentException(
org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_INTERFACES
+ " not set"); //$NON-NLS-1$
// verifyExportedInterfaces
if (!validExportedInterfaces(serviceReference, exportedInterfaces))
return Collections.EMPTY_LIST;
// Get optional exported configs
String[] ecs = PropertiesUtil
.getStringArrayFromPropertyValue(overridingProperties
.get(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS));
if (ecs == null) {
ecs = PropertiesUtil
.getStringArrayFromPropertyValue(serviceReference
.getProperty(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS));
}
final String[] exportedConfigs = ecs;
// Get all intents (service.intents, service.exported.intents,
// service.exported.intents.extra)
final String[] serviceIntents = PropertiesUtil.getServiceIntents(
serviceReference, overridingProperties);
// Create result registrations. This collection will be returned
Collection<ExportRegistration> resultRegistrations = new ArrayList<ExportRegistration>();
// check for previously exported registration for the serviceReference
synchronized (exportedRegistrations) {
ExportEndpoint exportEndpoint = findExistingExportEndpoint(
serviceReference, null);
// If found then create a second ExportRegistration from endpoint
if (exportEndpoint != null) {
trace("exportService", "serviceReference=" + serviceReference + " export endpoint already exists=" + exportEndpoint + ". Returning new ExportRegistration for existing endpoint"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
ExportRegistration reg = new ExportRegistration(exportEndpoint);
addExportRegistration(reg);
resultRegistrations.add(reg);
}
}
// If the serviceReference hasn't already been exported before (above)
if (resultRegistrations.size() == 0) {
// Get a host container selector
final IHostContainerSelector hostContainerSelector = getHostContainerSelector();
// and use it to select ECF remote service containers that match given exported
// interfaces, configs, and intents
IRemoteServiceContainer[] rsContainers = null;
try {
rsContainers = AccessController
.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws SelectContainerException {
return hostContainerSelector
.selectHostContainers(
serviceReference,
(Map<String, Object>) overridingProperties,
exportedInterfaces,
exportedConfigs, serviceIntents);
}
});
} catch (PrivilegedActionException e) {
Exception except = e.getException();
// see discussion on osgi bug https://www.osgi.org/members/bugzilla/show_bug.cgi?id=2591
String errorMessage = "Failed to select host container"; //$NON-NLS-1$
if (except instanceof SelectContainerException) {
SelectContainerException sce = (SelectContainerException) except;
Throwable sceCause = sce.getCause();
if (sceCause instanceof ContainerCreateException) {
// Some dummy props need to be set to allow the creation of a dummy export registration
Map<String,Object> props = new HashMap<String,Object>(overridingProperties);
props.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID, "0"); //$NON-NLS-1$
props.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS, "import.error.config"); //$NON-NLS-1$
props.put(RemoteConstants.ENDPOINT_ID,"export.error.id"); //$NON-NLS-1$
props.put(RemoteConstants.ENDPOINT_CONTAINER_ID_NAMESPACE, StringID.class.getName());
ExportRegistration errorRegistration = new RemoteServiceAdmin.ExportRegistration(sceCause,
new EndpointDescription(serviceReference,props));
addExportRegistration(errorRegistration);
resultRegistrations.add(errorRegistration);
} else
throw new IllegalArgumentException(errorMessage, except);
} else
throw new IllegalArgumentException(errorMessage, except);
}
// If no registration exist (no errorRegistration added above)
if (resultRegistrations.size() == 0) {
// If no containers found above, log warning and return
if (rsContainers == null || rsContainers.length == 0) {
String errorMessage = "No containers found for serviceReference=" //$NON-NLS-1$
+ serviceReference
+ " properties=" + overridingProperties + ". Remote service NOT EXPORTED"; //$NON-NLS-1$//$NON-NLS-2$
logWarning("exportService", errorMessage); //$NON-NLS-1$
return Collections.EMPTY_LIST;
}
// actually do the export
synchronized (exportedRegistrations) {
// For all selected containers
for (int i = 0; i < rsContainers.length; i++) {
Map endpointDescriptionProperties = createExportEndpointDescriptionProperties(
serviceReference,
(Map<String, Object>) overridingProperties,
exportedInterfaces, serviceIntents,
rsContainers[i]);
// otherwise, actually export the service to create
// a new ExportEndpoint and use it to create a new
// ExportRegistration
EndpointDescription endpointDescription = new EndpointDescription(endpointDescriptionProperties);
checkEndpointPermission(endpointDescription,
EndpointPermission.EXPORT);
ExportRegistration exportRegistration = null;
try {
// Actually do the export and return export
// registration
exportRegistration = exportService(
serviceReference, overridingProperties,
exportedInterfaces, rsContainers[i],
endpointDescriptionProperties);
} catch (Exception e) {
exportRegistration = new ExportRegistration(e,
endpointDescription);
}
addExportRegistration(exportRegistration);
// We add it to the results in either success or error case
resultRegistrations.add(exportRegistration);
}
}
}
}
// publish all activeExportRegistrations
for (ExportRegistration exportReg : resultRegistrations)
publishExportEvent(exportReg);
trace("exportService","exported registrations="+resultRegistrations); //$NON-NLS-1$ //$NON-NLS-2$
// and return
return new ArrayList<org.osgi.service.remoteserviceadmin.ExportRegistration>(
resultRegistrations);
}
public org.osgi.service.remoteserviceadmin.ImportRegistration importService(
org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription) {
trace("importService", "endpointDescription=" + endpointDescription); //$NON-NLS-1$ //$NON-NLS-2$
// First, make sure that the client bundle has the IMPORT endpoint
// permission
checkEndpointPermission(endpointDescription, EndpointPermission.IMPORT);
final EndpointDescription ed = (endpointDescription instanceof EndpointDescription) ? (EndpointDescription) endpointDescription
: new EndpointDescription(endpointDescription.getProperties());
// Now get IConsumerContainerSelector, to select the ECF container
// for the given endpointDescription
final IConsumerContainerSelector consumerContainerSelector = getConsumerContainerSelector();
// If there is none, then we can go no further
if (consumerContainerSelector == null) {
String errorMessage = "No consumerContainerSelector available"; //$NON-NLS-1$
logError("importService",errorMessage,new SelectContainerException(errorMessage,null,null)); //$NON-NLS-1$
// As specified in section 122.5.2, return null
return null;
}
// Select the rsContainer to handle the endpoint description
IRemoteServiceContainer rsContainer = null;
ImportRegistration importRegistration = null;
try {
rsContainer = AccessController
.doPrivileged(new PrivilegedExceptionAction<IRemoteServiceContainer>() {
public IRemoteServiceContainer run()
throws SelectContainerException {
return consumerContainerSelector
.selectConsumerContainer(ed);
}
});
} catch (PrivilegedActionException e) {
logError("importService","Unexpected exception in selectConsumerContainer",e.getException()); //$NON-NLS-1$ //$NON-NLS-2$
importRegistration = new ImportRegistration(ed, e.getException());
} catch (Exception e) {
logError("importService","Unexpected exception in selectConsumerContainer",e); //$NON-NLS-1$ //$NON-NLS-2$
importRegistration = new ImportRegistration(ed, e);
}
// If none found, log an error and return null
if (rsContainer == null && importRegistration == null) {
String errorMessage = "No remote service container selected for endpoint=" //$NON-NLS-1$
+ endpointDescription
+ ". Remote service NOT IMPORTED"; //$NON-NLS-1$
logError("importService",errorMessage,new SelectContainerException(errorMessage,null,null)); //$NON-NLS-1$
// As specified in section 122.5.2, return null
return null;
}
// If one selected then import the service to create an import
// registration
synchronized (importedRegistrations) {
if (importRegistration == null) {
ImportEndpoint importEndpoint = findImportEndpoint(ed);
importRegistration = ((importEndpoint != null) ? new ImportRegistration(importEndpoint)
: importService(ed, rsContainer));
}
addImportRegistration(importRegistration);
}
// publish import event
publishImportEvent(importRegistration);
trace("importService","importRegistration="+importRegistration); //$NON-NLS-1$ //$NON-NLS-2$
// Finally, return the importRegistration. It may be null or not.
return importRegistration;
}
public Collection<org.osgi.service.remoteserviceadmin.ExportReference> getExportedServices() {
Collection<org.osgi.service.remoteserviceadmin.ExportReference> results = new ArrayList<org.osgi.service.remoteserviceadmin.ExportReference>();
synchronized (exportedRegistrations) {
// XXX The spec doesn't specify what is supposed to happen
// when the registrations is empty...but the TCK test method: RemoteServiceAdminSecure.testNoPermissions()
// assumes that a SecurityException is thrown when accessed without READ permission
if (exportedRegistrations.isEmpty())
checkRSAReadAccess();
for (org.osgi.service.remoteserviceadmin.ExportRegistration reg : exportedRegistrations) {
org.osgi.service.remoteserviceadmin.ExportReference eRef = reg
.getExportReference();
if (eRef != null
&& checkEndpointPermissionRead("getExportedServices", //$NON-NLS-1$
eRef.getExportedEndpoint()))
results.add(eRef);
}
}
return results;
}
private void checkRSAReadAccess() {
Map<String, Object> props = new HashMap();
props.put(
org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID,
UUID.randomUUID().toString());
props.put(org.osgi.framework.Constants.OBJECTCLASS, new String[] { UUID.randomUUID().toString() });
props.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS, UUID.randomUUID().toString());
checkEndpointPermission(new org.osgi.service.remoteserviceadmin.EndpointDescription(
props),org.osgi.service.remoteserviceadmin.EndpointPermission.READ);
}
public Collection<org.osgi.service.remoteserviceadmin.ImportReference> getImportedEndpoints() {
Collection<org.osgi.service.remoteserviceadmin.ImportReference> results = new ArrayList<org.osgi.service.remoteserviceadmin.ImportReference>();
synchronized (importedRegistrations) {
// XXX The spec doesn't specify what is supposed to happen
// when the registrations is empty...but the TCK test method: RemoteServiceAdminSecure.testNoPermissions()
// assumes that a SecurityException is thrown when accessed without READ permission
if (importedRegistrations.isEmpty())
checkRSAReadAccess();
for (org.osgi.service.remoteserviceadmin.ImportRegistration reg : importedRegistrations) {
org.osgi.service.remoteserviceadmin.ImportReference iRef = reg
.getImportReference();
if (iRef != null
&& checkEndpointPermissionRead("getImportedEndpoints", //$NON-NLS-1$
iRef.getImportedEndpoint()))
results.add(iRef);
}
}
return results;
}
// end RemoteServiceAdmin service interface impl methods
private boolean checkEndpointPermissionRead(
String methodName,
org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription) {
try {
checkEndpointPermission(endpointDescription,
EndpointPermission.READ);
return true;
} catch (SecurityException e) {
logError(methodName,
"permission check failed for read access to endpointDescription=" //$NON-NLS-1$
+ endpointDescription, e);
return false;
}
}
private BundleContext getClientBundleContext() {
return clientBundle.getBundleContext();
}
private BundleContext getRSABundleContext() {
return Activator.getContext();
}
private Bundle getRSABundle() {
BundleContext bc = Activator.getContext();
if (bc == null) return null;
return bc.getBundle();
}
private void addImportRegistration(ImportRegistration importRegistration) {
synchronized (importedRegistrations) {
importedRegistrations.add(importRegistration);
localImportedRegistrations.add(importRegistration);
}
}
private void addExportRegistration(ExportRegistration exportRegistration) {
synchronized (exportedRegistrations) {
exportedRegistrations.add(exportRegistration);
localExportedRegistrations.add(exportRegistration);
}
}
private boolean removeExportRegistration(
ExportRegistration exportRegistration) {
synchronized (exportedRegistrations) {
localExportedRegistrations.remove(exportRegistration);
return exportedRegistrations.remove(exportRegistration);
}
}
private boolean removeImportRegistration(
ImportRegistration importRegistration) {
synchronized (importedRegistrations) {
localExportedRegistrations.remove(importRegistration);
return importedRegistrations.remove(importRegistration);
}
}
private void checkEndpointPermission(
org.osgi.service.remoteserviceadmin.EndpointDescription endpointDescription,
String permissionType) throws SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm == null)
return;
sm.checkPermission(new EndpointPermission(endpointDescription,
Activator.getDefault().getFrameworkUUID(), permissionType));
}
class ExportEndpoint {
private ServiceReference serviceReference;
private EndpointDescription endpointDescription;
private IRemoteServiceRegistration rsRegistration;
private Set<ExportRegistration> activeExportRegistrations = new HashSet<ExportRegistration>();
private Map<String,Object> originalProperties;
public String toString() {
StringBuffer buf = new StringBuffer("ExportEndpoint["); //$NON-NLS-1$
buf.append("serviceReference=").append(serviceReference).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
buf.append("rsRegistration=").append(rsRegistration).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buf.toString();
}
ExportEndpoint(ServiceReference serviceReference,
EndpointDescription endpointDescription,
IRemoteServiceRegistration reg, Map<String,Object> originalProperties) {
Assert.isNotNull(serviceReference);
this.serviceReference = serviceReference;
Assert.isNotNull(endpointDescription);
this.endpointDescription = endpointDescription;
Assert.isNotNull(reg);
this.rsRegistration = reg;
Assert.isNotNull(originalProperties);
this.originalProperties = originalProperties;
}
synchronized ID getContainerID() {
return endpointDescription.getContainerID();
}
synchronized ServiceReference getServiceReference() {
return serviceReference;
}
synchronized EndpointDescription getEndpointDescription() {
return endpointDescription;
}
synchronized IRemoteServiceRegistration getRemoteServiceRegistration() {
return rsRegistration;
}
synchronized boolean addExportRegistration(
ExportRegistration exportRegistration) {
return this.activeExportRegistrations.add(exportRegistration);
}
synchronized boolean close(ExportRegistration exportRegistration) {
boolean removed = this.activeExportRegistrations
.remove(exportRegistration);
if (removed && activeExportRegistrations.size() == 0) {
if (rsRegistration != null) {
rsRegistration.unregister();
rsRegistration = null;
}
serviceReference = null;
endpointDescription = null;
originalProperties = null;
}
return removed;
}
synchronized EndpointDescription update(Map properties) {
// As per ExportRegistraiton.update javadocs, query the
// serviceReference for current properties
Map<String, Object> serviceReferenceProperties = PropertiesUtil
.copyProperties(serviceReference, new HashMap());
// As per ExportRegistration.update javadocs, if properties argument
// is null,
// use the original ED properties
// Get copy of original remote service properties
Map<String, Object> rsProperties = PropertiesUtil.copyProperties(
this.originalProperties, new HashMap<String, Object>());
Map<String, Object> updateProperties = (properties == null) ? rsProperties
: PropertiesUtil.copyProperties(properties, rsProperties);
Map<String, Object> updatedEDProperties = PropertiesUtil
.mergeProperties(updateProperties,
serviceReferenceProperties);
// update timestamp
updatedEDProperties.put(RemoteConstants.ENDPOINT_TIMESTAMP,
System.currentTimeMillis());
// Create new endpoint description, and this will be our updated
// EndpointDescription
this.endpointDescription = new EndpointDescription(
updatedEDProperties);
return this.endpointDescription;
}
}
/**
* @since 4.2
*/
public class ExportRegistration implements
org.osgi.service.remoteserviceadmin.ExportRegistration {
private ExportReference exportReference;
private boolean closed = false;
public String toString() {
StringBuffer buf = new StringBuffer("ExportRegistration["); //$NON-NLS-1$
buf.append("exportReference=").append(exportReference).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
buf.append("closed=").append(closed).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buf.toString();
}
ExportRegistration(ExportEndpoint exportEndpoint) {
Assert.isNotNull(exportEndpoint);
exportEndpoint.addExportRegistration(this);
this.exportReference = new ExportReference(exportEndpoint);
}
ExportRegistration(Throwable exception,
EndpointDescription errorEndpointDescription) {
Assert.isNotNull(exception);
this.exportReference = new ExportReference(exception,
errorEndpointDescription);
}
public ID getContainerID() {
return (closed)?null:exportReference.getContainerID();
}
ServiceReference getServiceReference() {
return (closed)?null:exportReference.getExportedService();
}
public long getRemoteServiceId() {
return (closed) ? 0 : exportReference.getRemoteServiceId();
}
public org.osgi.service.remoteserviceadmin.ExportReference getExportReference() {
Throwable t = getException();
if (t != null)
return null;
return (closed)?null:exportReference;
}
boolean match(ServiceReference serviceReference) {
return match(serviceReference, null);
}
boolean match(ServiceReference serviceReference, ID containerID) {
ServiceReference ourServiceReference = getServiceReference();
if (ourServiceReference == null)
return false;
boolean serviceReferenceCompare = ourServiceReference
.equals(serviceReference);
// If the second parameter is null, then we compare only on service
// references
if (containerID == null)
return serviceReferenceCompare;
ID ourContainerID = getContainerID();
if (ourContainerID == null)
return false;
return serviceReferenceCompare
&& ourContainerID.equals(containerID);
}
synchronized ExportEndpoint getExportEndpoint(
ServiceReference serviceReference, ID containerID) {
return (closed?null:(match(serviceReference, containerID) ? exportReference
.getExportEndpoint() : null));
}
IRemoteServiceRegistration getRemoteServiceRegistration() {
return (closed)?null:exportReference.getRemoteServiceRegistration();
}
EndpointDescription getEndpointDescription() {
return (closed)?null:exportReference.getEndpointDescription();
}
public void close() {
boolean publish = false;
ID containerID = null;
Throwable exception = null;
EndpointDescription endpointDescription = null;
ExportReference exRef = null;
synchronized (this) {
// Only do this once
if (!closed) {
containerID = getContainerID();
exception = getException();
endpointDescription = getEndpointDescription();
exRef = this.exportReference;
publish = exportReference.close(this);
this.exportReference = null;
closed = true;
}
}
removeExportRegistration(this);
Bundle rsaBundle = getRSABundle();
// Only publish events
if (publish && rsaBundle != null && exRef != null)
publishEvent(new RemoteServiceAdminEvent(containerID,
RemoteServiceAdminEvent.EXPORT_UNREGISTRATION,
rsaBundle, exRef, exception,
endpointDescription), endpointDescription);
}
public Throwable getException() {
return (closed)?updateException:exportReference.getException();
}
private Throwable updateException;
public org.osgi.service.remoteserviceadmin.EndpointDescription update(
Map<String, ?> properties) {
// If this registration has been closed then set updateException
// to IllegalStateException and return null
if (closed) {
updateException = new IllegalStateException("Update failed since ExportRegistration already closed"); //$NON-NLS-1$
return null;
}
// Update exportReference. If exception is thrown, or update
// returns null then set updateException and return null;
EndpointDescription updatedED = null;
Map<String, Object> props = PropertiesUtil.copySerializableProperties(properties,
new TreeMap<String, Object>());
try {
updatedED = exportReference.update(props);
} catch (RuntimeException e) {
updateException = e;
return null;
}
// If the exportReference returned null, then the underlying ExportEndpoint was null
if (updatedED == null) {
updateException = new IllegalStateException("Update failed because ExportEndpoint was null"); //$NON-NLS-1$
return null;
}
// We've succeeded in the update to set updateException to null
// in case it was set by previous update
this.updateException = null;
Bundle rsaBundle = getRSABundle();
// Notify with EXPORT_UPDATE
if (rsaBundle != null)
publishEvent(new RemoteServiceAdminEvent(getContainerID(),
RemoteServiceAdminEvent.EXPORT_UPDATE,
rsaBundle, this.exportReference, null, updatedED), updatedED);
return updatedED;
}
}
/**
* @since 4.2
*/
public class ExportReference implements
org.osgi.service.remoteserviceadmin.ExportReference {
private ExportEndpoint exportEndpoint;
private Throwable exception;
private EndpointDescription errorEndpointDescription;
public String toString() {
StringBuffer buf = new StringBuffer("ExportReference["); //$NON-NLS-1$
buf.append("exportEndpoint="+exportEndpoint).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
buf.append("exception=").append(exception).append(";").append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return buf.toString();
}
ExportReference(ExportEndpoint exportEndpoint) {
Assert.isNotNull(exportEndpoint);
this.exportEndpoint = exportEndpoint;
}
synchronized EndpointDescription update(Map<String, ?> properties) {
if (exportEndpoint == null) return null;
return exportEndpoint.update(properties);
}
ExportReference(Throwable exception,
EndpointDescription errorEndpointDescription) {
Assert.isNotNull(exception);
this.exception = exception;
Assert.isNotNull(exception);
this.errorEndpointDescription = errorEndpointDescription;
}
synchronized Throwable getException() {
return exception;
}
synchronized boolean close(ExportRegistration exportRegistration) {
if (exportEndpoint == null)
return false;
boolean result = exportEndpoint.close(exportRegistration);
exportEndpoint = null;
return result;
}
synchronized ExportEndpoint getExportEndpoint() {
return exportEndpoint;
}
synchronized IRemoteServiceRegistration getRemoteServiceRegistration() {
return (exportEndpoint == null) ? null : exportEndpoint
.getRemoteServiceRegistration();
}
public synchronized ID getContainerID() {
return (exportEndpoint == null) ? null : exportEndpoint
.getContainerID();
}
public synchronized long getRemoteServiceId() {
IRemoteServiceRegistration r = getRemoteServiceRegistration();
return (r == null)?0:r.getID().getContainerRelativeID();
}
public synchronized ServiceReference getExportedService() {
return (exportEndpoint == null) ? null : exportEndpoint
.getServiceReference();
}
public synchronized org.osgi.service.remoteserviceadmin.EndpointDescription getExportedEndpoint() {
return (exportEndpoint == null) ? null : exportEndpoint
.getEndpointDescription();
}
synchronized EndpointDescription getEndpointDescription() {
return (exportEndpoint == null) ? errorEndpointDescription
: exportEndpoint.getEndpointDescription();
}
}
class ImportEndpoint {
private IRemoteServiceContainer rsContainer;
private IRemoteService rs;
private IRemoteServiceListener rsListener;
private EndpointDescription endpointDescription;
private IRemoteServiceReference rsReference;
private ServiceRegistration proxyRegistration;
private Set<ImportRegistration> activeImportRegistrations = new HashSet<ImportRegistration>();
public String toString() {
StringBuffer buf = new StringBuffer("ImportEndpoint["); //$NON-NLS-1$
buf.append("proxyRegistration=").append(proxyRegistration); //$NON-NLS-1$
buf.append("rsReference=").append(rsReference).append(";").append("]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return buf.toString();
}
ImportEndpoint(IRemoteServiceContainer rsContainer,
IRemoteServiceReference rsReference,
IRemoteService rs,
ServiceRegistration proxyRegistration,
EndpointDescription endpointDescription) {
this.rsContainer = rsContainer;
this.rsReference = rsReference;
this.endpointDescription = endpointDescription;
this.rs = rs;
this.proxyRegistration = proxyRegistration;
this.rsListener = new RemoteServiceListener();
// Add the remoteservice listener to the container adapter, so that
// the rsListener notified asynchronously if our underlying remote
// service reference is unregistered locally due to disconnect or remote
// ejection
this.rsContainer.getContainerAdapter().addRemoteServiceListener(this.rsListener);
}
synchronized EndpointDescription getEndpointDescription() {
return endpointDescription;
}
synchronized ServiceRegistration getProxyRegistration() {
return proxyRegistration;
}
synchronized ID getContainerID() {
return (rsReference == null) ? null : rsReference.getContainerID();
}
synchronized boolean addImportRegistration(
ImportRegistration importRegistration) {
return this.activeImportRegistrations.add(importRegistration);
}
synchronized boolean close(ImportRegistration importRegistration) {
boolean removed = this.activeImportRegistrations
.remove(importRegistration);
if (removed && activeImportRegistrations.size() == 0) {
if (proxyRegistration != null) {
try {
proxyRegistration.unregister();
} catch (Throwable t) {
// do nothing
}
proxyRegistration = null;
}
IRemoteServiceContainerAdapter rsContainerAdapter = rsContainer.getContainerAdapter();
if (rsContainerAdapter != null) {
if (rsReference != null) {
rsContainerAdapter.ungetRemoteService(rsReference);
rsReference = null;
}
if (rsListener != null) {
rsContainerAdapter
.removeRemoteServiceListener(rsListener);
rsListener = null;
}
}
rs = null;
endpointDescription = null;
rsContainer = null;
}
return removed;
}
synchronized boolean match(IRemoteServiceID remoteServiceID) {
if (remoteServiceID == null || rsReference == null)
return false;
return rsReference.getID().equals(remoteServiceID);
}
synchronized boolean match(EndpointDescription ed) {
if (activeImportRegistrations.size() == 0)
return false;
return this.endpointDescription.isSameService(ed);
}
synchronized void update(
org.osgi.service.remoteserviceadmin.EndpointDescription endpoint) {
if (proxyRegistration == null)
return;
// Get or create ECF endpoint description
EndpointDescription updatedEndpoint = (endpoint instanceof EndpointDescription) ? ((EndpointDescription) endpoint)
: new EndpointDescription(endpoint.getProperties());
// Create new proxy properties from updatedEndpoint and rsReference and rs
Map newProxyProperties = createProxyProperties(rsContainer.getContainer().getID(), updatedEndpoint,
rsReference, rs);
// set the endpoint description with the proxy properties
updatedEndpoint.setPropertiesOverrides(newProxyProperties);
// set this endpointDescription to updatedEndpoint
this.endpointDescription = updatedEndpoint;
// Set proxyRegistration properties
this.proxyRegistration.setProperties(PropertiesUtil
.createDictionaryFromMap(newProxyProperties));
}
}
/**
* @since 4.2
*/
public class ImportRegistration implements
org.osgi.service.remoteserviceadmin.ImportRegistration {
private ImportReference importReference;
private boolean closed = false;
public String toString() {
StringBuffer buf = new StringBuffer("ImportRegistration["); //$NON-NLS-1$
buf.append("importReference=").append(importReference).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
buf.append("closed=").append(closed).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buf.toString();
}
ImportRegistration(ImportEndpoint importEndpoint) {
Assert.isNotNull(importEndpoint);
importEndpoint.addImportRegistration(this);
this.importReference = new ImportReference(importEndpoint);
}
ImportRegistration(EndpointDescription errorEndpointDescription,
Throwable exception) {
this.importReference = new ImportReference(
errorEndpointDescription, exception);
}
public ID getContainerID() {
return (closed)?null:importReference.getContainerID();
}
public long getRemoteServiceId() {
return (closed)?0:importReference.getRemoteServiceId();
}
EndpointDescription getEndpointDescription() {
return (closed)?null:importReference.getEndpointDescription();
}
boolean match(IRemoteServiceID remoteServiceID) {
return (closed)?null:importReference.match(remoteServiceID);
}
boolean match(EndpointDescription ed) {
return (getImportEndpoint(ed) != null);
}
ImportEndpoint getImportEndpoint(EndpointDescription ed) {
return (closed)?null:importReference.match(ed);
}
public org.osgi.service.remoteserviceadmin.ImportReference getImportReference() {
Throwable t = getException();
if (t != null)
return null;
return (closed)?null:importReference;
}
public void close() {
boolean publish = false;
ID containerID = null;
Throwable exception = null;
EndpointDescription endpointDescription = null;
ImportReference imRef = null;
synchronized (this) {
// only do this once
if (!closed) {
containerID = getContainerID();
exception = getException();
endpointDescription = getEndpointDescription();
imRef = this.importReference;
publish = importReference.close(this);
this.importReference = null;
closed = true;
}
}
removeImportRegistration(this);
Bundle rsaBundle = getRSABundle();
if (publish && rsaBundle != null && imRef != null)
publishEvent(new RemoteServiceAdminEvent(containerID,
RemoteServiceAdminEvent.IMPORT_UNREGISTRATION,
rsaBundle, imRef, exception,
endpointDescription), endpointDescription);
}
public Throwable getException() {
return (closed)?updateException:importReference.getException();
}
private Throwable updateException;
public boolean update(
org.osgi.service.remoteserviceadmin.EndpointDescription endpoint) {
// If this registration has been closed then set updateException
// to IllegalStateException and return null
if (closed) {
updateException = new IllegalStateException(
"Update failed since ImportRegistration already closed"); //$NON-NLS-1$
return false;
}
try {
importReference.update(endpoint);
} catch (Exception e) {
updateException = e;
return false;
}
Bundle rsaBundle = getRSABundle();
EndpointDescription ed = getEndpointDescription();
if (rsaBundle != null)
publishEvent(new RemoteServiceAdminEvent(getContainerID(),
RemoteServiceAdminEvent.IMPORT_UPDATE, rsaBundle,
this.importReference, null, ed), ed);
return true;
}
}
/**
* @since 4.2
*/
public class ImportReference implements
org.osgi.service.remoteserviceadmin.ImportReference {
private ImportEndpoint importEndpoint;
private Throwable exception;
private EndpointDescription errorEndpointDescription;
public String toString() {
StringBuffer buf = new StringBuffer("ImportReference["); //$NON-NLS-1$
buf.append("importEndpoint=").append(importEndpoint).append(";"); //$NON-NLS-1$ //$NON-NLS-2$
buf.append("exception=").append(exception).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
return buf.toString();
}
ImportReference(ImportEndpoint importEndpoint) {
Assert.isNotNull(importEndpoint);
this.importEndpoint = importEndpoint;
}
synchronized void update(
org.osgi.service.remoteserviceadmin.EndpointDescription endpoint) {
if (importEndpoint != null) importEndpoint.update(endpoint);
}
ImportReference(EndpointDescription endpointDescription,
Throwable exception) {
Assert.isNotNull(exception);
this.exception = exception;
Assert.isNotNull(endpointDescription);
this.errorEndpointDescription = endpointDescription;
}
synchronized Throwable getException() {
return exception;
}
synchronized boolean match(IRemoteServiceID remoteServiceID) {
return (importEndpoint == null) ? false : importEndpoint
.match(remoteServiceID);
}
synchronized ImportEndpoint match(EndpointDescription ed) {
if (importEndpoint != null && importEndpoint.match(ed))
return importEndpoint;
return null;
}
synchronized EndpointDescription getEndpointDescription() {
return (importEndpoint == null) ? errorEndpointDescription
: importEndpoint.getEndpointDescription();
}
public synchronized ID getContainerID() {
return (importEndpoint == null) ? null : importEndpoint
.getContainerID();
}
public synchronized long getRemoteServiceId() {
EndpointDescription ed = getEndpointDescription();
return (ed == null)?0:ed.getRemoteServiceId();
}
public synchronized ServiceReference getImportedService() {
return (importEndpoint == null) ? null : importEndpoint
.getProxyRegistration().getReference();
}
public synchronized org.osgi.service.remoteserviceadmin.EndpointDescription getImportedEndpoint() {
return (importEndpoint == null) ? null : importEndpoint
.getEndpointDescription();
}
synchronized boolean close(ImportRegistration importRegistration) {
if (importEndpoint == null)
return false;
boolean result = importEndpoint.close(importRegistration);
importEndpoint = null;
return result;
}
}
private void publishEvent(final RemoteServiceAdminEvent event,
EndpointDescription endpointDescription) {
// send event synchronously to RemoteServiceAdminListeners
EndpointPermission perm = new EndpointPermission(endpointDescription,
Activator.getDefault().getFrameworkUUID(),
EndpointPermission.READ);
// notify synchronously all appropriate listeners (those with READ
// permission)
final RemoteServiceAdminListener[] listeners = getListeners(perm);
if (listeners != null)
for (int i = 0; i < listeners.length; i++) {
final RemoteServiceAdminListener listener = listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
logError("publishEvent", "Exeption in RemoteServiceAdminListener.remoteAdminEvent for listener="+listener, exception); //$NON-NLS-1$ //$NON-NLS-2$
}
public void run() throws Exception {
listener.remoteAdminEvent(event);
}
});
}
// Now also post the event asynchronously to EventAdmin
postEvent(event, endpointDescription);
}
private void postEvent(RemoteServiceAdminEvent event,
EndpointDescription endpointDescription) {
int eventType = event.getType();
String eventTypeName = null;
String registrationTypeName = null;
switch (eventType) {
case (RemoteServiceAdminEvent.EXPORT_REGISTRATION):
eventTypeName = "EXPORT_REGISTRATION"; //$NON-NLS-1$
registrationTypeName = "export.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.EXPORT_ERROR):
eventTypeName = "EXPORT_ERROR"; //$NON-NLS-1$
registrationTypeName = "export.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.EXPORT_UNREGISTRATION):
eventTypeName = "EXPORT_UNREGISTRATION"; //$NON-NLS-1$
registrationTypeName = "export.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.EXPORT_WARNING):
eventTypeName = "EXPORT_WARNING"; //$NON-NLS-1$
registrationTypeName = "export.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.EXPORT_UPDATE):
eventTypeName = "EXPORT_UPDATE"; //$NON-NLS-1$
registrationTypeName = "export.registration"; //$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.IMPORT_REGISTRATION):
eventTypeName = "IMPORT_REGISTRATION"; //$NON-NLS-1$
registrationTypeName = "import.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.IMPORT_ERROR):
eventTypeName = "IMPORT_ERROR"; //$NON-NLS-1$
registrationTypeName = "import.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.IMPORT_UNREGISTRATION):
eventTypeName = "IMPORT_UNREGISTRATION"; //$NON-NLS-1$
registrationTypeName = "import.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.IMPORT_WARNING):
eventTypeName = "IMPORT_WARNING"; //$NON-NLS-1$
registrationTypeName = "import.registration";//$NON-NLS-1$
break;
case (RemoteServiceAdminEvent.IMPORT_UPDATE):
eventTypeName = "IMPORT_UPDATE"; //$NON-NLS-1$
registrationTypeName = "import.registration"; //$NON-NLS-1$
break;
}
if (eventTypeName == null) {
logError("postEvent", "Event type=" + eventType //$NON-NLS-1$ //$NON-NLS-2$
+ " not understood for event=" + event + ". Not posting"); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
final String topic = "org/osgi/service/remoteserviceadmin/" + eventTypeName; //$NON-NLS-1$
Bundle rsaBundle = getRSABundle();
if (rsaBundle == null) {
logError(
"postEvent", "RSA Bundle is null. Not posting remote service admin event=" + event); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
final Dictionary eventProperties = new Properties();
eventProperties.put("bundle", rsaBundle); //$NON-NLS-1$
eventProperties.put("bundle.id", //$NON-NLS-1$
new Long(rsaBundle.getBundleId()));
eventProperties.put("bundle.symbolicname", //$NON-NLS-1$
rsaBundle.getSymbolicName());
eventProperties.put("bundle.version", rsaBundle.getVersion()); //$NON-NLS-1$
List<String> result = new ArrayList<String>();
Map signers1 = clientBundle.getSignerCertificates(Bundle.SIGNERS_ALL);
for (Iterator i = signers1.keySet().iterator(); i.hasNext();)
result.add(i.next().toString());
String[] signers = (String[]) result.toArray(new String[result.size()]);
if (signers != null && signers.length > 0)
eventProperties.put("bundle.signer", signers); //$NON-NLS-1$
Throwable t = event.getException();
if (t != null) {
eventProperties.put("cause", t); //$NON-NLS-1$
// Additions for RSA 1.1 section 122.7.1
eventProperties.put("exception", t); //$NON-NLS-1$
eventProperties.put("exception.class", t.getClass().getName()); //$NON-NLS-1$
String exceptionMessage = t.getMessage();
if (exceptionMessage != null)
eventProperties.put("exception.message", exceptionMessage); //$NON-NLS-1$
}
long serviceId = endpointDescription.getServiceId();
if (serviceId != 0)
eventProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID,
new Long(serviceId));
String frameworkUUID = endpointDescription.getFrameworkUUID();
if (frameworkUUID != null)
eventProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID,
frameworkUUID);
String endpointId = endpointDescription.getId();
if (endpointId != null)
eventProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID,
endpointId);
List<String> interfaces = endpointDescription.getInterfaces();
if (interfaces != null && interfaces.size() > 0)
eventProperties.put(org.osgi.framework.Constants.OBJECTCLASS,
interfaces.toArray(new String[interfaces.size()]));
List<String> importedConfigs = endpointDescription
.getConfigurationTypes();
if (importedConfigs != null && importedConfigs.size() > 0)
eventProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS,
importedConfigs.toArray(new String[importedConfigs
.size()]));
eventProperties.put("timestamp", new Long(new Date().getTime())); //$NON-NLS-1$
eventProperties.put("event", event); //$NON-NLS-1$
if (registrationTypeName != null)
eventProperties.put(registrationTypeName, endpointDescription);
final EventAdmin eventAdmin = AccessController
.doPrivileged(new PrivilegedAction<EventAdmin>() {
public EventAdmin run() {
synchronized (eventAdminTrackerLock) {
eventAdminTracker = new ServiceTracker(
getRSABundleContext(), EventAdmin.class
.getName(), null);
eventAdminTracker.open();
}
return (EventAdmin) eventAdminTracker.getService();
}
});
if (eventAdmin == null) {
logError("postEvent", //$NON-NLS-1$
"No EventAdmin service available to send eventTopic=" //$NON-NLS-1$
+ topic + " eventProperties=" + eventProperties); //$NON-NLS-1$
return;
}
// post via event admin
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
eventAdmin.postEvent(new Event(topic, eventProperties));
return null;
}
});
}
private void publishExportEvent(ExportRegistration exportRegistration) {
Throwable exception = exportRegistration.getException();
org.osgi.service.remoteserviceadmin.ExportReference exportReference = (exception == null) ? exportRegistration
.getExportReference() : null;
EndpointDescription endpointDescription = exportRegistration
.getEndpointDescription();
RemoteServiceAdminEvent rsaEvent = new RemoteServiceAdminEvent(
exportRegistration.getContainerID(),
(exception == null) ? RemoteServiceAdminEvent.EXPORT_REGISTRATION
: RemoteServiceAdminEvent.EXPORT_ERROR, getRSABundle(),
exportReference, exception, endpointDescription);
publishEvent(rsaEvent, endpointDescription);
}
private void publishImportEvent(ImportRegistration importRegistration) {
Throwable exception = importRegistration.getException();
org.osgi.service.remoteserviceadmin.ImportReference importReference = (exception == null) ? importRegistration
.getImportReference() : null;
EndpointDescription endpointDescription = importRegistration
.getEndpointDescription();
RemoteServiceAdminEvent rsaEvent = new RemoteServiceAdminEvent(
importRegistration.getContainerID(),
(exception == null) ? RemoteServiceAdminEvent.IMPORT_REGISTRATION
: RemoteServiceAdminEvent.IMPORT_ERROR, getRSABundle(),
importReference, exception, endpointDescription);
publishEvent(rsaEvent, endpointDescription);
}
private RemoteServiceAdminListener[] getListeners(EndpointPermission perm) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
synchronized (remoteServiceAdminListenerTrackerLock) {
if (remoteServiceAdminListenerTracker == null) {
remoteServiceAdminListenerTracker = new ServiceTracker(
getRSABundleContext(),
RemoteServiceAdminListener.class.getName(),
null);
remoteServiceAdminListenerTracker.open();
}
return null;
}
}
});
ServiceReference[] unfilteredRefs = remoteServiceAdminListenerTracker
.getServiceReferences();
if (unfilteredRefs == null)
return null;
// Filter by Bundle.hasPermission
List<ServiceReference> filteredRefs = new ArrayList<ServiceReference>();
for (ServiceReference ref : unfilteredRefs)
if (perm == null || ref.getBundle().hasPermission(perm))
filteredRefs.add(ref);
List<RemoteServiceAdminListener> results = new ArrayList<RemoteServiceAdminListener>();
for (final ServiceReference ref : filteredRefs) {
RemoteServiceAdminListener l = AccessController
.doPrivileged(new PrivilegedAction<RemoteServiceAdminListener>() {
public RemoteServiceAdminListener run() {
return (RemoteServiceAdminListener) remoteServiceAdminListenerTracker
.getService(ref);
}
});
if (l != null)
results.add(l);
}
return results.toArray(new RemoteServiceAdminListener[results.size()]);
}
private ExportEndpoint findExistingExportEndpoint(
ServiceReference serviceReference, ID containerID) {
for (org.osgi.service.remoteserviceadmin.ExportRegistration eReg : exportedRegistrations) {
if (eReg instanceof ExportRegistration) {
ExportEndpoint exportEndpoint = ((ExportRegistration) eReg)
.getExportEndpoint(serviceReference, containerID);
if (exportEndpoint != null)
return exportEndpoint;
}
}
return null;
}
private Object consumerContainerSelectorTrackerLock = new Object();
private ServiceTracker consumerContainerSelectorTracker;
private Object hostContainerSelectorTrackerLock = new Object();
private ServiceTracker hostContainerSelectorTracker;
protected IHostContainerSelector getHostContainerSelector() {
return AccessController
.doPrivileged(new PrivilegedAction<IHostContainerSelector>() {
public IHostContainerSelector run() {
synchronized (hostContainerSelectorTrackerLock) {
if (hostContainerSelectorTracker == null) {
hostContainerSelectorTracker = new ServiceTracker(
getRSABundleContext(),
IHostContainerSelector.class.getName(),
null);
hostContainerSelectorTracker.open();
}
}
return (IHostContainerSelector) hostContainerSelectorTracker
.getService();
}
});
}
protected IConsumerContainerSelector getConsumerContainerSelector() {
return AccessController
.doPrivileged(new PrivilegedAction<IConsumerContainerSelector>() {
public IConsumerContainerSelector run() {
synchronized (consumerContainerSelectorTrackerLock) {
if (consumerContainerSelectorTracker == null) {
consumerContainerSelectorTracker = new ServiceTracker(
getRSABundleContext(),
IConsumerContainerSelector.class
.getName(), null);
consumerContainerSelectorTracker.open();
}
}
return (IConsumerContainerSelector) consumerContainerSelectorTracker
.getService();
}
});
}
private ContainerTypeDescription getContainerTypeDescription(ID containerID) {
return Activator.getDefault().getContainerManager()
.getContainerTypeDescription(containerID);
}
private boolean isClient(IContainer container) {
ContainerTypeDescription ctd = getContainerTypeDescription(container.getID());
if (ctd == null)
return false;
else
return !ctd.isServer();
}
private Version getPackageVersion(final ServiceReference serviceReference,
String serviceInterface, String packageName) {
Object service = AccessController
.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return getRSABundleContext().getService(
serviceReference);
}
});
if (service == null)
return null;
List<Class> interfaces = new ArrayList<Class>();
Class<?> serviceClass = service.getClass();
while (!serviceClass.equals(Object.class)) {
interfaces.addAll(Arrays.asList(serviceClass.getInterfaces()));
serviceClass = serviceClass.getSuperclass();
}
Class[] interfaceClasses = interfaces.toArray(new Class[interfaces.size()]);
if (interfaceClasses == null)
return null;
Class interfaceClass = null;
for (int i = 0; i < interfaceClasses.length; i++)
if (interfaceClasses[i].getName().equals(serviceInterface))
interfaceClass = interfaceClasses[i];
if (interfaceClass == null)
return null;
Bundle providingBundle = FrameworkUtil.getBundle(interfaceClass);
if (providingBundle == null)
return null;
return getVersionForPackage(providingBundle, packageName);
}
private Map<String, Object> createExportEndpointDescriptionProperties(
ServiceReference serviceReference,
Map<String, Object> overridingProperties,
String[] exportedInterfaces, String[] serviceIntents,
IRemoteServiceContainer rsContainer) {
IContainer container = rsContainer.getContainer();
ID containerID = container.getID();
Map<String, Object> endpointDescriptionProperties = new TreeMap<String, Object>(
String.CASE_INSENSITIVE_ORDER);
// OSGi properties
// OBJECTCLASS set to exportedInterfaces
endpointDescriptionProperties.put(
org.osgi.framework.Constants.OBJECTCLASS, exportedInterfaces);
// Service interface versions
for (int i = 0; i < exportedInterfaces.length; i++) {
String packageName = getPackageName(exportedInterfaces[i]);
String packageVersionKey = org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_PACKAGE_VERSION_
+ packageName;
// If it's pre-set...by registration or by overridingProperties,
// then use that value
String packageVersion = (String) PropertiesUtil.getPropertyValue(
serviceReference, overridingProperties, packageVersionKey);
if (packageVersion == null) {
Version version = getPackageVersion(serviceReference,
exportedInterfaces[i], packageName);
if (version != null && !version.equals(Version.emptyVersion))
packageVersion = version.toString();
}
// Only set the package version if we have a non-null value
if (packageVersion != null)
endpointDescriptionProperties.put(packageVersionKey,
packageVersion);
}
// ENDPOINT_ID
String endpointId = (String) PropertiesUtil
.getPropertyValue(
serviceReference,
overridingProperties,
org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID);
if (endpointId == null)
endpointId = UUID.randomUUID().toString();
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID,
endpointId);
// ECF ENDPOINT ID
String ecfEndpointId = (String) PropertiesUtil.getPropertyValue(
serviceReference, overridingProperties,
RemoteConstants.ENDPOINT_ID);
if (ecfEndpointId == null)
ecfEndpointId = containerID.getName();
endpointDescriptionProperties.put(RemoteConstants.ENDPOINT_ID,
ecfEndpointId);
// ENDPOINT_SERVICE_ID
// This is always set to the value from serviceReference as per 122.5.1
Long serviceId = (Long) serviceReference
.getProperty(org.osgi.framework.Constants.SERVICE_ID);
endpointDescriptionProperties.put(
org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID, serviceId);
// ENDPOINT_FRAMEWORK_ID
String frameworkId = (String) PropertiesUtil
.getPropertyValue(
serviceReference,
overridingProperties,
org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID);
if (frameworkId == null)
frameworkId = Activator.getDefault().getFrameworkUUID();
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID,
frameworkId);
// REMOTE_CONFIGS_SUPPORTED
String[] remoteConfigsSupported = getSupportedConfigs(container.getID());
if (remoteConfigsSupported == null)
remoteConfigsSupported = new String[0];
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_CONFIGS_SUPPORTED,
remoteConfigsSupported);
// SERVICE_IMPORTED_CONFIGS...set to constant value for all ECF
// providers
// supported (which is computed
// for the exporting ECF container
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS,
remoteConfigsSupported);
// SERVICE_INTENTS
Object intents = PropertiesUtil
.getPropertyValue(
null,
overridingProperties,
org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_INTENTS);
if (intents == null)
intents = serviceIntents;
if (intents != null)
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_INTENTS,
intents);
// REMOTE_INTENTS_SUPPORTED
String[] remoteIntentsSupported = getSupportedIntents(container.getID());
if (remoteIntentsSupported != null)
endpointDescriptionProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_INTENTS_SUPPORTED,
remoteIntentsSupported);
// ECF properties
// ID namespace
String idNamespace = containerID.getNamespace().getName();
endpointDescriptionProperties.put(
RemoteConstants.ENDPOINT_CONTAINER_ID_NAMESPACE, idNamespace);
// timestamp
endpointDescriptionProperties.put(RemoteConstants.ENDPOINT_TIMESTAMP, System.currentTimeMillis());
// ENDPOINT_CONNECTTARGET_ID
String connectTarget = (String) PropertiesUtil.getPropertyValue(
serviceReference, overridingProperties,
RemoteConstants.ENDPOINT_CONNECTTARGET_ID);
if (connectTarget == null && isClient(container)) {
ID connectedID = container.getConnectedID();
if (connectedID != null && !connectedID.equals(containerID))
connectTarget = connectedID.getName();
}
if (connectTarget != null)
endpointDescriptionProperties.put(
RemoteConstants.ENDPOINT_CONNECTTARGET_ID, connectTarget);
// ENDPOINT_IDFILTER_IDS
String[] idFilter = (String[]) PropertiesUtil.getPropertyValue(
serviceReference, overridingProperties,
RemoteConstants.ENDPOINT_IDFILTER_IDS);
if (idFilter != null && idFilter.length > 0)
endpointDescriptionProperties.put(
RemoteConstants.ENDPOINT_IDFILTER_IDS, idFilter);
// ENDPOINT_REMOTESERVICE_FILTER
String rsFilter = (String) PropertiesUtil.getPropertyValue(
serviceReference, overridingProperties,
RemoteConstants.ENDPOINT_REMOTESERVICE_FILTER);
if (rsFilter != null)
endpointDescriptionProperties.put(
RemoteConstants.ENDPOINT_REMOTESERVICE_FILTER, rsFilter);
// Finally, copy all non-reserved properties
return PropertiesUtil.copyNonReservedProperties(overridingProperties, endpointDescriptionProperties);
}
private Map<String, Object> copyNonReservedProperties(
ServiceReference serviceReference,
Map<String, Object> overridingProperties, Map<String, Object> target) {
// copy all other properties...from service reference
PropertiesUtil.copyNonReservedProperties(serviceReference, target);
// And override with overridingProperties
PropertiesUtil.copyNonReservedProperties(overridingProperties, target);
return target;
}
private String[] getSupportedConfigs(ID containerID) {
ContainerTypeDescription ctd = getContainerTypeDescription(containerID);
return (ctd == null) ? null : ctd.getSupportedConfigs();
}
private String[] getImportedConfigs(ID containerID,
String[] exporterSupportedConfigs) {
ContainerTypeDescription ctd = getContainerTypeDescription(containerID);
return (ctd == null) ? null : ctd
.getImportedConfigs(exporterSupportedConfigs);
}
private String[] getSupportedIntents(ID containerID) {
ContainerTypeDescription ctd = getContainerTypeDescription(containerID);
return (ctd == null) ? null : ctd.getSupportedIntents();
}
private ID[] getIDFilter(EndpointDescription endpointDescription,
ID endpointID) {
ID[] idFilter = endpointDescription.getIDFilter();
// If it is null,
return (idFilter == null) ? new ID[] { endpointID } : idFilter;
}
private String getRemoteServiceFilter(
EndpointDescription endpointDescription) {
long rsId = 0;
// if the ECF remote service id is present in properties, allow it to
// override
Long l = endpointDescription.getRemoteServiceId();
if (l != null)
rsId = l.longValue();
// if rsId is still zero, use the endpoint.service.id from
// endpoint description
if (rsId == 0)
rsId = endpointDescription.getServiceId();
// If it's *still* zero, then just use the raw filter
if (rsId == 0) {
// It's not known...so we just return the 'raw' remote service
// filter
return endpointDescription.getRemoteServiceFilter();
} else {
String edRsFilter = endpointDescription.getRemoteServiceFilter();
// It's a real remote service id...so we return
StringBuffer result = new StringBuffer("(&(") //$NON-NLS-1$
.append(org.eclipse.ecf.remoteservice.Constants.SERVICE_ID)
.append("=").append(rsId).append(")"); //$NON-NLS-1$ //$NON-NLS-2$
if (edRsFilter != null)
result.append(edRsFilter);
result.append(")"); //$NON-NLS-1$
return result.toString();
}
}
private ImportEndpoint createAndRegisterProxy(
final EndpointDescription endpointDescription,
final IRemoteServiceContainer rsContainer,
final IRemoteServiceReference selectedRsReference) throws Exception {
final BundleContext proxyServiceFactoryContext = getProxyServiceFactoryContext(endpointDescription);
if (proxyServiceFactoryContext == null)
throw new NullPointerException(
"getProxyServiceFactoryContext returned null. Cannot register proxy service factory"); //$NON-NLS-1$
final IRemoteServiceContainerAdapter containerAdapter = rsContainer
.getContainerAdapter();
ID rsContainerID = rsContainer.getContainer().getID();
// First get IRemoteService for selectedRsReference
final IRemoteService rs = containerAdapter
.getRemoteService(selectedRsReference);
if (rs == null)
throw new NullPointerException(
"getRemoteService returned null for selectedRsReference=" //$NON-NLS-1$
+ selectedRsReference + ",rsContainerID=" //$NON-NLS-1$
+ rsContainerID);
final Map proxyProperties = createProxyProperties(rsContainerID, endpointDescription,
selectedRsReference, rs);
// sync sref props with endpoint props
endpointDescription.setPropertiesOverrides(proxyProperties);
final List<String> originalTypes = endpointDescription.getInterfaces();
final List<String> asyncServiceTypes = endpointDescription.getAsyncInterfaces();
final List<String> serviceTypes = new ArrayList<String>(originalTypes);
if (asyncServiceTypes != null)
for(String ast: asyncServiceTypes)
if (ast != null && !serviceTypes.contains(ast)) serviceTypes.add(ast);
ServiceRegistration proxyRegistration = AccessController
.doPrivileged(new PrivilegedAction<ServiceRegistration>() {
public ServiceRegistration run() {
return proxyServiceFactoryContext.registerService(
(String[]) serviceTypes
.toArray(new String[serviceTypes.size()]),
createProxyServiceFactory(endpointDescription,containerAdapter,selectedRsReference,
rs),
(Dictionary) PropertiesUtil
.createDictionaryFromMap(proxyProperties));
}
});
return new ImportEndpoint(rsContainer, selectedRsReference, rs, proxyRegistration, endpointDescription);
}
private BundleContext getProxyServiceFactoryContext(
EndpointDescription endpointDescription) throws Exception {
Activator a = Activator.getDefault();
if (a == null)
throw new NullPointerException(
"ECF RemoteServiceAdmin Activator cannot be null."); //$NON-NLS-1$
if (a.isOldEquinox()) {
// In this case, we get the Bundle that exposes the first service
// interface class
BundleContext rsaContext = Activator.getContext();
if (rsaContext == null)
throw new NullPointerException(
"RSA BundleContext cannot be null"); //$NON-NLS-1$
List<String> interfaces = endpointDescription.getInterfaces();
Collection<Class> serviceInterfaceClasses = loadServiceInterfacesViaBundle(
rsaContext.getBundle(),
interfaces.toArray(new String[interfaces.size()]));
if (serviceInterfaceClasses.size() == 0)
throw new NullPointerException(
"No interface classes loadable for endpointDescription=" //$NON-NLS-1$
+ endpointDescription);
// Get the bundle responsible for the first service interface class
Class serviceInterfaceClass = serviceInterfaceClasses.iterator()
.next();
Bundle bundle = FrameworkUtil.getBundle(serviceInterfaceClass);
if (bundle == null)
throw new BundleException("Bundle for service interface class=" //$NON-NLS-1$
+ serviceInterfaceClass.getName() + " cannot be found"); //$NON-NLS-1$
int bundleState = bundle.getState();
BundleContext bundleContext = bundle.getBundleContext();
if (bundleContext == null)
throw new BundleException("Bundle=" + bundle.getSymbolicName() //$NON-NLS-1$
+ " in wrong state (" + bundleState //$NON-NLS-1$
+ ") for using BundleContext proxy service factory"); //$NON-NLS-1$
return bundleContext;
}
return a.getProxyServiceFactoryBundleContext(endpointDescription);
}
private ServiceFactory createProxyServiceFactory(
EndpointDescription endpointDescription,
IRemoteServiceContainerAdapter containerAdapter, IRemoteServiceReference selectedRsReference, IRemoteService remoteService) {
return new ProxyServiceFactory(
endpointDescription.getInterfaceVersions(), containerAdapter, selectedRsReference, remoteService);
}
private Collection<Class> loadServiceInterfacesViaBundle(Bundle bundle,
String[] interfaces) {
List<Class> result = new ArrayList<Class>();
for (int i = 0; i < interfaces.length; i++) {
try {
result.add(bundle.loadClass(interfaces[i]));
} catch (ClassNotFoundException e) {
logError("loadInterfacesViaBundle", "interface=" //$NON-NLS-1$ //$NON-NLS-2$
+ interfaces[i] + " cannot be loaded by clientBundle=" //$NON-NLS-1$
+ bundle.getSymbolicName(), e);
continue;
} catch (IllegalStateException e) {
logError(
"loadInterfacesViaBundle", //$NON-NLS-1$
"interface=" //$NON-NLS-1$
+ interfaces[i]
+ " cannot be loaded since clientBundle is in illegal state", //$NON-NLS-1$
e);
continue;
}
}
return result;
}
class ProxyServiceFactory implements ServiceFactory {
private final IRemoteServiceContainerAdapter containerAdapter;
private final IRemoteServiceReference rsReference;
private IRemoteService remoteService;
private Map<String, Version> interfaceVersions;
private long remoteProxyCount = 0L;
public ProxyServiceFactory(Map<String, Version> interfaceVersions,
IRemoteServiceContainerAdapter containerAdapter, IRemoteServiceReference rsReference, IRemoteService remoteService) {
this.containerAdapter = containerAdapter;
this.rsReference = rsReference;
this.interfaceVersions = interfaceVersions;
this.remoteService = remoteService;
}
public Object getService(Bundle bundle, ServiceRegistration registration) {
Object proxy = createProxy(bundle, registration.getReference(),
remoteService, interfaceVersions);
if (proxy != null) remoteProxyCount++;
return proxy;
}
public void ungetService(Bundle bundle,
ServiceRegistration registration, Object service) {
if (remoteProxyCount == 1L)
containerAdapter.ungetRemoteService(rsReference);
remoteProxyCount--;
ungetProxyClassLoader(bundle);
}
}
private Object createProxy(Bundle requestingBundle,
ServiceReference serviceReference, IRemoteService remoteService,
Map<String, Version> interfaceVersions) {
// Get symbolicName once for possible use below
String bundleSymbolicName = requestingBundle.getSymbolicName();
// Get String[] via OBJECTCLASS constant property
String[] serviceClassnames = (String[]) serviceReference
.getProperty(org.osgi.framework.Constants.OBJECTCLASS);
// Load as many of the serviceInterface classes as possible
Collection<Class> serviceInterfaceClasses = loadServiceInterfacesViaBundle(
requestingBundle, serviceClassnames);
// There has to be at least one serviceInterface that the clientBundle
// can
// load...otherwise the service can't be accessed
if (serviceInterfaceClasses.size() < 1)
throw new RuntimeException(
"ProxyServiceFactory cannot load any serviceInterfaces=" //$NON-NLS-1$
+ serviceInterfaceClasses
+ " for serviceReference=" + serviceReference //$NON-NLS-1$
+ " via clientBundle=" + bundleSymbolicName); //$NON-NLS-1$
// Now verify that the classes are of valid versions
if (!verifyServiceInterfaceVersionsForProxy(requestingBundle,
serviceInterfaceClasses, interfaceVersions))
return null;
// Now create/get class loader for proxy. This will typically
// be an instance of ProxyClassLoader
ClassLoader cl = getProxyClassLoader(requestingBundle);
try {
return remoteService.getProxy(cl, (Class[]) serviceInterfaceClasses
.toArray(new Class[serviceInterfaceClasses.size()]));
} catch (ECFException e) {
throw new ServiceException(
"ProxyServiceFactory cannot create proxy for clientBundle=" //$NON-NLS-1$
+ bundleSymbolicName + " from serviceReference=" //$NON-NLS-1$
+ serviceReference, e);
}
}
private Map<Bundle, ProxyClassLoader> proxyClassLoaders = new HashMap<Bundle, ProxyClassLoader>();
private ClassLoader getProxyClassLoader(Bundle bundle) {
ProxyClassLoader proxyClassLoaderForBundle = null;
synchronized (proxyClassLoaders) {
proxyClassLoaderForBundle = proxyClassLoaders.get(bundle);
if (proxyClassLoaderForBundle == null) {
proxyClassLoaderForBundle = new ProxyClassLoader(bundle);
proxyClassLoaders.put(bundle, proxyClassLoaderForBundle);
} else
proxyClassLoaderForBundle.addServiceUseCount();
}
return proxyClassLoaderForBundle;
}
private void ungetProxyClassLoader(Bundle bundle) {
synchronized (proxyClassLoaders) {
ProxyClassLoader proxyClassLoaderForBundle = proxyClassLoaders
.get(bundle);
if (proxyClassLoaderForBundle != null) {
int useCount = proxyClassLoaderForBundle.getServiceUseCount();
if (useCount == 0)
proxyClassLoaders.remove(bundle);
else
proxyClassLoaderForBundle.removeServiceUseCount();
}
}
}
protected class ProxyClassLoader extends ClassLoader {
private Bundle loadingBundle;
private int serviceUseCount = 0;
public ProxyClassLoader(Bundle loadingBundle) {
this.loadingBundle = loadingBundle;
}
public Class loadClass(String name) throws ClassNotFoundException {
return loadingBundle.loadClass(name);
}
public int getServiceUseCount() {
return serviceUseCount;
}
public void addServiceUseCount() {
serviceUseCount++;
}
public void removeServiceUseCount() {
serviceUseCount--;
}
}
private String getPackageName(String className) {
int lastDotIndex = className.lastIndexOf("."); //$NON-NLS-1$
if (lastDotIndex == -1)
return ""; //$NON-NLS-1$
return className.substring(0, lastDotIndex);
}
private boolean comparePackageVersions(String packageName,
Version remoteVersion, Version localVersion)
throws RuntimeException {
// If no remote version info, then set it to empty
if (remoteVersion == null)
remoteVersion = Version.emptyVersion;
if (localVersion == null)
localVersion = Version.emptyVersion;
// We do strict comparison of remote with local
int compareResult = localVersion.compareTo(remoteVersion);
// Now check compare result, and throw exception to fail compare
return (compareResult != 0);
}
private boolean verifyServiceInterfaceVersionsForProxy(Bundle bundle,
Collection<Class> classes, Map<String, Version> interfaceVersions) {
// For all service interface classes
boolean result = true;
// For enhancement https://bugs.eclipse.org/bugs/show_bug.cgi?id=472106
if (disableServiceVersionChecking) {
logWarning("verifyServiceInterfaceVersionsForProxy", //$NON-NLS-1$
"Service version checking disabled via service property"); //$NON-NLS-1$
return result;
}
for (Class clazz : classes) {
String className = clazz.getName();
String packageName = getPackageName(className);
// Now get remoteVersion, localVersion and do compare via package
// version comparator service
Version remoteVersion = interfaceVersions.get(className);
Version localVersion = getPackageVersionViaRequestingBundle(
packageName, bundle, remoteVersion);
LogUtility.trace("comparePackageVersions", //$NON-NLS-1$
DebugOptions.PACKAGE_VERSION_COMPARATOR, this.getClass(),
"bundle=" + bundle.getSymbolicName() + ",class=" + clazz + ",packageName=" + packageName //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
+ ",remoteVersion=" //$NON-NLS-1$
+ remoteVersion + ",localVersion=" + localVersion); //$NON-NLS-1$
if (comparePackageVersions(packageName, remoteVersion, localVersion)) {
logError("verifyServiceInterfaceVersionsForProxy", //$NON-NLS-1$
"Failed version check for proxy creation. clientBundle=" //$NON-NLS-1$
+ clientBundle + " interfaceType=" + className //$NON-NLS-1$
+ " remoteVersion=" + remoteVersion //$NON-NLS-1$
+ " localVersion=" + localVersion); //$NON-NLS-1$
result = false;
}
}
return result;
}
private Version getVersionForMatchingCapability(String packageName,
BundleCapability capability) {
// If it's a package namespace (Import-Package)
Map<String, Object> attributes = capability.getAttributes();
// Then we get the package attribute
String p = (String) attributes.get(BundleRevision.PACKAGE_NAMESPACE);
// And compare it to the package name
if (p != null && packageName.equals(p))
return (Version) attributes.get(Constants.VERSION_ATTRIBUTE);
return null;
}
private Version getPackageVersionForMatchingWire(String packageName,
List<BundleWire> bundleWires, String namespace) {
Version result = null;
for (BundleWire wire : bundleWires) {
if (namespace.equals(BundleRevision.PACKAGE_NAMESPACE))
result = getVersionForMatchingCapability(packageName,
wire.getCapability());
else if (namespace.equals(BundleRevision.BUNDLE_NAMESPACE))
// If it's a bundle namespace (Require-Bundle), then we get the
// version for package
// of the providing bundle
result = getVersionForPackage(wire.getProvider().getBundle(),
packageName);
if (result != null)
return result;
}
return result;
}
private Version getVersionForPackage(final Bundle providingBundle,
String packageName) {
Version result = null;
BundleRevision providingBundleRevision = AccessController
.doPrivileged(new PrivilegedAction<BundleRevision>() {
public BundleRevision run() {
return providingBundle.adapt(BundleRevision.class);
}
});
if (providingBundleRevision == null)
return null;
List<BundleCapability> providerCapabilities = providingBundleRevision
.getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE);
for (BundleCapability c : providerCapabilities) {
result = getVersionForMatchingCapability(packageName, c);
if (result != null)
return result;
}
return result;
}
private Version getPackageVersionViaRequestingBundle(String packageName,
final Bundle requestingBundle, Version remoteVersion) {
Version result = null;
// First check the requesting bundle for the desired export package
// capability
BundleRevision requestingBundleRevision = AccessController
.doPrivileged(new PrivilegedAction<BundleRevision>() {
public BundleRevision run() {
return requestingBundle.adapt(BundleRevision.class);
}
});
if (requestingBundleRevision != null) {
List<BundleCapability> requestingBundleCapabilities = requestingBundleRevision
.getDeclaredCapabilities(BundleRevision.PACKAGE_NAMESPACE);
for (BundleCapability requestingBundleCapability : requestingBundleCapabilities) {
Version candidate = getVersionForMatchingCapability(
packageName, requestingBundleCapability);
// If found, set our result
if (candidate != null) {
if (remoteVersion != null
&& candidate.equals(remoteVersion))
return candidate;
result = candidate;
}
}
}
// If not found in requestingBundle export package, then
// look in exported package that are wired to the requesting bundle
if (result == null) {
// look for wired exported packages
BundleWiring requestingBundleWiring = requestingBundle
.adapt(BundleWiring.class);
if (requestingBundleWiring != null) {
result = getPackageVersionForMatchingWire(
packageName,
requestingBundleWiring
.getRequiredWires(BundleRevision.PACKAGE_NAMESPACE),
BundleRevision.PACKAGE_NAMESPACE);
// If not found in wired exported packages, then look
// in wired require bundles
if (result == null)
result = getPackageVersionForMatchingWire(
packageName,
requestingBundleWiring
.getRequiredWires(BundleRevision.BUNDLE_NAMESPACE),
BundleRevision.BUNDLE_NAMESPACE);
}
}
return result;
}
private IRemoteServiceReference selectRemoteServiceReference(
Collection<IRemoteServiceReference> rsRefs, ID targetID,
ID[] idFilter, Collection<String> interfaces, String rsFilter,
IRemoteServiceContainer rsContainer) {
if (rsRefs.size() == 0)
return null;
if (rsRefs.size() > 1) {
logWarning("selectRemoteServiceReference", "rsRefs=" + rsRefs //$NON-NLS-1$ //$NON-NLS-2$
+ ",targetID=" + targetID + ",idFilter=" + idFilter //$NON-NLS-1$ //$NON-NLS-2$
+ ",interfaces=" + interfaces + ",rsFilter=" + rsFilter //$NON-NLS-1$ //$NON-NLS-2$
+ ",rsContainer=" + rsContainer.getContainer().getID() //$NON-NLS-1$
+ " has " + rsRefs.size() //$NON-NLS-1$
+ " values. Selecting the first element"); //$NON-NLS-1$
}
return rsRefs.iterator().next();
}
private Map createProxyProperties(ID importContainerID, EndpointDescription endpointDescription,
IRemoteServiceReference rsReference, IRemoteService remoteService) {
Map resultProperties = new TreeMap<String, Object>(
String.CASE_INSENSITIVE_ORDER);
PropertiesUtil.copyNonReservedProperties(rsReference, resultProperties);
PropertiesUtil.copyNonReservedProperties(
endpointDescription.getProperties(), resultProperties);
// remove OBJECTCLASS
resultProperties
.remove(org.eclipse.ecf.remoteservice.Constants.OBJECTCLASS);
// remove remote service id
resultProperties
.remove(org.eclipse.ecf.remoteservice.Constants.SERVICE_ID);
// Set intents if there are intents
Object intentsValue = PropertiesUtil
.convertToStringPlusValue(endpointDescription.getIntents());
if (intentsValue != null)
resultProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_INTENTS,
intentsValue);
// Set service.imported to IRemoteService unless
// SERVICE_IMPORTED_VALUETYPE is
// set
String serviceImportedType = (String) endpointDescription
.getProperties()
.get(org.eclipse.ecf.osgi.services.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_VALUETYPE);
if (serviceImportedType == null
|| serviceImportedType.equals(IRemoteService.class.getName()))
resultProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED,
remoteService);
else
resultProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED,
new Boolean(true));
String[] exporterSupportedConfigs = (String[]) endpointDescription
.getProperties()
.get(org.osgi.service.remoteserviceadmin.RemoteConstants.REMOTE_CONFIGS_SUPPORTED);
String[] importedConfigs = getImportedConfigs(importContainerID, exporterSupportedConfigs);
// Set service.imported.configs
resultProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS,
importedConfigs);
// Set endpoint.id
String endpointId = endpointDescription.getId();
resultProperties
.put(org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID,
endpointId);
return resultProperties;
}
private ExportRegistration exportService(
final ServiceReference serviceReference,
Map<String, ?> overridingProperties, String[] exportedInterfaces,
IRemoteServiceContainer rsContainer,
Map<String, Object> endpointDescriptionProperties) throws Exception {
// Create remote service properties
Map remoteServiceProperties = copyNonReservedProperties(
serviceReference, (Map<String, Object>) overridingProperties,
new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER));
IRemoteServiceContainerAdapter containerAdapter = rsContainer
.getContainerAdapter();
// create serializable dictionary from remote service properties
Dictionary rsp = PropertiesUtil.createSerializableDictionaryFromMap(remoteServiceProperties);
// Register remote service via ECF container adapter to create
// remote service registration
IRemoteServiceRegistration remoteRegistration = null;
if (containerAdapter instanceof IOSGiRemoteServiceContainerAdapter) {
IOSGiRemoteServiceContainerAdapter osgiContainerAdapter = (IOSGiRemoteServiceContainerAdapter) containerAdapter;
remoteRegistration = osgiContainerAdapter.registerRemoteService(
exportedInterfaces, serviceReference, rsp);
} else {
Object service = AccessController
.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return getClientBundleContext().getService(
serviceReference);
}
});
remoteRegistration = containerAdapter.registerRemoteService(
exportedInterfaces, service, rsp);
}
endpointDescriptionProperties.put(
org.eclipse.ecf.remoteservice.Constants.SERVICE_ID,
remoteRegistration.getID().getContainerRelativeID());
if (remoteRegistration instanceof IExtendedRemoteServiceRegistration) {
IExtendedRemoteServiceRegistration iersr = (IExtendedRemoteServiceRegistration) remoteRegistration;
Map<String, Object> extraProperties = iersr.getExtraProperties();
if (extraProperties != null)
endpointDescriptionProperties = PropertiesUtil.mergeProperties(endpointDescriptionProperties, extraProperties);
}
// Copy only serializable properties
endpointDescriptionProperties = PropertiesUtil.copySerializableProperties(endpointDescriptionProperties,
new TreeMap<String, Object>());
// Create ExportEndpoint/ExportRegistration
return new ExportRegistration(new ExportEndpoint(serviceReference,
new EndpointDescription(endpointDescriptionProperties), remoteRegistration,endpointDescriptionProperties));
}
private ImportRegistration importService(
final EndpointDescription endpointDescription,
IRemoteServiceContainer rsContainer) {
// Get interfaces from endpoint description
Collection<String> interfaces = endpointDescription.getInterfaces();
Assert.isNotNull(interfaces);
Assert.isTrue(interfaces.size() > 0);
// Get ECF endpoint ID...if this throws IDCreateException (because the
// local system does not have
// namespace for creating ID, or no namespace is present in
// endpointDescription or endpoint id,
// then it will be caught by the caller
ID endpointContainerID = endpointDescription.getContainerID();
Assert.isNotNull(endpointContainerID);
// Get connect target ID. May be null
ID tID = endpointDescription.getConnectTargetID();
if (tID == null)
tID = endpointContainerID;
final ID targetID = tID;
// Get idFilter...also may be null
final ID[] idFilter = getIDFilter(endpointDescription,
targetID);
// Get remote service filter
final String rsFilter = getRemoteServiceFilter(endpointDescription);
// IRemoteServiceReferences from query
Collection<IRemoteServiceReference> rsRefs = new ArrayList<IRemoteServiceReference>();
// Get IRemoteServiceContainerAdapter
final IRemoteServiceContainerAdapter containerAdapter = rsContainer
.getContainerAdapter();
// rsContainerID
ID rsContainerID = rsContainer.getContainer().getID();
try {
final IRSAConsumerContainerAdapter cca = (IRSAConsumerContainerAdapter) containerAdapter.getAdapter(IRSAConsumerContainerAdapter.class);
// Get first interface name for service reference
// lookup
final String intf = interfaces.iterator().next();
// Get/lookup remote service references
IRemoteServiceReference[] refs = AccessController
.doPrivileged(new PrivilegedExceptionAction<IRemoteServiceReference[]>() {
public IRemoteServiceReference[] run()
throws ContainerConnectException,
InvalidSyntaxException {
// If the RSAConsumerContainerAdapter is present, use it
if (cca != null)
// Call importEndpoint if the IRSAConsumerContainerAdapter is present
return cca.importEndpoint(endpointDescription.getProperties());
// Otherwise use the 'old' container adapter
else return containerAdapter.getRemoteServiceReferences(
targetID, idFilter, intf, rsFilter);
}
});
if (refs == null) {
logWarning("doImportService", //$NON-NLS-1$
"getRemoteServiceReferences return null for targetID=" //$NON-NLS-1$
+ targetID + ",idFilter=" + idFilter //$NON-NLS-1$
+ ",intf=" + intf + ",rsFilter=" + rsFilter //$NON-NLS-1$ //$NON-NLS-2$
+ " on rsContainerID=" + rsContainerID); //$NON-NLS-1$
} else
for (int i = 0; i < refs.length; i++)
rsRefs.add(refs[i]);
// If there are several refs resulting (should not be)
// we select the one to use
IRemoteServiceReference selectedRsReference = selectRemoteServiceReference(
rsRefs, targetID, idFilter, interfaces, rsFilter,
rsContainer);
// If none found, we obviously can't continue
if (selectedRsReference == null)
throw new RemoteReferenceNotFoundException(targetID, idFilter,
interfaces, rsFilter);
return new ImportRegistration(createAndRegisterProxy(
endpointDescription, rsContainer, selectedRsReference));
} catch (PrivilegedActionException e) {
logError(
"importService", "selectRemoteServiceReference returned null for rsRefs=" //$NON-NLS-1$ //$NON-NLS-2$
+ rsRefs + ",targetID=" + targetID //$NON-NLS-1$
+ ",idFilter=" + idFilter + ",interfaces=" //$NON-NLS-1$ //$NON-NLS-2$
+ interfaces + ",rsFilter=" + rsFilter //$NON-NLS-1$
+ ",rsContainerID=" + rsContainerID, e.getException()); //$NON-NLS-1$
return new ImportRegistration(endpointDescription, e.getException());
} catch (Exception e) {
logError(
"importService", "selectRemoteServiceReference returned null for rsRefs=" //$NON-NLS-1$ //$NON-NLS-2$
+ rsRefs + ",targetID=" + targetID //$NON-NLS-1$
+ ",idFilter=" + idFilter + ",interfaces=" //$NON-NLS-1$ //$NON-NLS-2$
+ interfaces + ",rsFilter=" + rsFilter //$NON-NLS-1$
+ ",rsContainerID=" + rsContainerID, e); //$NON-NLS-1$
return new ImportRegistration(endpointDescription, e);
}
}
public void close() {
trace("close", "closing importedRegistrations=" + importedRegistrations //$NON-NLS-1$ //$NON-NLS-2$
+ " exportedRegistrations=" + exportedRegistrations); //$NON-NLS-1$
// close any imported and exported registrations
List<org.osgi.service.remoteserviceadmin.ImportRegistration> toClose = null;
synchronized (importedRegistrations) {
toClose = new ArrayList<org.osgi.service.remoteserviceadmin.ImportRegistration>(localImportedRegistrations);
}
for (org.osgi.service.remoteserviceadmin.ImportRegistration reg : toClose)
reg.close();
List<org.osgi.service.remoteserviceadmin.ExportRegistration> toClose1 = null;
synchronized (localExportedRegistrations) {
toClose1 = new ArrayList<org.osgi.service.remoteserviceadmin.ExportRegistration>(localExportedRegistrations);
}
for (org.osgi.service.remoteserviceadmin.ExportRegistration reg1 : toClose1)
reg1.close();
this.localExportedRegistrations.clear();
this.localImportedRegistrations.clear();
synchronized (remoteServiceAdminListenerTrackerLock) {
if (remoteServiceAdminListenerTracker != null) {
remoteServiceAdminListenerTracker.close();
remoteServiceAdminListenerTracker = null;
}
}
synchronized (eventAdminTrackerLock) {
if (eventAdminTracker != null) {
eventAdminTracker.close();
eventAdminTracker = null;
}
}
synchronized (proxyClassLoaders) {
proxyClassLoaders.clear();
}
synchronized (consumerContainerSelectorTrackerLock) {
if (consumerContainerSelectorTracker != null) {
consumerContainerSelectorTracker.close();
consumerContainerSelectorTracker = null;
}
}
if (defaultConsumerContainerSelector != null) {
defaultConsumerContainerSelector.close();
defaultConsumerContainerSelector = null;
}
synchronized (hostContainerSelectorTrackerLock) {
if (hostContainerSelectorTracker != null) {
hostContainerSelectorTracker.close();
hostContainerSelectorTracker = null;
}
}
if (defaultHostContainerSelector != null) {
defaultHostContainerSelector.close();
defaultHostContainerSelector = null;
}
if (defaultHostContainerSelectorRegistration != null) {
defaultHostContainerSelectorRegistration.unregister();
defaultHostContainerSelectorRegistration = null;
}
if (defaultHostContainerSelector != null) {
defaultHostContainerSelector.close();
defaultHostContainerSelector = null;
}
if (defaultConsumerContainerSelectorRegistration != null) {
defaultConsumerContainerSelectorRegistration.unregister();
defaultConsumerContainerSelectorRegistration = null;
}
if (defaultConsumerContainerSelector != null) {
defaultConsumerContainerSelector.close();
defaultConsumerContainerSelector = null;
}
if (eventListenerHookRegistration != null) {
eventListenerHookRegistration.unregister();
eventListenerHookRegistration = null;
}
}
private ImportEndpoint findImportEndpoint(EndpointDescription ed) {
for (org.osgi.service.remoteserviceadmin.ImportRegistration reg : importedRegistrations) {
if (reg instanceof ImportRegistration) {
ImportEndpoint endpoint = ((ImportRegistration) reg)
.getImportEndpoint(ed);
if (endpoint != null)
return endpoint;
}
}
return null;
}
private void unimportService(IRemoteServiceID remoteServiceID) {
List<ImportRegistration> removedRegistrations = new ArrayList<ImportRegistration>();
synchronized (importedRegistrations) {
for (Iterator<org.osgi.service.remoteserviceadmin.ImportRegistration> i = importedRegistrations
.iterator(); i.hasNext();) {
org.osgi.service.remoteserviceadmin.ImportRegistration iReg = i
.next();
if (iReg instanceof ImportRegistration) {
ImportRegistration importRegistration = (ImportRegistration) iReg;
if (importRegistration.match(remoteServiceID))
removedRegistrations.add(importRegistration);
}
}
}
// Now close all of them
for (ImportRegistration removedReg : removedRegistrations) {
trace("unimportService", "closing importRegistration=" + removedReg); //$NON-NLS-1$ //$NON-NLS-2$
removedReg.close();
}
}
class RemoteServiceListener implements IRemoteServiceListener {
public void handleServiceEvent(IRemoteServiceEvent event) {
if (event instanceof IRemoteServiceUnregisteredEvent)
unimportService(event.getReference().getID());
}
}
private void trace(String methodName, String message) {
LogUtility.trace(methodName, DebugOptions.REMOTE_SERVICE_ADMIN,
this.getClass(), message);
}
private void logWarning(String methodName, String message) {
LogUtility.logWarning(methodName, DebugOptions.REMOTE_SERVICE_ADMIN,
this.getClass(), message);
}
private void logError(String methodName, String message, Throwable t) {
LogUtility.logError(methodName, DebugOptions.REMOTE_SERVICE_ADMIN,
this.getClass(), message, t);
}
private void logError(String methodName, String message) {
logError(methodName, message, (Throwable) null);
}
public class RemoteServiceAdminEvent extends
org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent {
private ID containerID;
private EndpointDescription endpointDescription;
public RemoteServiceAdminEvent(
ID containerID,
int type,
Bundle source,
org.osgi.service.remoteserviceadmin.ExportReference exportReference,
Throwable exception, EndpointDescription endpointDescription) {
super(type, source, exportReference, exception);
this.containerID = containerID;
this.endpointDescription = endpointDescription;
}
public RemoteServiceAdminEvent(
ID containerID,
int type,
Bundle source,
org.osgi.service.remoteserviceadmin.ImportReference importReference,
Throwable exception, EndpointDescription endpointDescription) {
super(type, source, importReference, exception);
this.containerID = containerID;
this.endpointDescription = endpointDescription;
}
/**
* @since 3.0
*/
public EndpointDescription getEndpointDescription() {
return endpointDescription;
}
public ID getContainerID() {
return containerID;
}
public String toString() {
return "RemoteServiceAdminEvent[containerID=" + containerID //$NON-NLS-1$
+ ", getType()=" + getType() + ", getSource()=" + getSource() //$NON-NLS-1$ //$NON-NLS-2$
+ ", getException()=" + getException() //$NON-NLS-1$
+ ", getImportReference()=" + getImportReference() //$NON-NLS-1$
+ ", getExportReference()=" + getExportReference() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
}
}