[DOSGI-229] Use separate bundles for API and RSA impl. Remove dynamic import
Project: http://git-wip-us.apache.org/repos/asf/aries-rsa/repo Commit: http://git-wip-us.apache.org/repos/asf/aries-rsa/commit/4ed2614d Tree: http://git-wip-us.apache.org/repos/asf/aries-rsa/tree/4ed2614d Diff: http://git-wip-us.apache.org/repos/asf/aries-rsa/diff/4ed2614d Branch: refs/heads/master Commit: 4ed2614db3d5ac7550b6738811014dae404282b1 Parents: 84f6be4 Author: Christian Schneider <[email protected]> Authored: Tue Mar 8 00:47:16 2016 +0100 Committer: Christian Schneider <[email protected]> Committed: Tue Mar 8 00:47:16 2016 +0100 ---------------------------------------------------------------------- .../features/src/main/resources/features.xml | 2 + dsw/cxf-dosgi-provider-api/pom.xml | 64 +++ .../cxf/dosgi/dsw/api/DistributionProvider.java | 54 ++ .../org/apache/cxf/dosgi/dsw/api/Endpoint.java | 27 + .../dsw/api/IntentUnsatisfiedException.java | 35 ++ dsw/cxf-dosgi-rsa/pom.xml | 71 +++ .../apache/cxf/dosgi/dsw/service/Activator.java | 37 ++ .../dosgi/dsw/service/ClientServiceFactory.java | 106 ++++ .../service/DistributionProviderTracker.java | 67 +++ .../cxf/dosgi/dsw/service/EventAdminHelper.java | 151 +++++ .../cxf/dosgi/dsw/service/EventProducer.java | 114 ++++ .../dosgi/dsw/service/ExportReferenceImpl.java | 77 +++ .../dsw/service/ExportRegistrationImpl.java | 152 +++++ .../dsw/service/ImportRegistrationImpl.java | 230 ++++++++ .../dsw/service/RemoteServiceAdminCore.java | 553 +++++++++++++++++++ .../dsw/service/RemoteServiceAdminInstance.java | 99 ++++ .../dsw/service/RemoteServiceadminFactory.java | 51 ++ .../cxf/dosgi/dsw/service/StringPlus.java | 72 +++ .../dsw/service/ClientServiceFactoryTest.java | 88 +++ .../dosgi/dsw/service/EventProducerTest.java | 189 +++++++ .../dsw/service/ImportRegistrationImplTest.java | 178 ++++++ .../dsw/service/RemoteServiceAdminCoreTest.java | 491 ++++++++++++++++ .../cxf/dosgi/dsw/service/StringPlusTest.java | 63 +++ dsw/cxf-dsw/pom.xml | 12 +- .../org/apache/cxf/dosgi/dsw/Activator.java | 8 +- .../cxf/dosgi/dsw/api/DistributionProvider.java | 54 -- .../org/apache/cxf/dosgi/dsw/api/Endpoint.java | 27 - .../dsw/api/IntentUnsatisfiedException.java | 35 -- .../dosgi/dsw/service/ClientServiceFactory.java | 106 ---- .../cxf/dosgi/dsw/service/EventAdminHelper.java | 151 ----- .../cxf/dosgi/dsw/service/EventProducer.java | 114 ---- .../dosgi/dsw/service/ExportReferenceImpl.java | 77 --- .../dsw/service/ExportRegistrationImpl.java | 152 ----- .../dsw/service/ImportRegistrationImpl.java | 230 -------- .../cxf/dosgi/dsw/service/PackageFinder.java | 47 -- .../dsw/service/RemoteServiceAdminCore.java | 549 ------------------ .../dsw/service/RemoteServiceAdminInstance.java | 99 ---- .../dsw/service/RemoteServiceadminFactory.java | 51 -- .../cxf/dosgi/dsw/service/StringPlus.java | 72 --- .../org/apache/cxf/dosgi/dsw/ActivatorTest.java | 4 +- .../dsw/service/ClientServiceFactoryTest.java | 90 --- .../service/DistributionProviderImplTest.java | 124 ----- .../dosgi/dsw/service/EventProducerTest.java | 189 ------- .../dsw/service/ImportRegistrationImplTest.java | 178 ------ .../dsw/service/RemoteServiceAdminCoreTest.java | 491 ---------------- .../cxf/dosgi/dsw/service/StringPlusTest.java | 63 --- dsw/pom.xml | 2 + 47 files changed, 2983 insertions(+), 2913 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/distribution/features/src/main/resources/features.xml ---------------------------------------------------------------------- diff --git a/distribution/features/src/main/resources/features.xml b/distribution/features/src/main/resources/features.xml index 1c19381..b39562b 100644 --- a/distribution/features/src/main/resources/features.xml +++ b/distribution/features/src/main/resources/features.xml @@ -24,6 +24,8 @@ <feature>cxf-http</feature> <feature>http</feature> <bundle start-level="8">mvn:${project.groupId}/cxf-dosgi-ri-osgi-api/${project.version}</bundle> + <bundle>mvn:${project.groupId}/cxf-dosgi-ri-provider-api/${project.version}</bundle> + <bundle>mvn:${project.groupId}/cxf-dosgi-ri-rsa/${project.version}</bundle> <bundle>mvn:${project.groupId}/cxf-dosgi-ri-topology-manager/${project.version}</bundle> <bundle>mvn:${project.groupId}/cxf-dosgi-ri-dsw-cxf/${project.version}</bundle> </feature> http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-provider-api/pom.xml ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-provider-api/pom.xml b/dsw/cxf-dosgi-provider-api/pom.xml new file mode 100644 index 0000000..9b248fb --- /dev/null +++ b/dsw/cxf-dosgi-provider-api/pom.xml @@ -0,0 +1,64 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.cxf.dosgi</groupId> + <artifactId>cxf-dosgi-ri-parent</artifactId> + <version>1.8-SNAPSHOT</version> + <relativePath>../../parent/pom.xml</relativePath> + </parent> + <artifactId>cxf-dosgi-ri-provider-api</artifactId> + <packaging>bundle</packaging> + <name>Distributed OSGI Distributed Software Modules</name> + + <properties> + <topDirectoryLocation>../..</topDirectoryLocation> + </properties> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymockclassextension</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jdk14</artifactId> + <version>1.7.14</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/DistributionProvider.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/DistributionProvider.java b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/DistributionProvider.java new file mode 100644 index 0000000..c7328db --- /dev/null +++ b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/DistributionProvider.java @@ -0,0 +1,54 @@ +/** + * 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.cxf.dosgi.dsw.api; + +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.remoteserviceadmin.EndpointDescription; + +@SuppressWarnings("rawtypes") +public interface DistributionProvider { + + String[] getSupportedTypes(); + + /** + * @param sref reference of the service to be exported + * @param effectiveProperties combined properties of the service and additional properties from rsa + * @param exportedInterface name of the interface to be exported + * @return closeable Endpoint that represents the service that is exposed to the outside world + */ + Endpoint exportService(ServiceReference<?> sref, + Map<String, Object> effectiveProperties, + Class[] exportedInterfaces); + + /** + * @param sref reference of the service offered to the requesting bundle + * @param interfaces interfaces of the service to proxy + * @param endpoint description of the remote endpoint + * @return service proxy to be given to the requesting bundle + * @throws IntentUnsatisfiedException + */ + Object importEndpoint(BundleContext consumerContext, + Class[] interfaces, + EndpointDescription endpoint) + throws IntentUnsatisfiedException; + +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/Endpoint.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/Endpoint.java b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/Endpoint.java new file mode 100644 index 0000000..f45b562 --- /dev/null +++ b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/Endpoint.java @@ -0,0 +1,27 @@ +/** + * 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.cxf.dosgi.dsw.api; + +import java.io.Closeable; + +import org.osgi.service.remoteserviceadmin.EndpointDescription; + +public interface Endpoint extends Closeable { + EndpointDescription description(); +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/IntentUnsatisfiedException.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/IntentUnsatisfiedException.java b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/IntentUnsatisfiedException.java new file mode 100644 index 0000000..4e27938 --- /dev/null +++ b/dsw/cxf-dosgi-provider-api/src/main/java/org/apache/cxf/dosgi/dsw/api/IntentUnsatisfiedException.java @@ -0,0 +1,35 @@ +/** + * 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.cxf.dosgi.dsw.api; + +public class IntentUnsatisfiedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final String intent; + + public IntentUnsatisfiedException(String intent) { + super(intent); + this.intent = intent; + } + + public String getIntent() { + return intent; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/pom.xml ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/pom.xml b/dsw/cxf-dosgi-rsa/pom.xml new file mode 100644 index 0000000..3327135 --- /dev/null +++ b/dsw/cxf-dosgi-rsa/pom.xml @@ -0,0 +1,71 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.cxf.dosgi</groupId> + <artifactId>cxf-dosgi-ri-parent</artifactId> + <version>1.8-SNAPSHOT</version> + <relativePath>../../parent/pom.xml</relativePath> + </parent> + <artifactId>cxf-dosgi-ri-rsa</artifactId> + <packaging>bundle</packaging> + <name>CXF dOSGi Remote Service Admin Implementation</name> + <description>The CXF Remote Service Admin as described in the OSGi Remote Service Admin specification</description> + + <properties> + <topDirectoryLocation>../..</topDirectoryLocation> + </properties> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.apache.cxf.dosgi</groupId> + <artifactId>cxf-dosgi-provider-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymockclassextension</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jdk14</artifactId> + <version>1.7.14</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> + <Bundle-Activator>org.apache.cxf.dosgi.dsw.service.Activator</Bundle-Activator> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java new file mode 100644 index 0000000..4c4d7ad --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/Activator.java @@ -0,0 +1,37 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + private DistributionProviderTracker tracker; + + public void start(BundleContext bundlecontext) throws Exception { + tracker = new DistributionProviderTracker(bundlecontext); + tracker.open(); + } + + public void stop(BundleContext context) throws Exception { + tracker.close(); + } + +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java new file mode 100644 index 0000000..b482b64 --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java @@ -0,0 +1,106 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.apache.cxf.dosgi.dsw.api.IntentUnsatisfiedException; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("rawtypes") +public class ClientServiceFactory implements ServiceFactory { + + private static final Logger LOG = LoggerFactory.getLogger(ClientServiceFactory.class); + + private EndpointDescription endpoint; + private DistributionProvider handler; + private ImportRegistrationImpl importRegistration; + + private boolean closeable; + private int serviceCounter; + + public ClientServiceFactory(EndpointDescription endpoint, + DistributionProvider handler, ImportRegistrationImpl ir) { + this.endpoint = endpoint; + this.handler = handler; + this.importRegistration = ir; + } + + public Object getService(final Bundle requestingBundle, final ServiceRegistration sreg) { + List<String> interfaceNames = endpoint.getInterfaces(); + try { + LOG.debug("getService() from serviceFactory for {}", interfaceNames); + final List<Class<?>> interfaces = new ArrayList<Class<?>>(); + for (String ifaceName : interfaceNames) { + interfaces.add(requestingBundle.loadClass(ifaceName)); + } + Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + Class<?>[] ifAr = interfaces.toArray(new Class[]{}); + return handler.importEndpoint(requestingBundle.getBundleContext(), ifAr, endpoint); + } + }); + + synchronized (this) { + serviceCounter++; + } + return proxy; + } catch (IntentUnsatisfiedException iue) { + LOG.info("Did not create proxy for {} because intent {} could not be satisfied", + interfaceNames, iue.getIntent()); + } catch (Exception e) { + LOG.warn("Problem creating a remote proxy for {}", interfaceNames, e); + } + return null; + } + + public void ungetService(Bundle requestingBundle, ServiceRegistration sreg, Object serviceObject) { + String[] interfaces = (String[])sreg.getReference().getProperty(org.osgi.framework.Constants.OBJECTCLASS); + LOG.info("Releasing a client object, interfaces: {}", Arrays.toString(interfaces)); + + synchronized (this) { + serviceCounter--; + LOG.debug("Services still provided by this ServiceFactory: {}", serviceCounter); + closeIfUnused(); + } + } + + public void setCloseable(boolean closeable) { + synchronized (this) { + this.closeable = closeable; + closeIfUnused(); + } + } + + private synchronized void closeIfUnused() { + if (serviceCounter <= 0 && closeable) { + importRegistration.closeAll(); + } + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java new file mode 100644 index 0000000..7dd31b7 --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTracker.java @@ -0,0 +1,67 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin; +import org.osgi.util.tracker.ServiceTracker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("rawtypes") +public class DistributionProviderTracker extends ServiceTracker<DistributionProvider, ServiceRegistration> { + private static final Logger LOG = LoggerFactory.getLogger(Activator.class); + + public DistributionProviderTracker(BundleContext context) { + super(context, DistributionProvider.class, null); + } + + @Override + public ServiceRegistration addingService(ServiceReference<DistributionProvider> reference) { + LOG.debug("RemoteServiceAdmin Implementation is starting up"); + DistributionProvider provider = context.getService(reference); + Bundle apiBundle = FrameworkUtil.getBundle(DistributionProvider.class); + RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(context, + apiBundle.getBundleContext(), + provider); + RemoteServiceadminFactory rsaf = new RemoteServiceadminFactory(rsaCore); + Dictionary<String, Object> props = new Hashtable<String, Object>(); + props.put("remote.intents.supported", reference.getProperty("remote.intents.supported")); + props.put("remote.configs.supported", reference.getProperty("remote.configs.supported")); + LOG.info("Registering RemoteServiceAdmin for provider " + provider.getClass().getName()); + return context.registerService(RemoteServiceAdmin.class.getName(), rsaf, props); + } + + @Override + public void removedService(ServiceReference<DistributionProvider> reference, + ServiceRegistration reg) { + LOG.debug("RemoteServiceAdmin Implementation is shutting down now"); + reg.unregister(); + super.removedService(reference, reg); + } + +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java new file mode 100644 index 0000000..4868efa --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java @@ -0,0 +1,151 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.util.HashMap; +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.Version; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EventAdminHelper { + + private static final Logger LOG = LoggerFactory.getLogger(EventAdminHelper.class); + + private BundleContext bctx; + + public EventAdminHelper(BundleContext bc) { + bctx = bc; + } + + private Event createEvent(Map<String, Object> props, String type) { + String topic = "org/osgi/service/remoteserviceadmin/" + type; + props.put("bundle", bctx.getBundle()); + props.put("bundle.id", bctx.getBundle().getBundleId()); + props.put("bundle.symbolicname", bctx.getBundle().getSymbolicName()); + + String version = (String)bctx.getBundle().getHeaders().get("Bundle-Version"); + Version v = version != null ? new Version(version) : Version.emptyVersion; + setIfNotNull(props, "bundle.version", v); + + return new Event(topic, props); + } + + public void notifyEventAdmin(RemoteServiceAdminEvent rsae) { + String topic = remoteServiceAdminEventTypeToString(rsae.getType()); + + Map<String, Object> props = new HashMap<String, Object>(); + setIfNotNull(props, "cause", rsae.getException()); + + EndpointDescription endpoint = null; + if (rsae.getImportReference() != null) { + endpoint = ((ImportRegistrationImpl)rsae.getImportReference()).getImportedEndpointAlways(); + setIfNotNull(props, "import.registration", endpoint); + } else if (rsae.getExportReference() != null) { + endpoint = rsae.getExportReference().getExportedEndpoint(); + setIfNotNull(props, "export.registration", endpoint); + } + + if (endpoint != null) { + setIfNotNull(props, "service.remote.id", endpoint.getServiceId()); + setIfNotNull(props, "service.remote.uuid", endpoint.getFrameworkUUID()); + setIfNotNull(props, "service.remote.uri", endpoint.getId()); + setIfNotNull(props, "objectClass", endpoint.getInterfaces().toArray()); + setIfNotNull(props, "service.imported.configs", endpoint.getConfigurationTypes()); + } + props.put("timestamp", System.currentTimeMillis()); + props.put("event", rsae); + + Event event = createEvent(props, topic); + notifyEventAdmins(topic, event); + } + + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + private void notifyEventAdmins(String topic, Event event) { + ServiceReference[] refs = null; + try { + refs = bctx.getAllServiceReferences(EventAdmin.class.getName(), null); + } catch (InvalidSyntaxException e) { + LOG.error("Failed to get EventAdmin: " + e.getMessage(), e); + } + + if (refs != null) { + LOG.debug("Publishing event to {} EventAdmins; Topic:[{}]", refs.length, topic); + for (ServiceReference serviceReference : refs) { + EventAdmin eventAdmin = (EventAdmin) bctx.getService(serviceReference); + try { + eventAdmin.postEvent(event); + } finally { + if (eventAdmin != null) { + bctx.ungetService(serviceReference); + } + } + } + } + } + + private <K, V> void setIfNotNull(Map<K, V> map, K key, V val) { + if (val != null) { + map.put(key, val); + } + } + + private static String remoteServiceAdminEventTypeToString(int type) { + String retval; + switch (type) { + case RemoteServiceAdminEvent.EXPORT_ERROR: + retval = "EXPORT_ERROR"; + break; + case RemoteServiceAdminEvent.EXPORT_REGISTRATION: + retval = "EXPORT_REGISTRATION"; + break; + case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION: + retval = "EXPORT_UNREGISTRATION"; + break; + case RemoteServiceAdminEvent.EXPORT_WARNING: + retval = "EXPORT_WARNING"; + break; + case RemoteServiceAdminEvent.IMPORT_ERROR: + retval = "IMPORT_ERROR"; + break; + case RemoteServiceAdminEvent.IMPORT_REGISTRATION: + retval = "IMPORT_REGISTRATION"; + break; + case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION: + retval = "IMPORT_UNREGISTRATION"; + break; + case RemoteServiceAdminEvent.IMPORT_WARNING: + retval = "IMPORT_WARNING"; + break; + default: + retval = "UNKNOWN_EVENT"; + } + return retval; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java new file mode 100644 index 0000000..26a46ab --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java @@ -0,0 +1,114 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.util.List; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.service.remoteserviceadmin.ExportRegistration; +import org.osgi.service.remoteserviceadmin.ImportRegistration; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EventProducer { + + private static final Logger LOG = LoggerFactory.getLogger(EventProducer.class); + private final BundleContext bctx; + private final EventAdminHelper eaHelper; + + public EventProducer(BundleContext bc) { + bctx = bc; + eaHelper = new EventAdminHelper(bctx); + } + + protected void publishNotification(List<ExportRegistration> erl) { + for (ExportRegistration exportRegistration : erl) { + publishNotification(exportRegistration); + } + } + + protected void publishNotification(ExportRegistration er) { + int type = er.getException() == null + ? RemoteServiceAdminEvent.EXPORT_REGISTRATION + : RemoteServiceAdminEvent.EXPORT_ERROR; + notify(type, null, er); + } + + protected void publishNotification(ImportRegistration ir) { + int type = ir.getException() == null + ? RemoteServiceAdminEvent.IMPORT_REGISTRATION + : RemoteServiceAdminEvent.IMPORT_ERROR; + notify(type, ir, null); + } + + public void notifyRemoval(ExportRegistration er) { + notify(RemoteServiceAdminEvent.EXPORT_UNREGISTRATION, null, er); + } + + public void notifyRemoval(ImportRegistration ir) { + notify(RemoteServiceAdminEvent.IMPORT_UNREGISTRATION, ir, null); + } + + // only one of ir or er must be set, and the other must be null + private void notify(int type, ImportRegistration ir, ExportRegistration er) { + try { + RemoteServiceAdminEvent event = ir != null + ? new RemoteServiceAdminEvent(type, bctx.getBundle(), ir.getImportReference(), ir.getException()) + : new RemoteServiceAdminEvent(type, bctx.getBundle(), er.getExportReference(), er.getException()); + notifyListeners(event); + eaHelper.notifyEventAdmin(event); + } catch (IllegalStateException ise) { + LOG.debug("can't send notifications since bundle context is no longer valid"); + } + } + + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + private void notifyListeners(RemoteServiceAdminEvent rsae) { + try { + ServiceReference[] listenerRefs = bctx.getServiceReferences( + RemoteServiceAdminListener.class.getName(), null); + if (listenerRefs != null) { + for (ServiceReference sref : listenerRefs) { + RemoteServiceAdminListener rsal = (RemoteServiceAdminListener)bctx.getService(sref); + if (rsal != null) { + try { + Bundle bundle = sref.getBundle(); + if (bundle != null) { + LOG.debug("notify RemoteServiceAdminListener {} of bundle {}", + rsal, bundle.getSymbolicName()); + rsal.remoteAdminEvent(rsae); + } + } finally { + bctx.ungetService(sref); + } + } + } + } + } catch (InvalidSyntaxException e) { + LOG.error(e.getMessage(), e); + } + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java new file mode 100644 index 0000000..497aa9c --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java @@ -0,0 +1,77 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import org.osgi.framework.ServiceReference; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.ExportReference; + +@SuppressWarnings("rawtypes") +public class ExportReferenceImpl implements ExportReference { + + private ServiceReference serviceReference; + private EndpointDescription endpoint; + + public ExportReferenceImpl(ServiceReference serviceReference, EndpointDescription endpoint) { + this.serviceReference = serviceReference; + this.endpoint = endpoint; + } + + public ExportReferenceImpl(ExportReference exportReference) { + this(exportReference.getExportedService(), exportReference.getExportedEndpoint()); + } + + public EndpointDescription getExportedEndpoint() { + return endpoint; + } + + public ServiceReference getExportedService() { + return serviceReference; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (endpoint == null ? 0 : endpoint.hashCode()); + result = prime * result + (serviceReference == null ? 0 : serviceReference.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ExportReferenceImpl other = (ExportReferenceImpl) obj; + boolean ed = endpoint == null ? other.endpoint == null + : endpoint.equals(other.endpoint); + boolean sr = serviceReference == null ? other.serviceReference == null + : serviceReference.equals(other.serviceReference); + return ed && sr; + } + + synchronized void close() { + this.endpoint = null; + this.serviceReference = null; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java new file mode 100644 index 0000000..d80bd40 --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java @@ -0,0 +1,152 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +import org.apache.cxf.dosgi.dsw.api.Endpoint; +import org.osgi.framework.ServiceReference; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.ExportReference; +import org.osgi.service.remoteserviceadmin.ExportRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("rawtypes") +public class ExportRegistrationImpl implements ExportRegistration { + + private static final Logger LOG = LoggerFactory.getLogger(ExportRegistrationImpl.class); + + private final RemoteServiceAdminCore rsaCore; + private final ExportReferenceImpl exportReference; + private final Closeable server; + private final Throwable exception; + + private final ExportRegistrationImpl parent; + private int instanceCount; + private volatile boolean closed; + + private ExportRegistrationImpl(ExportRegistrationImpl parent, RemoteServiceAdminCore rsaCore, + ExportReferenceImpl exportReference, Closeable server, Throwable exception) { + this.parent = parent != null ? parent.parent : this; // a parent points to itself + this.parent.addInstance(); + this.rsaCore = rsaCore; + this.exportReference = exportReference; + this.server = server; + this.exception = exception; + } + + // create a clone of the provided ExportRegistrationImpl that is linked to it + public ExportRegistrationImpl(ExportRegistrationImpl parent) { + this(parent, parent.rsaCore, new ExportReferenceImpl(parent.exportReference), + parent.server, parent.exception); + } + + // create a new (parent) instance which was exported successfully with the given server + public ExportRegistrationImpl(ServiceReference sref, Endpoint endpoint, RemoteServiceAdminCore rsaCore) { + this(null, rsaCore, new ExportReferenceImpl(sref, endpoint.description()), endpoint, null); + } + + // create a new (parent) instance which failed to be exported with the given exception + public ExportRegistrationImpl(RemoteServiceAdminCore rsaCore, Throwable exception) { + this(null, rsaCore, null, null, exception); + } + + private void ensureParent() { + if (parent != this) { + throw new IllegalStateException("this method may only be called on the parent"); + } + } + + public ExportReference getExportReference() { + if (exportReference == null) { + throw new IllegalStateException(getException()); + } + return closed ? null : exportReference; + } + + public Throwable getException() { + return closed ? null : exception; + } + + public final void close() { + synchronized (this) { + if (closed) { + return; + } + closed = true; + } + + rsaCore.removeExportRegistration(this); + exportReference.close(); + parent.removeInstance(); + } + + private void addInstance() { + ensureParent(); + synchronized (this) { + instanceCount++; + } + } + + private void removeInstance() { + ensureParent(); + synchronized (this) { + instanceCount--; + if (instanceCount <= 0) { + LOG.debug("really closing ExportRegistration now!"); + + if (server != null) { + try { + server.close(); + } catch (IOException e) { + LOG.warn("Error closing ExportRegistration", e); + } + } + } + } + } + + @Override + public String toString() { + if (closed) { + return "ExportRegistration closed"; + } + EndpointDescription endpoint = getExportReference().getExportedEndpoint(); + ServiceReference serviceReference = getExportReference().getExportedService(); + String r = "EndpointDescription for ServiceReference " + serviceReference; + + r += "\n*** EndpointDescription: ****\n"; + if (endpoint == null) { + r += "---> NULL <---- \n"; + } else { + Set<Map.Entry<String, Object>> props = endpoint.getProperties().entrySet(); + for (Map.Entry<String, Object> entry : props) { + Object value = entry.getValue(); + r += entry.getKey() + " => " + + (value instanceof Object[] ? Arrays.toString((Object[]) value) : value) + "\n"; + } + } + return r; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java new file mode 100644 index 0000000..2b896db --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java @@ -0,0 +1,230 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.util.ArrayList; +import java.util.List; + +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.ImportReference; +import org.osgi.service.remoteserviceadmin.ImportRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@SuppressWarnings("rawtypes") +public class ImportRegistrationImpl implements ImportRegistration, ImportReference { + + private static final Logger LOG = LoggerFactory.getLogger(ImportRegistrationImpl.class); + + private volatile Throwable exception; + private volatile ServiceRegistration importedService; // used only in parent + private EndpointDescription endpoint; + private volatile ClientServiceFactory clientServiceFactory; + private RemoteServiceAdminCore rsaCore; + private boolean closed; + private boolean detached; // used only in parent + + private ImportRegistrationImpl parent; + private List<ImportRegistrationImpl> children; // used only in parent + + public ImportRegistrationImpl(Throwable ex) { + exception = ex; + initParent(); + } + + public ImportRegistrationImpl(EndpointDescription endpoint, RemoteServiceAdminCore rsac) { + this.endpoint = endpoint; + this.rsaCore = rsac; + initParent(); + } + + /** + * Creates a clone of the given parent instance. + */ + public ImportRegistrationImpl(ImportRegistrationImpl ir) { + // we always want a link to the parent... + parent = ir.getParent(); + exception = parent.getException(); + endpoint = parent.getImportedEndpointDescription(); + clientServiceFactory = parent.clientServiceFactory; + rsaCore = parent.rsaCore; + + parent.instanceAdded(this); + } + + private void initParent() { + parent = this; + children = new ArrayList<ImportRegistrationImpl>(1); + } + + private void ensureParent() { + if (parent != this) { + throw new IllegalStateException("this method may only be called on the parent"); + } + } + + /** + * Called on parent when a child is added. + * + * @param iri the child + */ + private synchronized void instanceAdded(ImportRegistrationImpl iri) { + ensureParent(); + children.add(iri); + } + + /** + * Called on parent when a child is closed. + * + * @param iri the child + */ + private void instanceClosed(ImportRegistrationImpl iri) { + ensureParent(); + synchronized (this) { + children.remove(iri); + if (!children.isEmpty() || detached || !closed) { + return; + } + detached = true; + } + + LOG.debug("really closing ImportRegistration now"); + + if (importedService != null) { + try { + importedService.unregister(); + } catch (IllegalStateException ise) { + LOG.debug("imported service is already unregistered"); + } + importedService = null; + } + if (clientServiceFactory != null) { + clientServiceFactory.setCloseable(true); + } + } + + public void close() { + LOG.debug("close() called"); + + synchronized (this) { + if (isInvalid()) { + return; + } + closed = true; + } + rsaCore.removeImportRegistration(this); + parent.instanceClosed(this); + } + + /** + * Closes all ImportRegistrations which share the same parent as this one. + */ + public void closeAll() { + if (this == parent) { + LOG.info("closing down all child ImportRegistrations"); + + // we must iterate over a copy of children since close() removes the child + // from the list (which would cause a ConcurrentModificationException) + for (ImportRegistrationImpl ir : copyChildren()) { + ir.close(); + } + this.close(); + } else { + parent.closeAll(); + } + } + + private List<ImportRegistrationImpl> copyChildren() { + synchronized (this) { + return new ArrayList<ImportRegistrationImpl>(children); + } + } + + public EndpointDescription getImportedEndpointDescription() { + return isInvalid() ? null : endpoint; + } + + @Override + public EndpointDescription getImportedEndpoint() { + return getImportedEndpointDescription(); + } + + @Override + public ServiceReference getImportedService() { + return isInvalid() || parent.importedService == null ? null : parent.importedService.getReference(); + } + + @Override + public ImportReference getImportReference() { + return this; + } + + @Override + public Throwable getException() { + return exception; + } + + public void setException(Throwable ex) { + exception = ex; + } + + private synchronized boolean isInvalid() { + return exception != null || closed; + } + + /** + * Sets the {@link ServiceRegistration} representing the locally + * registered {@link ClientServiceFactory} service which provides + * proxies to the remote imported service. It is set only on the parent. + * + * @param sreg the ServiceRegistration + */ + public void setImportedServiceRegistration(ServiceRegistration sreg) { + ensureParent(); + importedService = sreg; + } + + /** + * Sets the {@link ClientServiceFactory} which is the implementation + * of the locally registered service which provides proxies to the + * remote imported service. It is set only on the parent. + * + * @param csf the ClientServiceFactory + */ + public void setClientServiceFactory(ClientServiceFactory csf) { + ensureParent(); + clientServiceFactory = csf; + } + + public ImportRegistrationImpl getParent() { + return parent; + } + + /** + * Returns the imported endpoint even if this + * instance is closed or has an exception. + * + * @return the imported endpoint + */ + public EndpointDescription getImportedEndpointAlways() { + return endpoint; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java new file mode 100644 index 0000000..aa6e4ee --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java @@ -0,0 +1,553 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.apache.cxf.dosgi.dsw.api.Endpoint; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +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.RemoteConstants; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteServiceAdminCore implements RemoteServiceAdmin { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteServiceAdminCore.class); + + private final Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices + = new LinkedHashMap<Map<String, Object>, Collection<ExportRegistration>>(); + private final Map<EndpointDescription, Collection<ImportRegistrationImpl>> importedServices + = new LinkedHashMap<EndpointDescription, Collection<ImportRegistrationImpl>>(); + + // Is stored in exportedServices while the export is in progress as a marker + private final List<ExportRegistration> exportInProgress = Collections.emptyList(); + + private final BundleContext bctx; + private final EventProducer eventProducer; + private final ServiceListener exportedServiceListener; + private DistributionProvider provider; + private BundleContext apictx; + + public RemoteServiceAdminCore(BundleContext context, BundleContext apiContext, DistributionProvider provider) { + this.bctx = context; + this.apictx = apiContext; + this.eventProducer = new EventProducer(bctx); + this.provider = provider; + // listen for exported services being unregistered so we can close the export + this.exportedServiceListener = new ServiceListener() { + public void serviceChanged(ServiceEvent event) { + if (event.getType() == ServiceEvent.UNREGISTERING) { + removeServiceExports(event.getServiceReference()); + } + } + }; + try { + String filter = "(" + RemoteConstants.SERVICE_EXPORTED_INTERFACES + "=*)"; + context.addServiceListener(exportedServiceListener, filter); + } catch (InvalidSyntaxException ise) { + throw new RuntimeException(ise); // can never happen + } + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List<ExportRegistration> exportService(ServiceReference serviceReference, Map additionalProperties) + throws IllegalArgumentException, UnsupportedOperationException { + Map<String, Object> serviceProperties = getProperties(serviceReference); + if (additionalProperties != null) { + overlayProperties(serviceProperties, additionalProperties); + } + Map<String, Object> key = makeKey(serviceProperties); + + List<String> interfaceNames = getInterfaceNames(serviceProperties); + + if (isImportedService(serviceReference)) { + return Collections.emptyList(); + } + + List<ExportRegistration> exportRegs = getExistingAndLock(key, interfaceNames); + if (exportRegs != null) { + return exportRegs; + } + + try { + ExportRegistration exportReg = exportService(interfaceNames, serviceReference, serviceProperties); + exportRegs = new ArrayList<>(); + exportRegs.add(exportReg); + store(key, exportRegs); + return exportRegs; + } finally { + unlock(key); + } + } + + private void store(Map<String, Object> key, List<ExportRegistration> exportRegs) { + if (!exportRegs.isEmpty()) { + // enlist initial export registrations in global list of exportRegistrations + synchronized (exportedServices) { + exportedServices.put(key, new ArrayList<ExportRegistration>(exportRegs)); + } + eventProducer.publishNotification(exportRegs); + } + } + + private void unlock(Map<String, Object> key) { + synchronized (exportedServices) { + if (exportedServices.get(key) == exportInProgress) { + exportedServices.remove(key); + } + exportedServices.notifyAll(); // in any case, always notify waiting threads + } + } + + private List<ExportRegistration> getExistingAndLock(Map<String, Object> key, List<String> interfaces) { + synchronized (exportedServices) { + // check if it is already exported... + Collection<ExportRegistration> existingRegs = exportedServices.get(key); + + // if the export is already in progress, wait for it to be complete + while (existingRegs == exportInProgress) { + try { + exportedServices.wait(); + existingRegs = exportedServices.get(key); + } catch (InterruptedException ie) { + LOG.debug("interrupted while waiting for export in progress"); + return Collections.emptyList(); + } + } + + // if the export is complete, return a copy of existing export + if (existingRegs != null) { + LOG.debug("already exported this service. Returning existing exportRegs {} ", interfaces); + return copyExportRegistration(existingRegs); + } + + // mark export as being in progress + exportedServices.put(key, exportInProgress); + } + return null; + } + + private ExportRegistration exportService(List<String> interfaceNames, + ServiceReference<?> serviceReference, Map<String, Object> serviceProperties) { + LOG.info("interfaces selected for export: " + interfaceNames); + try { + Class<?>[] interfaces = getInterfaces(interfaceNames, serviceReference.getBundle()); + Endpoint endpoint = provider.exportService(serviceReference, serviceProperties, interfaces); + return new ExportRegistrationImpl(serviceReference, endpoint, this); + } catch (Exception e) { + return new ExportRegistrationImpl(this, e); + } + } + + private Class<?>[] getInterfaces(List<String> interfaceNames, + Bundle serviceBundle) throws ClassNotFoundException { + List<Class<?>> interfaces = new ArrayList<>(); + for (String interfaceName : interfaceNames) { + interfaces.add(serviceBundle.loadClass(interfaceName)); + } + return interfaces.toArray(new Class[]{}); + } + + /** + * Determines which interfaces should be exported. + * + * @param serviceProperties the exported service properties + * @return the interfaces to be exported + * @throws IllegalArgumentException if the service parameters are invalid + * @see RemoteServiceAdmin#exportService + * @see org.osgi.framework.Constants#OBJECTCLASS + * @see RemoteConstants#SERVICE_EXPORTED_INTERFACES + */ + private List<String> getInterfaceNames(Map<String, Object> serviceProperties) { + String[] providedInterfaces = (String[])serviceProperties.get(org.osgi.framework.Constants.OBJECTCLASS); + if (providedInterfaces == null || providedInterfaces.length == 0) { + throw new IllegalArgumentException("service is missing the objectClass property"); + } + + String[] exportedInterfaces + = StringPlus.normalize(serviceProperties.get(RemoteConstants.SERVICE_EXPORTED_INTERFACES)); + if (exportedInterfaces == null || exportedInterfaces.length == 0) { + throw new IllegalArgumentException("service is missing the service.exported.interfaces property"); + } + + List<String> interfaces = new ArrayList<String>(1); + if (exportedInterfaces.length == 1 && "*".equals(exportedInterfaces[0])) { + // FIXME: according to the spec, this should only return the interfaces, and not + // non-interface classes (which are valid OBJECTCLASS values, even if discouraged) + Collections.addAll(interfaces, providedInterfaces); + } else { + List<String> providedList = Arrays.asList(providedInterfaces); + List<String> allowedList = Arrays.asList(exportedInterfaces); + if (!providedList.containsAll(allowedList)) { + throw new IllegalArgumentException(String.format( + "exported interfaces %s must be a subset of the service's registered types %s", + allowedList, providedList)); + } + + Collections.addAll(interfaces, exportedInterfaces); + } + return interfaces; + } + + /** + * Converts the given properties map into one that can be used as a map key itself. + * For example, if a value is an array, it is converted into a list so that the + * equals method will compare it properly. + * + * @param properties a properties map + * @return a map that represents the given map, but can be safely used as a map key itself + */ + private Map<String, Object> makeKey(Map<String, Object> properties) { + // FIXME: we should also make logically equal values actually compare as equal + // (e.g. String+ values should be normalized) + Map<String, Object> converted = new HashMap<String, Object>(properties.size()); + for (Map.Entry<String, Object> entry : properties.entrySet()) { + Object val = entry.getValue(); + // convert arrays into lists so that they can be compared via equals() + if (val instanceof Object[]) { + val = Arrays.asList((Object[])val); + } + converted.put(entry.getKey(), val); + } + return converted; + } + + private List<ExportRegistration> copyExportRegistration(Collection<ExportRegistration> regs) { + Set<EndpointDescription> copiedEndpoints = new HashSet<EndpointDescription>(); + + // create a new list with copies of the exportRegistrations + List<ExportRegistration> copy = new ArrayList<ExportRegistration>(regs.size()); + for (ExportRegistration exportRegistration : regs) { + if (exportRegistration instanceof ExportRegistrationImpl) { + ExportRegistrationImpl exportRegistrationImpl = (ExportRegistrationImpl) exportRegistration; + EndpointDescription epd = exportRegistration.getExportReference().getExportedEndpoint(); + // create one copy for each distinct endpoint description + if (!copiedEndpoints.contains(epd)) { + copiedEndpoints.add(epd); + copy.add(new ExportRegistrationImpl(exportRegistrationImpl)); + } + } + } + + regs.addAll(copy); + + eventProducer.publishNotification(copy); + return copy; + } + + private boolean isImportedService(ServiceReference<?> sref) { + return sref.getProperty(RemoteConstants.SERVICE_IMPORTED) != null; + } + + @Override + public Collection<ExportReference> getExportedServices() { + synchronized (exportedServices) { + List<ExportReference> ers = new ArrayList<ExportReference>(); + for (Collection<ExportRegistration> exportRegistrations : exportedServices.values()) { + for (ExportRegistration er : exportRegistrations) { + ers.add(new ExportReferenceImpl(er.getExportReference())); + } + } + return Collections.unmodifiableCollection(ers); + } + } + + @Override + public Collection<ImportReference> getImportedEndpoints() { + synchronized (importedServices) { + List<ImportReference> irs = new ArrayList<ImportReference>(); + for (Collection<ImportRegistrationImpl> irl : importedServices.values()) { + for (ImportRegistrationImpl impl : irl) { + irs.add(impl.getImportReference()); + } + } + return Collections.unmodifiableCollection(irs); + } + } + + /** + * Importing form here... + */ + @Override + public ImportRegistration importService(EndpointDescription endpoint) { + LOG.debug("importService() Endpoint: {}", endpoint.getProperties()); + + synchronized (importedServices) { + Collection<ImportRegistrationImpl> imRegs = importedServices.get(endpoint); + if (imRegs != null && !imRegs.isEmpty()) { + LOG.debug("creating copy of existing import registrations"); + ImportRegistrationImpl irParent = imRegs.iterator().next(); + ImportRegistrationImpl ir = new ImportRegistrationImpl(irParent); + imRegs.add(ir); + eventProducer.publishNotification(ir); + return ir; + } + + if (determineConfigTypesForImport(endpoint).size() == 0) { + LOG.info("No matching handler can be found for remote endpoint {}.", endpoint.getId()); + return null; + } + + // TODO: somehow select the interfaces that should be imported ---> job of the TopologyManager? + List<String> matchingInterfaces = endpoint.getInterfaces(); + + if (matchingInterfaces.size() == 0) { + LOG.info("No matching interfaces found for remote endpoint {}.", endpoint.getId()); + return null; + } + + LOG.info("Importing service {} with interfaces {} using handler {}.", + endpoint.getId(), endpoint.getInterfaces(), provider.getClass()); + + ImportRegistrationImpl imReg = exposeServiceFactory(matchingInterfaces.get(0), endpoint, provider); + if (imRegs == null) { + imRegs = new ArrayList<ImportRegistrationImpl>(); + importedServices.put(endpoint, imRegs); + } + imRegs.add(imReg); + eventProducer.publishNotification(imReg); + return imReg; + } + } + + private List<String> determineConfigTypesForImport(EndpointDescription endpoint) { + List<String> remoteConfigurationTypes = endpoint.getConfigurationTypes(); + + List<String> usableConfigurationTypes = new ArrayList<String>(); + for (String ct : provider.getSupportedTypes()) { + if (remoteConfigurationTypes.contains(ct)) { + usableConfigurationTypes.add(ct); + } + } + + LOG.info("Ignoring endpoint {} as it has no compatible configuration types: {}.", + endpoint.getId(), remoteConfigurationTypes); + return usableConfigurationTypes; + } + + protected ImportRegistrationImpl exposeServiceFactory(String interfaceName, + EndpointDescription epd, + DistributionProvider handler) { + ImportRegistrationImpl imReg = new ImportRegistrationImpl(epd, this); + try { + EndpointDescription endpoint = imReg.getImportedEndpointDescription(); + Dictionary<String, Object> serviceProps = new Hashtable<String, Object>(endpoint.getProperties()); + serviceProps.put(RemoteConstants.SERVICE_IMPORTED, true); + serviceProps.remove(RemoteConstants.SERVICE_EXPORTED_INTERFACES); + + ClientServiceFactory csf = new ClientServiceFactory(endpoint, handler, imReg); + imReg.setClientServiceFactory(csf); + + /** + * Export the factory using the api context as it has very few imports. + * If the bundle publishing the factory does not import the service interface + * package then the factory is visible for all consumers which we want. + */ + ServiceRegistration<?> csfReg = apictx.registerService(interfaceName, csf, serviceProps); + imReg.setImportedServiceRegistration(csfReg); + } catch (Exception ex) { + // Only logging at debug level as this might be written to the log at the TopologyManager + LOG.debug("Can not proxy service with interface " + interfaceName + ": " + ex.getMessage(), ex); + imReg.setException(ex); + } + return imReg; + } + + /** + * Removes and closes all exports for the given service. + * This is called when the service is unregistered. + * + * @param sref the service whose exports should be removed and closed + */ + protected void removeServiceExports(ServiceReference<?> sref) { + List<ExportRegistration> regs = new ArrayList<ExportRegistration>(1); + synchronized (exportedServices) { + for (Iterator<Collection<ExportRegistration>> it = exportedServices.values().iterator(); it.hasNext();) { + Collection<ExportRegistration> value = it.next(); + for (Iterator<ExportRegistration> it2 = value.iterator(); it2.hasNext();) { + ExportRegistration er = it2.next(); + if (er.getExportReference().getExportedService().equals(sref)) { + regs.add(er); + } + } + } + // do this outside of iteration to avoid concurrent modification + for (ExportRegistration er : regs) { + LOG.debug("closing export for service {}", sref); + er.close(); + } + } + + } + + /** + * Removes the provided Export Registration from the internal management structures. + * This is called from the ExportRegistration itself when it is closed (so should + * not attempt to close it again here). + * + * @param eri the export registration to remove + */ + protected void removeExportRegistration(ExportRegistrationImpl eri) { + synchronized (exportedServices) { + for (Iterator<Collection<ExportRegistration>> it = exportedServices.values().iterator(); it.hasNext();) { + Collection<ExportRegistration> value = it.next(); + for (Iterator<ExportRegistration> it2 = value.iterator(); it2.hasNext();) { + ExportRegistration er = it2.next(); + if (er.equals(eri)) { + eventProducer.notifyRemoval(eri); + it2.remove(); + if (value.isEmpty()) { + it.remove(); + } + return; + } + } + } + } + } + + // remove all export registrations associated with the given bundle + protected void removeExportRegistrations(Bundle exportingBundle) { + List<ExportRegistration> bundleExports = getExportsForBundle(exportingBundle); + for (ExportRegistration export : bundleExports) { + export.close(); + } + } + + // remove all import registrations + protected void removeImportRegistrations() { + Collection<ImportRegistrationImpl> copy = new ArrayList<ImportRegistrationImpl>(); + synchronized (importedServices) { + for (Collection<ImportRegistrationImpl> irs : importedServices.values()) { + copy.addAll(irs); + } + } + for (ImportRegistrationImpl ir : copy) { + removeImportRegistration(ir); + } + } + + private List<ExportRegistration> getExportsForBundle(Bundle exportingBundle) { + synchronized (exportedServices) { + List<ExportRegistration> bundleRegs = new ArrayList<ExportRegistration>(); + for (Collection<ExportRegistration> regs : exportedServices.values()) { + if (!regs.isEmpty()) { + ExportRegistration exportRegistration = regs.iterator().next(); + if (exportRegistration.getException() == null) { + Bundle regBundle = exportRegistration.getExportReference().getExportedService().getBundle(); + if (exportingBundle.equals(regBundle)) { + bundleRegs.addAll(regs); + } + } + } + } + return bundleRegs; + } + } + + protected void removeImportRegistration(ImportRegistrationImpl iri) { + synchronized (importedServices) { + LOG.debug("Removing importRegistration {}", iri); + + Collection<ImportRegistrationImpl> imRegs = importedServices.get(iri.getImportedEndpointAlways()); + if (imRegs != null && imRegs.contains(iri)) { + imRegs.remove(iri); + eventProducer.notifyRemoval(iri); + } + if (imRegs == null || imRegs.isEmpty()) { + importedServices.remove(iri.getImportedEndpointAlways()); + } + } + } + + public void close() { + removeImportRegistrations(); + bctx.removeServiceListener(exportedServiceListener); + } + + static void overlayProperties(Map<String, Object> serviceProperties, + Map<String, Object> additionalProperties) { + Map<String, String> keysLowerCase = new HashMap<String, String>(); + for (String key : serviceProperties.keySet()) { + keysLowerCase.put(key.toLowerCase(), key); + } + + for (Map.Entry<String, Object> e : additionalProperties.entrySet()) { + String key = e.getKey(); + String lowerKey = key.toLowerCase(); + if (org.osgi.framework.Constants.SERVICE_ID.toLowerCase().equals(lowerKey) + || org.osgi.framework.Constants.OBJECTCLASS.toLowerCase().equals(lowerKey)) { + // objectClass and service.id must not be overwritten + LOG.info("exportService called with additional properties map that contained illegal key: " + + key + ", the key is ignored"); + } else { + String origKey = keysLowerCase.get(lowerKey); + if (origKey != null) { + LOG.debug("Overwriting property [{}] with value [{}]", origKey, e.getValue()); + } else { + origKey = key; + keysLowerCase.put(lowerKey, origKey); + } + serviceProperties.put(origKey, e.getValue()); + } + } + } + + /** + * Returns a service's properties as a map. + * + * @param serviceReference a service reference + * @return the service's properties as a map + */ + private Map<String, Object> getProperties(ServiceReference<?> serviceReference) { + String[] keys = serviceReference.getPropertyKeys(); + Map<String, Object> props = new HashMap<String, Object>(keys.length); + for (String key : keys) { + Object val = serviceReference.getProperty(key); + props.put(key, val); + } + return props; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java new file mode 100644 index 0000000..fc3a67e --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java @@ -0,0 +1,99 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +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; + +public class RemoteServiceAdminInstance implements RemoteServiceAdmin { + + private final BundleContext bctx; + private final RemoteServiceAdminCore rsaCore; + + private boolean closed; + + public RemoteServiceAdminInstance(BundleContext bc, RemoteServiceAdminCore core) { + bctx = bc; + rsaCore = core; + } + + @Override + @SuppressWarnings("rawtypes") + public List<ExportRegistration> exportService(final ServiceReference ref, final Map properties) { + checkPermission(new EndpointPermission("*", EndpointPermission.EXPORT)); + return AccessController.doPrivileged(new PrivilegedAction<List<ExportRegistration>>() { + public List<ExportRegistration> run() { + return closed ? Collections.<ExportRegistration>emptyList() : rsaCore.exportService(ref, properties); + } + }); + } + + @Override + public Collection<ExportReference> getExportedServices() { + checkPermission(new EndpointPermission("*", EndpointPermission.READ)); + return closed ? null : rsaCore.getExportedServices(); + } + + @Override + public Collection<ImportReference> getImportedEndpoints() { + checkPermission(new EndpointPermission("*", EndpointPermission.READ)); + return closed ? null : rsaCore.getImportedEndpoints(); + } + + @Override + public ImportRegistration importService(final EndpointDescription endpoint) { + String frameworkUUID = bctx.getProperty(Constants.FRAMEWORK_UUID); + checkPermission(new EndpointPermission(endpoint, frameworkUUID, EndpointPermission.IMPORT)); + return AccessController.doPrivileged(new PrivilegedAction<ImportRegistration>() { + public ImportRegistration run() { + return closed ? null : rsaCore.importService(endpoint); + } + }); + } + + public void close(boolean closeAll) { + closed = true; + rsaCore.removeExportRegistrations(bctx.getBundle()); + if (closeAll) { + rsaCore.close(); + } + } + + private void checkPermission(EndpointPermission permission) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(permission); + } + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/4ed2614d/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java ---------------------------------------------------------------------- diff --git a/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java new file mode 100644 index 0000000..968ff16 --- /dev/null +++ b/dsw/cxf-dosgi-rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java @@ -0,0 +1,51 @@ +/** + * 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.cxf.dosgi.dsw.service; + +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteServiceadminFactory implements ServiceFactory<RemoteServiceAdmin> { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteServiceadminFactory.class); + + private final RemoteServiceAdminCore rsaCore; + private int instances; + + public RemoteServiceadminFactory(RemoteServiceAdminCore rsaCore) { + this.rsaCore = rsaCore; + } + + public synchronized RemoteServiceAdmin getService(Bundle b, ServiceRegistration<RemoteServiceAdmin> sreg) { + LOG.debug("new RemoteServiceAdmin ServiceInstance created for Bundle {}", b.getSymbolicName()); + instances++; + return new RemoteServiceAdminInstance(b.getBundleContext(), rsaCore); + } + + public synchronized void ungetService(Bundle b, ServiceRegistration<RemoteServiceAdmin> sreg, + RemoteServiceAdmin serviceObject) { + LOG.debug("RemoteServiceAdmin ServiceInstance removed for Bundle {}", b.getSymbolicName()); + instances--; + ((RemoteServiceAdminInstance)serviceObject).close(instances == 0); + } +}
