![JAR search and dependency download from the Maven repository](/logo.png)
org.amdatu.remote.admin.http.RemoteServiceAdminImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.amdatu.remote.admin.http Show documentation
Show all versions of org.amdatu.remote.admin.http Show documentation
Amdatu Remote - Remote Service Admin (HTTP)
The newest version!
/*
* Licensed 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.amdatu.remote.admin.http;
import static org.amdatu.remote.ServiceUtil.getFrameworkUUID;
import static org.amdatu.remote.ServiceUtil.getStringPlusValue;
import static org.amdatu.remote.admin.http.HttpAdminConstants.CONFIGURATION_TYPE;
import static org.amdatu.remote.admin.http.HttpAdminConstants.ENDPOINT_URL;
import static org.amdatu.remote.admin.http.HttpAdminConstants.PASSBYVALYE_INTENT;
import static org.amdatu.remote.admin.http.HttpAdminConstants.SUPPORTED_INTENTS;
import static org.osgi.framework.Constants.OBJECTCLASS;
import static org.osgi.framework.Constants.SERVICE_ID;
import static org.osgi.service.remoteserviceadmin.EndpointPermission.EXPORT;
import static org.osgi.service.remoteserviceadmin.EndpointPermission.IMPORT;
import static org.osgi.service.remoteserviceadmin.EndpointPermission.READ;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_INTENTS;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_INTERFACES;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED_CONFIGS;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.SERVICE_INTENTS;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.EXPORT_REGISTRATION;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.EXPORT_UNREGISTRATION;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.EXPORT_UPDATE;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.IMPORT_REGISTRATION;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.IMPORT_UNREGISTRATION;
import static org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent.IMPORT_UPDATE;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import org.amdatu.remote.AbstractComponentDelegate;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.service.remoteserviceadmin.EndpointPermission;
import org.osgi.service.remoteserviceadmin.ExportReference;
import org.osgi.service.remoteserviceadmin.ExportRegistration;
import org.osgi.service.remoteserviceadmin.ImportReference;
import org.osgi.service.remoteserviceadmin.ImportRegistration;
import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
/**
* Remote Service Admin instance implementation.
*
* @author Amdatu Project Team
*/
public final class RemoteServiceAdminImpl extends AbstractComponentDelegate implements RemoteServiceAdmin {
private static final Set MY_SUPPORTED_INTENTS_SET = new HashSet(
Arrays.asList(SUPPORTED_INTENTS));
private final Map> m_exportedEndpoints =
new HashMap>();
private final Map> m_importedEndpoints =
new HashMap>();
private final RemoteServiceAdminFactory m_manager;
private final HttpAdminConfiguration m_configuration;
public RemoteServiceAdminImpl(RemoteServiceAdminFactory manager, HttpAdminConfiguration configuration) {
super(manager);
m_manager = manager;
m_configuration = configuration;
}
@Override
protected void startComponentDelegate() throws Exception {
}
@Override
protected void stopComponentDelegate() throws Exception {
synchronized (m_exportedEndpoints) {
for (Set exportedEndpoints : m_exportedEndpoints.values()) {
for (ExportedEndpointImpl exportedEndpoint : exportedEndpoints) {
exportedEndpoint.close();
}
}
}
synchronized (m_importedEndpoints) {
for (Set importedEndpoints : m_importedEndpoints.values()) {
for (ImportedEndpointImpl importedEndpoint : importedEndpoints) {
importedEndpoint.close();
}
}
}
}
@Override
public Collection exportService(final ServiceReference> reference,
final Map properties) {
final EndpointDescription endpoint = createEndpointDescription(reference, properties);
if (endpoint == null) {
return Collections.emptyList();
}
final Class>[] interfaces = loadEndpointInterfaces(reference, endpoint);
if (interfaces == null) {
return Collections.emptyList();
}
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(new EndpointPermission(endpoint, getFrameworkUUID(getBundleContext()),
EXPORT));
}
return AccessController.doPrivileged(new PrivilegedAction>() {
@Override
public Collection run() {
ExportedEndpointImpl exportedEndpoint = null;
synchronized (m_exportedEndpoints) {
Set exportedEndpoints = m_exportedEndpoints.get(endpoint);
if (exportedEndpoints == null) {
exportedEndpoints = new HashSet();
m_exportedEndpoints.put(endpoint, exportedEndpoints);
}
HttpServerEndpoint serverEndpoint =
getServerEndpointHandler().addEndpoint(reference, endpoint, interfaces);
exportedEndpoint =
new ExportedEndpointImpl(RemoteServiceAdminImpl.this, endpoint, reference, properties,
serverEndpoint);
exportedEndpoints.add(exportedEndpoint);
logDebug("Added exported endpoint: %s", exportedEndpoint);
}
m_manager.getEventsHandler().emitEvent(EXPORT_REGISTRATION, getBundleContext().getBundle(),
exportedEndpoint, exportedEndpoint.getException());
return Collections.singletonList((ExportRegistration) exportedEndpoint);
}
});
}
@Override
public ImportRegistration importService(final EndpointDescription endpoint) {
if (endpoint == null) {
logWarning("No valid endpoint specified. Ignoring...");
return null;
}
SecurityManager securityManager = System.getSecurityManager();
if (securityManager != null) {
securityManager.checkPermission(new EndpointPermission(endpoint,
getFrameworkUUID(getBundleContext()), IMPORT));
}
if (!endpoint.getConfigurationTypes().contains(CONFIGURATION_TYPE)) {
logInfo("No supported configuration type found. Not importing endpoint: %s", endpoint);
return null;
}
if (!hasAvailableInterfaces(endpoint)) {
logInfo("No available interfaces found. Not importing endpoint: %s", endpoint);
return null;
}
if (!hasValidServiceLocation(endpoint)) {
logInfo("No valid service location found. Not importing endpoint: %s", endpoint);
return null;
}
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public ImportRegistration run() {
ImportedEndpointImpl importedEndpoint = null;
synchronized (m_importedEndpoints) {
Set importedEndpoints = m_importedEndpoints.get(endpoint);
if (importedEndpoints == null) {
importedEndpoints = new HashSet();
m_importedEndpoints.put(endpoint, importedEndpoints);
}
importedEndpoint = new ImportedEndpointImpl(RemoteServiceAdminImpl.this, endpoint, m_configuration);
importedEndpoints.add(importedEndpoint);
logDebug("Added imported endpoint: %s", importedEndpoint);
}
getEventsHandler().emitEvent(IMPORT_REGISTRATION, getBundleContext().getBundle(), importedEndpoint,
importedEndpoint.getException());
return importedEndpoint;
}
});
}
@Override
public Collection getImportedEndpoints() {
return Collections.unmodifiableCollection(m_manager.getAllImportedEndpoints());
}
@Override
public Collection getExportedServices() {
return Collections.unmodifiableCollection(m_manager.getAllExportedEndpoints());
}
void addExportedEndpoints(Collection collection) {
SecurityManager securityManager = System.getSecurityManager();
String frameworkUUID = getFrameworkUUID(getBundleContext());
synchronized (m_exportedEndpoints) {
for (Entry> entry : m_exportedEndpoints.entrySet()) {
try {
if (securityManager != null) {
securityManager.checkPermission(new EndpointPermission(entry.getKey(), frameworkUUID, READ));
}
collection.addAll(entry.getValue());
}
catch (SecurityException e) {}
}
}
}
void addImportedEndpoints(Collection collection) {
SecurityManager securityManager = System.getSecurityManager();
String frameworkUUID = getFrameworkUUID(getBundleContext());
synchronized (m_importedEndpoints) {
for (Entry> entry : m_importedEndpoints.entrySet()) {
try {
if (securityManager != null) {
securityManager.checkPermission(new EndpointPermission(entry.getKey(), frameworkUUID, READ));
}
collection.addAll(entry.getValue());
}
catch (SecurityException e) {}
}
}
}
void exportedEndpointUpdated(final ExportedEndpointImpl exportedEndpoint) {
EndpointDescription endpoint = exportedEndpoint.getExportedEndpoint(true);
synchronized (m_exportedEndpoints) {
Set exportedEndpoints = m_exportedEndpoints.get(endpoint);
if (exportedEndpoints != null) {
exportedEndpoints.remove(exportedEndpoint);
exportedEndpoints.add(exportedEndpoint);
}
}
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
getEventsHandler().emitEvent(EXPORT_UPDATE, getBundleContext().getBundle(), exportedEndpoint,
exportedEndpoint.getException(true));
return null;
}
});
}
void exportedEndpointClosed(final ExportedEndpointImpl exportedEndpoint) {
EndpointDescription endpoint = exportedEndpoint.getExportedEndpoint(true);
synchronized (m_exportedEndpoints) {
Set exportedEndpoints = m_exportedEndpoints.get(endpoint);
if (exportedEndpoints != null) {
exportedEndpoints.remove(exportedEndpoint);
if (exportedEndpoints.isEmpty()) {
m_exportedEndpoints.remove(endpoint);
}
}
}
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
getEventsHandler().emitEvent(EXPORT_UNREGISTRATION, getBundleContext().getBundle(),
exportedEndpoint, exportedEndpoint.getException(true));
return null;
}
});
}
void importedEndpointUpdated(final ImportedEndpointImpl importedEndpoint) {
EndpointDescription endpoint = importedEndpoint.getImportedEndpoint(true);
synchronized (m_importedEndpoints) {
Set importedEndpoints = m_importedEndpoints.get(endpoint);
if (importedEndpoints != null) {
importedEndpoints.remove(importedEndpoint);
importedEndpoints.add(importedEndpoint);
}
}
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
getEventsHandler().emitEvent(IMPORT_UPDATE, getBundleContext().getBundle(), importedEndpoint,
importedEndpoint.getException(true));
return null;
}
});
}
void importedEndpointClosed(final ImportedEndpointImpl importedEndpoint) {
EndpointDescription endpoint = importedEndpoint.getImportedEndpoint(true);
synchronized (m_importedEndpoints) {
Set importedEndpoints = m_importedEndpoints.get(endpoint);
if (importedEndpoints != null) {
importedEndpoints.remove(importedEndpoint);
if (importedEndpoints.isEmpty()) {
m_importedEndpoints.remove(endpoint);
}
}
}
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
getEventsHandler().emitEvent(IMPORT_UNREGISTRATION, getBundleContext().getBundle(),
importedEndpoint, importedEndpoint.getException(true));
return null;
}
});
}
EventsHandlerImpl getEventsHandler() {
return m_manager.getEventsHandler();
}
HttpServerEndpointHandler getServerEndpointHandler() {
return m_manager.getServerEndpointHandler();
}
EndpointDescription createEndpointDescription(ServiceReference> reference, Map extraProperties) {
return createEndpointDescription(UUID.randomUUID().toString(), reference, extraProperties);
}
EndpointDescription createEndpointDescription(String endpointId, ServiceReference> reference,
Map extraProperties) {
return createEndpointDescription(endpointId, getMergedProperties(reference, extraProperties));
}
private Class>[] loadEndpointInterfaces(ServiceReference> reference, EndpointDescription endpoint) {
BundleContext context = getBundleContext();
Object service = context.getService(reference);
if (service == null) {
logWarning(
"Export failed. Unable to aquire service for reference (%s) and endpoint (%s). Service Registration closed?",
reference, endpoint);
return null;
}
Map> interfaces = collectInterfaces(service.getClass());
context.ungetService(reference);
String[] endpointInterfaces = getStringPlusValue(endpoint.getProperties().get(OBJECTCLASS));
Class>[] exportedInterfaces = new Class>[endpointInterfaces.length];
for (int i = 0; i < endpointInterfaces.length; i++) {
exportedInterfaces[i] = interfaces.get(endpointInterfaces[i]);
if (exportedInterfaces[i] == null) {
logWarning(
"Export failed. Unable load exported interface (%s) from bundle for reference (%s) and endpoint (%s)",
exportedInterfaces[i], reference, endpoint);
return null;
}
}
return exportedInterfaces;
}
private EndpointDescription createEndpointDescription(String endpointId, Map properties) {
String[] configurationTypes = getStringPlusValue(properties.get(SERVICE_EXPORTED_CONFIGS));
if (configurationTypes.length > 0) {
if (!Arrays.asList(configurationTypes).contains(CONFIGURATION_TYPE)) {
logDebug("Can not export service (no supported configuration type specified): %s", properties);
return null;
}
}
// FIXME This is a hotfix to satisfy the OSGi CT that asserts that an exceptions is raised when it passes
// garbage into configuration type specific parameters.
if (properties.get(ENDPOINT_URL) != null) {
throw new IllegalArgumentException("Can not export service (illegal configuration type parameter)");
}
if (properties.get(SERVICE_EXPORTED_INTERFACES) == null) {
logWarning("Can not export service (no exported interfaces): %s", properties);
throw new IllegalArgumentException("Can not export service (no exported interfaces)");
}
String[] exportedInterfaces = getExportedInterfaces(properties);
if (exportedInterfaces.length == 0) {
logWarning("Can not export service (no exported interfaces): %s", properties);
return null;
}
String[] exportedIntents = getExportedIntents(properties);
if (!isExportedIntentsSupported(exportedIntents)) {
logDebug("Can not export service (unsupported intent specified): %s", properties);
return null;
}
properties.put(ENDPOINT_ID, endpointId);
properties.put(OBJECTCLASS, exportedInterfaces);
properties.put(SERVICE_IMPORTED_CONFIGS, new String[] { CONFIGURATION_TYPE });
properties.put(SERVICE_INTENTS, exportedIntents);
properties.put(ENDPOINT_SERVICE_ID, properties.get(SERVICE_ID));
properties.put(ENDPOINT_FRAMEWORK_UUID, getFrameworkUUID(getBundleContext()));
URL endpointURL = m_manager.getServerEndpointHandler().getEndpointURL(endpointId);
properties.put(ENDPOINT_URL, endpointURL.toString());
return new EndpointDescription(properties);
}
/**
* Returns a list of exported interface names as declared by the SERVICE_EXPORTED_INTERFACES property
* using the following rules.
*
* - A single value of '*' means the OBJECTCLASS must be used
* - Any interface must be listed in the OBJECTCLASS
*
*
* @param exportProperties the map of export properties
* @return a list of exported interfaces names
* @throws IllegalArgumentException if an interfaces is listed as export but it is not in the OBJECTCLASS.
*/
private String[] getExportedInterfaces(Map exportProperties) {
String[] providedInterfaces = getStringPlusValue(exportProperties.get(OBJECTCLASS));
String[] exportedInterfaces = getStringPlusValue(exportProperties.get(SERVICE_EXPORTED_INTERFACES));
if (exportedInterfaces.length == 1 && exportedInterfaces[0].equals("*")) {
exportedInterfaces = providedInterfaces;
}
else {
for (String exportedInterface : exportedInterfaces) {
if ("*".equals(exportedInterface)) {
throw new IllegalArgumentException(
"Cannot accept wildcard together with other exported interfaces!");
}
boolean contained = false;
for (String providedInterface : providedInterfaces) {
contained |= providedInterface.equals(exportedInterface);
}
if (!contained) {
logWarning("Exported interface %s not implemented by service: %s", exportedInterface,
providedInterfaces);
return new String[] {};
}
}
}
return exportedInterfaces;
}
/**
* Returns an array exported intents based on the {@link SERVICE_EXPORTED_INTENTS} and {@link SERVICE_EXPORTED_INTENTS_EXTRA}
* property values as well as the default {@link HTTP_PASSBYVALYE_INTENT}.
*
* @param properties the properties
* @return an array of intents
*/
private static String[] getExportedIntents(Map properties) {
Object exportedIntents = properties.get(SERVICE_EXPORTED_INTENTS);
Object exportedIntentsExtra = properties.get(SERVICE_EXPORTED_INTENTS_EXTRA);
if (exportedIntents == null && exportedIntentsExtra == null) {
return new String[] { PASSBYVALYE_INTENT };
}
Set set = new HashSet();
if (exportedIntents != null) {
for (String exportedIntent : getStringPlusValue(exportedIntents)) {
set.add(exportedIntent);
}
}
if (exportedIntentsExtra != null) {
for (String exportedIntent : getStringPlusValue(exportedIntentsExtra)) {
set.add(exportedIntent);
}
}
set.add(PASSBYVALYE_INTENT);
return set.toArray(new String[set.size()]);
}
/**
* Returns a map of merged properties from the specified Service Reference and an optional map with
* extra properties. Merging is done under the following rules:
*
* - Properties with a key starting with a '.' are private and thus ignored
* - Extra properties override service properties irrespective of casing
*
*
* @param reference a Service Reference
* @param extraProperties a map of extra properties, can be {@link null}
* @return a map of merged properties
*/
private static Map getMergedProperties(ServiceReference> reference, Map extraProperties) {
Map serviceProperties = new HashMap();
for (String propertyKey : reference.getPropertyKeys()) {
if (propertyKey.startsWith(".")) {
continue;
}
serviceProperties.put(propertyKey, reference.getProperty(propertyKey));
}
if (extraProperties == null) {
return serviceProperties;
}
Set removeServicePropertyKeys = new HashSet();
for (String extraPropertyKey : extraProperties.keySet()) {
if (extraPropertyKey.startsWith(".") || extraPropertyKey.equalsIgnoreCase(SERVICE_ID)
|| extraPropertyKey.equalsIgnoreCase(OBJECTCLASS)) {
continue;
}
for (String servicePropertyKey : serviceProperties.keySet()) {
if (servicePropertyKey.equalsIgnoreCase(extraPropertyKey)) {
removeServicePropertyKeys.add(servicePropertyKey);
}
}
for (String removeServicePropertyKey : removeServicePropertyKeys) {
serviceProperties.remove(removeServicePropertyKey);
}
removeServicePropertyKeys.clear();
serviceProperties.put(extraPropertyKey, extraProperties.get(extraPropertyKey));
}
return serviceProperties;
}
/**
* Determines whether an array of intents is supported by this implementation.
*
* @param exportedIntents the intents
* @return {@code true} if supported, {@code false} otherwise.
*/
private static boolean isExportedIntentsSupported(String[] exportedIntents) {
if (exportedIntents == null) {
return false;
}
for (String exportedIntent : exportedIntents) {
if (!MY_SUPPORTED_INTENTS_SET.contains(exportedIntent)) {
return false;
}
}
return true;
}
/**
* Determines whether an {@link EndpointDescription} contains a well-formed {@code ENDPOINT_URL} property.
*
* @param endpoint the endpoint
* @return {@code true} if valid, {@code false} otherwise.
*/
private static boolean hasValidServiceLocation(EndpointDescription endpoint) {
Object serviceLocation = endpoint.getProperties().get(ENDPOINT_URL);
if (serviceLocation == null || !(serviceLocation instanceof String)) {
return false;
}
try {
new URL((String) serviceLocation);
}
catch (MalformedURLException e) {
return false;
}
return true;
}
/**
* Determines whether an {@link EndpointDescription} lists interfaces that are available to this
* Remote Service Admin bundle.
*
* Note that the loading of classes effectively triggers dynamic imports, wiring this bundle to a
* provider. Even though importing endpoints are registered using a {@link ServiceFactory} this seems
* to be required for Equinox 3.10 to consider them to be assignable to the interfaces.
*
* @param endpoint the endpoint
* @return {@code true} if valid, {@code false} otherwise.
*/
private static boolean hasAvailableInterfaces(EndpointDescription endpoint) {
List interfaces = endpoint.getInterfaces();
if (interfaces == null || interfaces.isEmpty()) {
return false;
}
try {
for (String iface : interfaces) {
RemoteServiceAdminImpl.class.getClassLoader().loadClass(iface);
}
}
catch (ClassNotFoundException e) {
return false;
}
return true;
}
private static Map> collectInterfaces(Class> clazz) {
Map> accumulator = new HashMap>();
collectInterfaces(clazz, accumulator);
return accumulator;
}
private static void collectInterfaces(Class> clazz, Map> accumulator) {
for (Class> iface : clazz.getInterfaces()) {
if (!accumulator.containsKey(iface.getName())) {
accumulator.put(iface.getName(), iface);
collectInterfaces(iface, accumulator);
}
}
Class> parent = clazz.getSuperclass();
if (parent != null) {
collectInterfaces(parent, accumulator);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy