blob: 26aee7907191c1abed144ec304cbb6c64936960f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 EclipseSource 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:
* EclipseSource - initial API and implementation
******************************************************************************/
package org.eclipse.ecf.internal.osgi.services.discovery;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.identity.IDCreateException;
import org.eclipse.ecf.core.identity.Namespace;
import org.eclipse.ecf.core.util.ECFRuntimeException;
import org.eclipse.ecf.core.util.Trace;
import org.eclipse.ecf.discovery.IDiscoveryAdvertiser;
import org.eclipse.ecf.discovery.IServiceEvent;
import org.eclipse.ecf.discovery.IServiceInfo;
import org.eclipse.ecf.discovery.IServiceListener;
import org.eclipse.ecf.discovery.IServiceProperties;
import org.eclipse.ecf.discovery.ServiceInfo;
import org.eclipse.ecf.discovery.ServiceProperties;
import org.eclipse.ecf.discovery.identity.IServiceID;
import org.eclipse.ecf.discovery.identity.IServiceTypeID;
import org.eclipse.ecf.discovery.identity.ServiceIDFactory;
import org.eclipse.ecf.osgi.services.discovery.IHostDiscoveryListener;
import org.eclipse.ecf.osgi.services.discovery.IProxyDiscoveryListener;
import org.eclipse.ecf.osgi.services.discovery.RemoteServicePublication;
import org.eclipse.ecf.remoteservice.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.discovery.DiscoveredServiceNotification;
import org.osgi.service.discovery.DiscoveredServiceTracker;
import org.osgi.service.discovery.Discovery;
import org.osgi.service.discovery.ServicePublication;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
public class ServicePublicationHandler implements ServiceTrackerCustomizer,
Discovery, IServiceListener {
private IDiscoveryAdvertiser advertiser;
private Map serviceInfos = Collections.synchronizedMap(new HashMap());
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ecf.discovery.IServiceListener#serviceDiscovered(org.eclipse
* .ecf.discovery.IServiceEvent)
*/
public void serviceDiscovered(IServiceEvent event) {
IServiceInfo serviceInfo = event.getServiceInfo();
IServiceID serviceID = serviceInfo.getServiceID();
trace("handleOSGIServiceDiscovered", " serviceInfo=" + serviceInfo); //$NON-NLS-1$ //$NON-NLS-2$
if (matchServiceID(serviceID)) {
fireProxyDiscoveredUndiscovered(serviceInfo, true);
trace(
"handleOSGIServiceDiscovered matched", " serviceInfo=" + serviceInfo); //$NON-NLS-1$ //$NON-NLS-2$
DiscoveredServiceTracker[] discoveredTrackers = findMatchingDiscoveredServiceTrackers(serviceInfo);
notifyDiscoveredServiceTrackers(discoveredTrackers, serviceInfo,
true);
}
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.ecf.discovery.IServiceListener#serviceUndiscovered(org.eclipse
* .ecf.discovery.IServiceEvent)
*/
public void serviceUndiscovered(IServiceEvent event) {
IServiceInfo serviceInfo = event.getServiceInfo();
IServiceID serviceID = serviceInfo.getServiceID();
if (matchServiceID(serviceID)) {
fireProxyDiscoveredUndiscovered(serviceInfo, false);
trace(
"handleOSGIServiceUndiscovered", " serviceInfo=" + serviceInfo); //$NON-NLS-1$ //$NON-NLS-2$
DiscoveredServiceTracker[] discoveredTrackers = findMatchingDiscoveredServiceTrackers(serviceInfo);
notifyDiscoveredServiceTrackers(discoveredTrackers, serviceInfo,
false);
}
}
private void notifyDiscoveredServiceTrackers(
DiscoveredServiceTracker[] discoveredTrackers,
IServiceInfo serviceInfo, boolean available) {
if (discoveredTrackers != null) {
for (int i = 0; i < discoveredTrackers.length; i++) {
discoveredTrackers[i]
.serviceChanged(new DiscoveredServiceNotificationImpl(
(available ? DiscoveredServiceNotification.AVAILABLE
: DiscoveredServiceNotification.UNAVAILABLE),
serviceInfo));
}
}
}
private DiscoveredServiceTracker[] findMatchingDiscoveredServiceTrackers(
IServiceInfo serviceInfo) {
ServiceReference[] sourceTrackers = Activator.getDefault()
.getDiscoveredServiceTrackerReferences();
if (sourceTrackers == null)
return null;
List matchingTrackers = new ArrayList();
for (int i = 0; i < sourceTrackers.length; i++) {
if (matchWithDiscoveredServiceInfo(sourceTrackers[i], serviceInfo))
matchingTrackers.add(Activator.getDefault().getContext()
.getService(sourceTrackers[i]));
}
return (DiscoveredServiceTracker[]) matchingTrackers
.toArray(new DiscoveredServiceTracker[] {});
}
private boolean matchWithDiscoveredServiceInfo(
ServiceReference serviceReference, IServiceInfo serviceInfo) {
// TODO Auto-generated method stub
// XXX for now match everything. See RFC119
return true;
}
private boolean matchServiceID(IServiceID serviceId) {
if (Arrays.asList(serviceId.getServiceTypeID().getServices()).contains(
RemoteServicePublication.SERVICE_TYPE))
return true;
return false;
}
private IServiceInfo addServiceInfo(ServiceReference sr, IServiceInfo si) {
return (IServiceInfo) serviceInfos.put(sr, si);
}
private IServiceInfo removeServiceInfo(ServiceReference sr) {
return (IServiceInfo) serviceInfos.remove(sr);
}
/*
* (non-Javadoc)
*
* @see
* org.osgi.util.tracker.ServiceTrackerCustomizer#addingService(org.osgi
* .framework.ServiceReference)
*/
public Object addingService(ServiceReference reference) {
handleServicePublication(reference);
return Activator.getDefault().getContext().getService(reference);
}
/**
* @param reference
*/
private void handleServicePublication(ServiceReference reference) {
// Get required service RFC 119 property "service.interface", which
// should be a
// Collection of Strings
Collection svcInterfaces = ServicePropertyUtils.getCollectionProperty(
reference, ServicePublication.SERVICE_INTERFACE_NAME);
// If it's not there, then we ignore this ServicePublication and return
if (svcInterfaces == null) {
logError(
"handleServicePublication", //$NON-NLS-1$
"ignoring " //$NON-NLS-1$
+ reference
+ ". ServicePublication.SERVICE_INTERFACE_NAME not set", //$NON-NLS-1$
null);
return;
}
IServiceProperties discoveryServiceProperties = new ServiceProperties();
discoveryServiceProperties.setPropertyString(
ServicePublication.SERVICE_INTERFACE_NAME, ServicePropertyUtils
.createStringFromCollection(svcInterfaces));
// We also use the optional RFC 119 property PROP_KEY_SERVICE_PROPERTIES
Map servicePublicationServiceProperties = ServicePropertyUtils
.getMapProperty(reference,
ServicePublication.SERVICE_PROPERTIES);
// Add them
if (servicePublicationServiceProperties != null)
addPropertiesToDiscoveryServiceProperties(
discoveryServiceProperties,
servicePublicationServiceProperties);
// See EventHookImpl.getServicePublicationProperties()
// Get and then serialize and set
// RemoteServicePublication.ENDPOINT_CONTAINERID
ID endpointContainerID = (ID) reference
.getProperty(RemoteServicePublication.ENDPOINT_CONTAINERID);
// This is required for ecf endpoints so if it's not found then it's an
// error
if (endpointContainerID == null) {
logError(
"handleServicePublication", //$NON-NLS-1$
"ignoring " //$NON-NLS-1$
+ reference
+ ". RemoteServicePublication.ENDPOINT_CONTAINERID not set", //$NON-NLS-1$
null);
return;
}
// Add endpoint container id.toExternalForm().getBytes...so AS byte []
discoveryServiceProperties.setPropertyBytes(
RemoteServicePublication.ENDPOINT_CONTAINERID,
endpointContainerID.toExternalForm().getBytes());
// Add endpoint container id namespace name
String endpointNamespace = endpointContainerID.getNamespace().getName();
discoveryServiceProperties.setPropertyString(
RemoteServicePublication.ENDPOINT_CONTAINERID_NAMESPACE,
endpointNamespace);
// See EventHookImpl.getServicePublicationProperties()
// Get and then serialize and set
// RemoteServicePublication.TARGET_CONTAINERID
ID targetContainerID = (ID) reference
.getProperty(RemoteServicePublication.TARGET_CONTAINERID);
// the target ID is optional, so only add it if it's been added
// via the EventHookImpl
if (targetContainerID != null) {
// Add endpoint container id.toExternalForm().getBytes...so AS byte
// []
discoveryServiceProperties.setPropertyBytes(
RemoteServicePublication.TARGET_CONTAINERID,
targetContainerID.toExternalForm().getBytes());
String targetNamespace = targetContainerID.getNamespace().getName();
discoveryServiceProperties.setPropertyString(
RemoteServicePublication.TARGET_CONTAINERID_NAMESPACE,
targetNamespace);
}
// add remote service namespace
String rsnamespace = ServicePropertyUtils.getStringProperty(reference,
Constants.SERVICE_NAMESPACE);
if (rsnamespace != null)
discoveryServiceProperties.setPropertyString(
Constants.SERVICE_NAMESPACE, rsnamespace);
// and remote service id
byte[] remoteServiceIDAsBytes = (byte[]) reference
.getProperty(Constants.SERVICE_ID);
if (remoteServiceIDAsBytes != null)
discoveryServiceProperties.setPropertyBytes(Constants.SERVICE_ID,
remoteServiceIDAsBytes);
IDiscoveryAdvertiser advertiser2 = getAdvertiser();
if (advertiser2 == null) {
logError(
"handleServicePublication", //$NON-NLS-1$
"ignoring " //$NON-NLS-1$
+ reference
+ ". No IDiscoveryAdvertiser available to handle this publication", //$NON-NLS-1$
null);
return;
}
Namespace advertiserNamespace = advertiser2.getServicesNamespace();
IServiceInfo svcInfo = null;
try {
IServiceTypeID serviceTypeID = createServiceTypeID(
servicePublicationServiceProperties, advertiserNamespace);
URI uri = createURI(endpointContainerID);
String serviceName = getPropertyWithDefault(
servicePublicationServiceProperties,
RemoteServicePublication.SERVICE_NAME,
(RemoteServicePublication.DEFAULT_SERVICE_NAME_PREFIX + new String(
remoteServiceIDAsBytes)));
svcInfo = new ServiceInfo(uri, serviceName, serviceTypeID,
discoveryServiceProperties);
} catch (IDCreateException e) {
logError("handleServicePublication", //$NON-NLS-1$
"Exception creating serviceID", e); //$NON-NLS-1$
return;
} catch (URISyntaxException e) {
logError("handleServicePublication", "Exception creating URI", e); //$NON-NLS-1$ //$NON-NLS-2$
return;
}
fireHostPublishUnpublish(reference, svcInfo, true);
synchronized (serviceInfos) {
try {
trace("publishService", "publishing serviceReference=" //$NON-NLS-1$ //$NON-NLS-2$
+ reference + ", svcInfo=" + svcInfo); //$NON-NLS-1$
advertiser2.registerService(svcInfo);
addServiceInfo(reference, svcInfo);
} catch (ECFRuntimeException e) {
logError("publishService", "cannot register service", e); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
private void fireHostPublishUnpublish(final ServiceReference reference,
final IServiceInfo serviceInfo, final boolean publish) {
Activator activator = Activator.getDefault();
if (activator != null) {
IHostDiscoveryListener[] listeners = activator
.getHostPublicationListeners();
if (listeners != null) {
for (int i = 0; i < listeners.length; i++) {
final IHostDiscoveryListener l = listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
logError(
"fireHostPublishUnpublish",
"Exception calling host discovery listener",
exception);
}
public void run() throws Exception {
if (publish)
l.publish(reference, serviceInfo);
else
l.unpublish(reference, serviceInfo);
}
});
}
}
}
}
private void fireProxyDiscoveredUndiscovered(
final IServiceInfo serviceInfo, final boolean discovered) {
Activator activator = Activator.getDefault();
if (activator != null) {
IProxyDiscoveryListener[] listeners = activator
.getProxyDiscoveredListeners();
if (listeners != null) {
for (int i = 0; i < listeners.length; i++) {
final IProxyDiscoveryListener l = listeners[i];
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
logError(
"fireProxyDiscoveredUndiscovered",
"Exception calling proxy discovery listener",
exception);
}
public void run() throws Exception {
if (discovered)
l.discovered(serviceInfo);
else
l.undiscovered(serviceInfo);
}
});
}
}
}
}
private void logError(String method, String message, Throwable exception) {
LogUtility.logError(method, message, this.getClass(), exception);
}
private URI createURI(ID endpointContainerID) throws URISyntaxException {
boolean done = false;
URI uri = null;
String str = endpointContainerID.getName();
while (!done) {
try {
uri = new URI(str);
if (!uri.isOpaque()) {
done = true;
} else {
str = uri.getRawSchemeSpecificPart();
}
} catch (URISyntaxException e) {
done = true;
}
}
String scheme = RemoteServicePublication.SERVICE_TYPE;
int port = 32565;
if (uri != null) {
port = uri.getPort();
if (port == -1)
port = 32565;
}
String host = null;
try {
host = InetAddress.getLocalHost().getHostAddress();
} catch (Exception e) {
host = "localhost"; //$NON-NLS-1$
}
return new URI(scheme, null, host, port, null, null, null);
}
private void addPropertiesToDiscoveryServiceProperties(
IServiceProperties discoveryServiceProperties,
Map servicePublicationServiceProperties) {
for (Iterator i = servicePublicationServiceProperties.keySet()
.iterator(); i.hasNext();) {
Object key = i.next();
if (!(key instanceof String)) {
trace("addPropertiesToDiscoveryServiceProperties", //$NON-NLS-1$
"skipping non-string key " + key); //$NON-NLS-1$
continue;
}
String keyStr = (String) key;
Object val = servicePublicationServiceProperties.get(keyStr);
if (val instanceof String) {
discoveryServiceProperties.setPropertyString(keyStr,
(String) val);
} else if (val instanceof byte[]) {
discoveryServiceProperties.setPropertyBytes(keyStr,
(byte[]) val);
} else if (val instanceof Serializable) {
discoveryServiceProperties.setProperty(keyStr, val);
}
}
}
private synchronized IDiscoveryAdvertiser getAdvertiser() {
try {
if (advertiser == null) {
advertiser = Activator.getDefault().getAdvertiser();
}
} catch (InterruptedException e) {
logError("getAdvertiser", "Cannot get IDiscoveryAdvertiser", e); //$NON-NLS-1$ //$NON-NLS-2$
}
return advertiser;
}
private String getPropertyWithDefault(Map properties, String key, String def) {
if (properties == null)
return def;
String val = (String) properties.get(key);
return (val == null) ? def : val;
}
protected IServiceTypeID createServiceTypeID(
Map servicePublicationProperties, Namespace aNamespace)
throws IDCreateException {
String namingAuthority = getPropertyWithDefault(
servicePublicationProperties,
RemoteServicePublication.NAMING_AUTHORITY,
IServiceTypeID.DEFAULT_NA);
String scope = getPropertyWithDefault(servicePublicationProperties,
RemoteServicePublication.SCOPE, IServiceTypeID.DEFAULT_SCOPE[0]);
String protocol = getPropertyWithDefault(servicePublicationProperties,
RemoteServicePublication.SERVICE_PROTOCOL,
IServiceTypeID.DEFAULT_PROTO[0]);
return ServiceIDFactory.getDefault().createServiceTypeID(aNamespace,
new String[] { RemoteServicePublication.SERVICE_TYPE },
new String[] { scope }, new String[] { protocol },
namingAuthority);
}
/*
* (non-Javadoc)
*
* @see
* org.osgi.util.tracker.ServiceTrackerCustomizer#modifiedService(org.osgi
* .framework.ServiceReference, java.lang.Object)
*/
public void modifiedService(ServiceReference reference, Object service) {
unpublishService(reference);
handleServicePublication(reference);
}
/*
* (non-Javadoc)
*
* @see
* org.osgi.util.tracker.ServiceTrackerCustomizer#removedService(org.osgi
* .framework.ServiceReference, java.lang.Object)
*/
public void removedService(ServiceReference reference, Object service) {
unpublishService(reference);
}
private void unpublishService(ServiceReference reference) {
IServiceInfo svcInfo = null;
synchronized (serviceInfos) {
try {
svcInfo = removeServiceInfo(reference);
if (svcInfo != null)
getAdvertiser().unregisterService(svcInfo);
} catch (ECFRuntimeException e) {
logError("publishService", "Cannot unregister serviceInfo=" //$NON-NLS-1$ //$NON-NLS-2$
+ svcInfo, e);
}
}
if (svcInfo != null)
fireHostPublishUnpublish(reference, svcInfo, false);
}
protected void trace(String methodName, String message) {
Trace.trace(Activator.PLUGIN_ID, DebugOptions.SVCPUBHANDLERDEBUG, this
.getClass(), methodName, message);
}
public void dispose() {
if (advertiser != null) {
for (Iterator i = serviceInfos.keySet().iterator(); i.hasNext();) {
ServiceReference sr = (ServiceReference) i.next();
unpublishService(sr);
}
serviceInfos.clear();
advertiser = null;
}
}
}