/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl;
import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.tuscany.sca.common.java.collection.CollectionMap;
import org.apache.tuscany.sca.core.LifeCycleListener;
import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointMatcher.ImportAction;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.service.ListenerHook;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.service.remoteserviceadmin.EndpointListener;
import org.osgi.service.remoteserviceadmin.ExportRegistration;
import org.osgi.service.remoteserviceadmin.ImportRegistration;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* Implementation of Remote Controller
*/
public class TopologyManagerImpl implements ListenerHook, RemoteServiceAdminListener, EndpointListener,
ServiceTrackerCustomizer, LifeCycleListener /*, EventHook */{
private final static Logger logger = Logger.getLogger(TopologyManagerImpl.class.getName());
public final static String ENDPOINT_LOCAL = "service.local";
private BundleContext context;
private ServiceTracker remoteAdmins;
private volatile ServiceRegistration registration;
private ServiceRegistration endpointListener;
private ServiceTracker remotableServices;
private EndpointMatcher endpointMatcher;
private CollectionMap<ServiceReference, ExportRegistration> exportedServices =
new CollectionMap<ServiceReference, ExportRegistration>();
private CollectionMap<ImportKey, ImportRegistration> importedServices =
new CollectionMap<ImportKey, ImportRegistration>();
private Filter remotableServiceFilter;
public TopologyManagerImpl(BundleContext context) {
this.context = context;
this.endpointMatcher = new EndpointMatcher(context);
}
public void start() {
String filter =
"(& (!(" + SERVICE_IMPORTED
+ "=*)) ("
+ SERVICE_EXPORTED_INTERFACES
+ "=*) ("
+ SERVICE_EXPORTED_CONFIGS
+ "=org.osgi.sca) )";
try {
remotableServiceFilter = context.createFilter(filter);
} catch (InvalidSyntaxException e) {
// Ignore
}
endpointListener = context.registerService(EndpointListener.class.getName(), this, null);
remoteAdmins = new ServiceTracker(this.context, RemoteServiceAdmin.class.getName(), null);
remoteAdmins.open();
// DO NOT register EventHook.class.getName() as it cannot report existing services
String interfaceNames[] =
new String[] {ListenerHook.class.getName(), RemoteServiceAdminListener.class.getName()};
// The registration will trigger the added() method before registration is assigned
registration = context.registerService(interfaceNames, this, null);
remotableServices = new ServiceTracker(context, remotableServiceFilter, this);
remotableServices.open(true);
Thread thread = new Thread(new ImportTask());
thread.start();
}
public Object addingService(ServiceReference reference) {
exportService(reference);
return reference.getBundle().getBundleContext().getService(reference);
}
public void modifiedService(ServiceReference reference, Object service) {
unexportService(reference);
exportService(reference);
}
public void removedService(ServiceReference reference, Object service) {
unexportService(reference);
}
private void unexportService(ServiceReference reference) {
// Call remote admin to unexport the service
Collection<ExportRegistration> exportRegistrations = exportedServices.get(reference);
if (exportRegistrations != null) {
for (Iterator<ExportRegistration> i = exportRegistrations.iterator(); i.hasNext();) {
ExportRegistration exported = i.next();
exported.close();
i.remove();
}
}
}
private void exportService(ServiceReference reference) {
// Call remote admin to export the service
Object[] admins = remoteAdmins.getServices();
if (admins == null) {
// Ignore
logger.warning("No RemoteAdmin services are available.");
} else {
for (Object ra : admins) {
RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
Collection<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference, null);
if (exportRegistrations != null && !exportRegistrations.isEmpty()) {
exportedServices.putValues(reference, exportRegistrations);
}
}
}
}
/**
* @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection)
*/
public void added(Collection listeners) {
try {
synchronized (endpointMatcher) {
Collection<String> oldFilters = endpointMatcher.getFilters();
Collection<String> newFilters = endpointMatcher.added(listeners);
if (!OSGiHelper.getAddedItems(oldFilters, newFilters).isEmpty()) {
updateEndpointListenerScope(newFilters);
}
}
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
if (e instanceof Error) {
throw (Error)e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
// Should not happen
throw new RuntimeException(e);
}
}
}
private void updateEndpointListenerScope(Collection<String> filters) {
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(ENDPOINT_LISTENER_SCOPE, filters);
endpointListener.setProperties(props);
}
/**
* @see org.osgi.framework.hooks.service.ListenerHook#removed(java.util.Collection)
*/
public void removed(Collection listeners) {
try {
synchronized (endpointMatcher) {
Collection<String> oldFilters = endpointMatcher.getFilters();
Collection<String> newFilters = endpointMatcher.removed(listeners);
if (!OSGiHelper.getRemovedItems(oldFilters, newFilters).isEmpty()) {
updateEndpointListenerScope(newFilters);
}
}
} catch (Throwable e) {
logger.log(Level.SEVERE, e.getMessage(), e);
if (e instanceof Error) {
throw (Error)e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
// Should not happen
throw new RuntimeException(e);
}
}
}
/**
* @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener#remoteAdminEvent(org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent)
*/
public void remoteAdminEvent(RemoteServiceAdminEvent event) {
switch (event.getType()) {
case RemoteServiceAdminEvent.EXPORT_ERROR:
case RemoteServiceAdminEvent.EXPORT_REGISTRATION:
case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION:
case RemoteServiceAdminEvent.EXPORT_WARNING:
break;
case RemoteServiceAdminEvent.IMPORT_ERROR:
case RemoteServiceAdminEvent.IMPORT_REGISTRATION:
case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION:
case RemoteServiceAdminEvent.IMPORT_WARNING:
break;
}
}
/**
* @see org.osgi.remoteserviceadmin.EndpointListener#addEndpoint(org.osgi.service.remoteserviceadmin.EndpointDescription,
* java.lang.String)
*/
public void endpointAdded(EndpointDescription endpoint, String matchedFilter) {
endpointMatcher.added(endpoint, matchedFilter);
// importService(endpoint, matchedFilter);
}
/**
* @see org.osgi.remoteserviceadmin.EndpointListener#removeEndpoint(org.osgi.service.remoteserviceadmin.EndpointDescription)
*/
public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) {
endpointMatcher.removed(endpoint, matchedFilter);
// unimportService(endpoint);
}
private void importService(EndpointDescription endpoint, String matchedFilter) {
Object[] admins = remoteAdmins.getServices();
if (admins == null) {
logger.warning("No Remote Service Admin services are available.");
return;
}
CollectionMap<Class<?>, ListenerInfo> interfaceToListeners =
endpointMatcher.groupListeners(endpoint, matchedFilter);
for (Map.Entry<Class<?>, Collection<ListenerInfo>> e : interfaceToListeners.entrySet()) {
Class<?> interfaceClass = e.getKey();
Collection<ListenerInfo> listeners = e.getValue();
// Get a listener
ListenerInfo listener = listeners.iterator().next();
Bundle bundle = listener.getBundleContext().getBundle();
if (bundle.getBundleId() == 0L) {
// Skip system bundles
continue;
}
try {
Filter filter = listener.getBundleContext().createFilter(matchedFilter);
if (!filter.match(new Hashtable<String, Object>(endpoint.getProperties()))) {
continue;
}
} catch (InvalidSyntaxException ex) {
logger.log(Level.SEVERE, ex.getMessage(), ex);
continue;
}
Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
props.put(Bundle.class.getName(), bundle);
props.put(Constants.OBJECTCLASS, new String[] {interfaceClass.getName()});
EndpointDescription description = new EndpointDescription(props);
if (admins != null) {
for (Object ra : admins) {
RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra;
ImportRegistration importRegistration = remoteAdmin.importService(description);
if (importRegistration != null) {
importedServices.putValue(new ImportKey(description, listener), importRegistration);
}
}
}
}
}
private void unimportService(EndpointDescription endpoint, ListenerInfo listenerInfo) {
// Call remote admin to unimport the service
Collection<ImportRegistration> importRegistrations =
importedServices.get(new ImportKey(endpoint, listenerInfo));
if (importRegistrations != null) {
for (Iterator<ImportRegistration> i = importRegistrations.iterator(); i.hasNext();) {
ImportRegistration imported = i.next();
imported.close();
i.remove();
}
}
}
public void stop() {
remotableServices.close();
if (registration != null) {
try {
registration.unregister();
} catch (IllegalStateException e) {
// The service has been unregistered, ignore it
}
registration = null;
}
if (remoteAdmins != null) {
remoteAdmins.close();
remoteAdmins = null;
}
if (endpointMatcher != null) {
endpointMatcher.clear();
}
}
private class ImportTask implements Runnable {
public void run() {
while (registration != null) {
BlockingQueue<EndpointMatcher.ImportAction> queue = endpointMatcher.getImportQueue();
ImportAction action = null;
try {
action = queue.poll(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// Ignore
}
if (action != null) {
if (action.type == ImportAction.Type.Add) {
importService(action.endpointDescription, action.listenerInfo.getFilter());
} else if (action.type == ImportAction.Type.Remove) {
unimportService(action.endpointDescription, action.listenerInfo);
}
}
}
}
}
private static class ImportKey {
private EndpointDescription endpointDescription;
/**
* @param endpointDescription
* @param listenerInfo
*/
private ImportKey(EndpointDescription endpointDescription, ListenerInfo listenerInfo) {
super();
this.endpointDescription = endpointDescription;
this.listenerInfo = listenerInfo;
}
private ListenerInfo listenerInfo;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((endpointDescription == null) ? 0 : endpointDescription.hashCode());
result = prime * result + ((listenerInfo == null) ? 0 : listenerInfo.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ImportKey other = (ImportKey)obj;
if (endpointDescription == null) {
if (other.endpointDescription != null)
return false;
} else if (!endpointDescription.equals(other.endpointDescription))
return false;
if (listenerInfo == null) {
if (other.listenerInfo != null)
return false;
} else if (!listenerInfo.equals(other.listenerInfo))
return false;
return true;
}
}
}