http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java ---------------------------------------------------------------------- diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java new file mode 100644 index 0000000..356de5e --- /dev/null +++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java @@ -0,0 +1,587 @@ +/** + * 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.apache.cxf.dosgi.dsw.api.EndpointHelper; +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()); + Map<String, Object> eprops = createEndpointProps(serviceProperties, interfaces); + BundleContext serviceContext = serviceReference.getBundle().getBundleContext(); + + // TODO unget service when export is destroyed + Object serviceO = serviceContext.getService(serviceReference); + Endpoint endpoint = provider.exportService(serviceO, serviceContext, eprops, 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; + } + + protected Map<String, Object> createEndpointProps(Map<String, Object> effectiveProps, + Class<?>[] ifaces) { + Map<String, Object> props = new HashMap<String, Object>(); + copyEndpointProperties(effectiveProps, props); + props.remove(org.osgi.framework.Constants.SERVICE_ID); + EndpointHelper.addObjectClass(props, ifaces); + props.put(RemoteConstants.ENDPOINT_SERVICE_ID, effectiveProps.get(org.osgi.framework.Constants.SERVICE_ID)); + String frameworkUUID = bctx.getProperty(org.osgi.framework.Constants.FRAMEWORK_UUID); + props.put(RemoteConstants.ENDPOINT_FRAMEWORK_UUID, frameworkUUID); + for (Class<?> iface : ifaces) { + String pkg = iface.getPackage().getName(); + props.put(RemoteConstants.ENDPOINT_PACKAGE_VERSION_ + pkg, PackageUtil.getVersion(iface, bctx)); + } + return props; + } + + + + private void copyEndpointProperties(Map<String, Object> sd, Map<String, Object> endpointProps) { + Set<Map.Entry<String, Object>> keys = sd.entrySet(); + for (Map.Entry<String, Object> entry : keys) { + String skey = entry.getKey(); + if (!skey.startsWith(".")) { + endpointProps.put(skey, entry.getValue()); + } + } + } +}
http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java ---------------------------------------------------------------------- diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java new file mode 100644 index 0000000..fc3a67e --- /dev/null +++ b/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/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java ---------------------------------------------------------------------- diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java new file mode 100644 index 0000000..968ff16 --- /dev/null +++ b/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); + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/StringPlus.java ---------------------------------------------------------------------- diff --git a/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/StringPlus.java b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/StringPlus.java new file mode 100644 index 0000000..e449f1d --- /dev/null +++ b/rsa/src/main/java/org/apache/cxf/dosgi/dsw/service/StringPlus.java @@ -0,0 +1,72 @@ +/** + * 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.Collection; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class StringPlus { + + private static final Logger LOG = LoggerFactory.getLogger(StringPlus.class); + + private StringPlus() { + // never constructed + } + + @SuppressWarnings("rawtypes") + public static String[] normalize(Object object) { + if (object instanceof String) { + String s = (String)object; + String[] values = s.split(","); + List<String> list = new ArrayList<String>(); + for (String val : values) { + String actualValue = val.trim(); + if (!actualValue.isEmpty()) { + list.add(actualValue); + } + } + return list.toArray(new String[list.size()]); + } + + if (object instanceof String[]) { + return (String[])object; + } + + if (object instanceof Collection) { + Collection col = (Collection)object; + List<String> ar = new ArrayList<String>(col.size()); + for (Object o : col) { + if (o instanceof String) { + String s = (String)o; + ar.add(s); + } else { + LOG.warn("stringPlus contained non string element in list! Element was skipped"); + } + } + return ar.toArray(new String[ar.size()]); + } + + return null; + } + +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactoryTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactoryTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactoryTest.java new file mode 100644 index 0000000..fbe2c02 --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactoryTest.java @@ -0,0 +1,92 @@ +/** + * 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 junit.framework.TestCase; + +import org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.RemoteConstants; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.isA; + + +public class ClientServiceFactoryTest extends TestCase { + + @SuppressWarnings({ + "rawtypes" + }) + public void testGetService() throws ClassNotFoundException { + final Object myTestProxyObject = new Object(); + + IMocksControl control = EasyMock.createControl(); + EndpointDescription endpoint = createTestEndpointDesc(); + ImportRegistrationImpl iri = new ImportRegistrationImpl(endpoint, null); + + BundleContext consumerContext = control.createMock(BundleContext.class); + Bundle consumerBundle = control.createMock(Bundle.class); + BundleWiring bundleWiring = control.createMock(BundleWiring.class); + EasyMock.expect(bundleWiring.getClassLoader()).andReturn(this.getClass().getClassLoader()); + EasyMock.expect(consumerBundle.adapt(BundleWiring.class)).andReturn(bundleWiring); + EasyMock.expect(consumerBundle.getBundleContext()).andReturn(consumerContext); + ServiceRegistration sreg = control.createMock(ServiceRegistration.class); + + + DistributionProvider handler = mockDistributionProvider(myTestProxyObject); + control.replay(); + + ClientServiceFactory csf = new ClientServiceFactory(endpoint, handler, iri); + assertSame(myTestProxyObject, csf.getService(consumerBundle, sreg)); + } + + /** + * Creating dummy class as I was not able to really mock it + * @param myTestProxyObject + * @return + */ + private DistributionProvider mockDistributionProvider(final Object proxy) { + DistributionProvider handler = EasyMock.createMock(DistributionProvider.class); + EasyMock.expect(handler.importEndpoint(anyObject(ClassLoader.class), + anyObject(BundleContext.class), + isA(Class[].class), + anyObject(EndpointDescription.class))).andReturn(proxy); + EasyMock.replay(handler); + return handler; + } + + private EndpointDescription createTestEndpointDesc() { + Map<String, Object> map = new HashMap<String, Object>(); + map.put(RemoteConstants.ENDPOINT_ID, "http://google.de"); + map.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myGreatConfiguration"); + map.put(Constants.OBJECTCLASS, new String[]{String.class.getName()}); + EndpointDescription endpoint = new EndpointDescription(map); + return endpoint; + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTrackerTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTrackerTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTrackerTest.java new file mode 100644 index 0000000..6ae7004 --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderTrackerTest.java @@ -0,0 +1,82 @@ +/** + * 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 org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.junit.Test; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.RemoteConstants; + +public class DistributionProviderTrackerTest { + + @SuppressWarnings({ + "unchecked", "rawtypes" + }) + @Test + public void testAddingRemoved() throws InvalidSyntaxException { + IMocksControl c = EasyMock.createControl(); + DistributionProvider provider = c.createMock(DistributionProvider.class); + + ServiceReference<DistributionProvider> providerRef = c.createMock(ServiceReference.class); + EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_INTENTS_SUPPORTED)).andReturn(""); + EasyMock.expect(providerRef.getProperty(RemoteConstants.REMOTE_CONFIGS_SUPPORTED)).andReturn(""); + + BundleContext context = c.createMock(BundleContext.class); + Filter filter = FrameworkUtil.createFilter("(objectClass=org.apache.cxf.dosgi.dsw.api.DistributionProvider)"); + EasyMock.expect(context.createFilter("(objectClass=org.apache.cxf.dosgi.dsw.api.DistributionProvider)")) + .andReturn(filter); + EasyMock.expect(context.getService(providerRef)).andReturn(provider); + ServiceRegistration rsaReg = c.createMock(ServiceRegistration.class); + EasyMock.expect(context.registerService(EasyMock.isA(String.class), EasyMock.isA(ServiceFactory.class), + EasyMock.isA(Dictionary.class))) + .andReturn(rsaReg).atLeastOnce(); + + context.addServiceListener(EasyMock.isA(ServiceListener.class), EasyMock.isA(String.class)); + EasyMock.expectLastCall(); + + final BundleContext apiContext = c.createMock(BundleContext.class); + c.replay(); + DistributionProviderTracker tracker = new DistributionProviderTracker(context) { + protected BundleContext getAPIContext() { + return apiContext; + }; + }; + tracker.addingService(providerRef); + c.verify(); + + c.reset(); + rsaReg.unregister(); + EasyMock.expectLastCall(); + EasyMock.expect(context.ungetService(providerRef)).andReturn(true); + c.replay(); + tracker.removedService(providerRef, rsaReg); + c.verify(); + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java new file mode 100644 index 0000000..71d3ed7 --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java @@ -0,0 +1,189 @@ +/** + * 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.Arrays; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.List; +import java.util.UUID; + +import org.apache.cxf.dosgi.dsw.api.Endpoint; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.junit.Assert; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +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.ExportReference; +import org.osgi.service.remoteserviceadmin.ExportRegistration; +import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class EventProducerTest { + + + @Test + public void testPublishNotification() throws Exception { + RemoteServiceAdminCore remoteServiceAdminCore = EasyMock.createNiceMock(RemoteServiceAdminCore.class); + EasyMock.replay(remoteServiceAdminCore); + + final EndpointDescription epd = EasyMock.createNiceMock(EndpointDescription.class); + EasyMock.expect(epd.getServiceId()).andReturn(Long.MAX_VALUE).anyTimes(); + final String uuid = UUID.randomUUID().toString(); + EasyMock.expect(epd.getFrameworkUUID()).andReturn(uuid).anyTimes(); + EasyMock.expect(epd.getId()).andReturn("foo://bar").anyTimes(); + final List<String> interfaces = Arrays.asList("org.foo.Bar", "org.boo.Far"); + EasyMock.expect(epd.getInterfaces()).andReturn(interfaces).anyTimes(); + EasyMock.expect(epd.getConfigurationTypes()).andReturn(Arrays.asList("org.apache.cxf.ws")).anyTimes(); + EasyMock.replay(epd); + final ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class); + EasyMock.replay(sref); + + final Bundle bundle = EasyMock.createNiceMock(Bundle.class); + EasyMock.expect(bundle.getBundleId()).andReturn(42L).anyTimes(); + EasyMock.expect(bundle.getSymbolicName()).andReturn("test.bundle").anyTimes(); + Dictionary<String, String> headers = new Hashtable<String, String>(); + headers.put("Bundle-Version", "1.2.3.test"); + EasyMock.expect(bundle.getHeaders()).andReturn(headers).anyTimes(); + EasyMock.replay(bundle); + + EventAdmin ea = EasyMock.createNiceMock(EventAdmin.class); + ea.postEvent((Event) EasyMock.anyObject()); + EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() { + @Override + public Object answer() throws Throwable { + Event event = (Event) EasyMock.getCurrentArguments()[0]; + + Assert.assertEquals("org/osgi/service/remoteserviceadmin/EXPORT_REGISTRATION", event.getTopic()); + Assert.assertSame(bundle, event.getProperty("bundle")); + Assert.assertEquals(42L, event.getProperty("bundle.id")); + Assert.assertEquals("test.bundle", event.getProperty("bundle.symbolicname")); + Assert.assertEquals(new Version(1, 2, 3, "test"), event.getProperty("bundle.version")); + Assert.assertNull(event.getProperty("cause")); + Assert.assertEquals(epd, event.getProperty("export.registration")); + + Assert.assertEquals(Long.MAX_VALUE, event.getProperty("service.remote.id")); + Assert.assertEquals(uuid, event.getProperty("service.remote.uuid")); + Assert.assertEquals("foo://bar", event.getProperty("service.remote.uri")); + Assert.assertTrue(Arrays.equals(interfaces.toArray(new String[] {}), + (String[]) event.getProperty("objectClass"))); + + Assert.assertNotNull(event.getProperty("timestamp")); + + RemoteServiceAdminEvent rsae = (RemoteServiceAdminEvent) event.getProperty("event"); + Assert.assertNull(rsae.getException()); + Assert.assertEquals(RemoteServiceAdminEvent.EXPORT_REGISTRATION, rsae.getType()); + Assert.assertSame(bundle, rsae.getSource()); + ExportReference er = rsae.getExportReference(); + Assert.assertSame(epd, er.getExportedEndpoint()); + Assert.assertSame(sref, er.getExportedService()); + + return null; + } + }); + EasyMock.replay(ea); + + ServiceReference eaSref = EasyMock.createNiceMock(ServiceReference.class); + EasyMock.replay(eaSref); + + BundleContext bc = EasyMock.createNiceMock(BundleContext.class); + EasyMock.expect(bc.getBundle()).andReturn(bundle).anyTimes(); + EasyMock.expect(bc.getAllServiceReferences(EventAdmin.class.getName(), null)) + .andReturn(new ServiceReference[] {eaSref}).anyTimes(); + EasyMock.expect(bc.getService(eaSref)).andReturn(ea).anyTimes(); + Endpoint endpoint = EasyMock.mock(Endpoint.class); + EasyMock.expect(endpoint.description()).andReturn(epd); + EasyMock.replay(endpoint); + EasyMock.replay(bc); + EventProducer eventProducer = new EventProducer(bc); + + ExportRegistrationImpl ereg = new ExportRegistrationImpl(sref, endpoint, remoteServiceAdminCore); + eventProducer.publishNotification(ereg); + } + + @Test + public void testPublishErrorNotification() throws Exception { + RemoteServiceAdminCore rsaCore = EasyMock.createNiceMock(RemoteServiceAdminCore.class); + EasyMock.replay(rsaCore); + + final EndpointDescription endpoint = EasyMock.createNiceMock(EndpointDescription.class); + EasyMock.expect(endpoint.getInterfaces()).andReturn(Arrays.asList("org.foo.Bar")).anyTimes(); + EasyMock.replay(endpoint); + final ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class); + EasyMock.replay(sref); + + final Bundle bundle = EasyMock.createNiceMock(Bundle.class); + EasyMock.expect(bundle.getBundleId()).andReturn(42L).anyTimes(); + EasyMock.expect(bundle.getSymbolicName()).andReturn("test.bundle").anyTimes(); + EasyMock.expect(bundle.getHeaders()).andReturn(new Hashtable<String, String>()).anyTimes(); + EasyMock.replay(bundle); + + final Exception exportException = new Exception(); + + EventAdmin ea = EasyMock.createNiceMock(EventAdmin.class); + ea.postEvent((Event) EasyMock.anyObject()); + EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() { + @Override + public Object answer() throws Throwable { + Event event = (Event) EasyMock.getCurrentArguments()[0]; + + Assert.assertEquals("org/osgi/service/remoteserviceadmin/EXPORT_ERROR", event.getTopic()); + Assert.assertSame(bundle, event.getProperty("bundle")); + Assert.assertEquals(42L, event.getProperty("bundle.id")); + Assert.assertEquals("test.bundle", event.getProperty("bundle.symbolicname")); + Assert.assertEquals(new Version("0"), event.getProperty("bundle.version")); + Assert.assertSame(exportException, event.getProperty("cause")); + Assert.assertEquals(endpoint, event.getProperty("export.registration")); + Assert.assertTrue(Arrays.equals(new String[] {"org.foo.Bar"}, + (String[]) event.getProperty("objectClass"))); + + RemoteServiceAdminEvent rsae = (RemoteServiceAdminEvent) event.getProperty("event"); + Assert.assertSame(exportException, rsae.getException()); + Assert.assertEquals(RemoteServiceAdminEvent.EXPORT_ERROR, rsae.getType()); + Assert.assertSame(bundle, rsae.getSource()); + ExportReference er = rsae.getExportReference(); + Assert.assertSame(endpoint, er.getExportedEndpoint()); + Assert.assertSame(sref, er.getExportedService()); + + return null; + } + }); + EasyMock.replay(ea); + + ServiceReference eaSref = EasyMock.createNiceMock(ServiceReference.class); + EasyMock.replay(eaSref); + + BundleContext bc = EasyMock.createNiceMock(BundleContext.class); + EasyMock.expect(bc.getBundle()).andReturn(bundle).anyTimes(); + EasyMock.expect(bc.getAllServiceReferences(EventAdmin.class.getName(), null)) + .andReturn(new ServiceReference[] {eaSref}).anyTimes(); + EasyMock.expect(bc.getService(eaSref)).andReturn(ea).anyTimes(); + EasyMock.replay(bc); + EventProducer eventProducer = new EventProducer(bc); + + ExportRegistrationImpl ereg = new ExportRegistrationImpl(rsaCore, exportException); + eventProducer.publishNotification(Arrays.<ExportRegistration>asList(ereg)); + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java new file mode 100644 index 0000000..23902a5 --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java @@ -0,0 +1,178 @@ +/** + * 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.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.junit.Test; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.remoteserviceadmin.EndpointDescription; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class ImportRegistrationImplTest { + + @Test + public void testException() { + IMocksControl c = EasyMock.createNiceControl(); + Exception e = c.createMock(Exception.class); + c.replay(); + + ImportRegistrationImpl i = new ImportRegistrationImpl(e); + + assertEquals(e, i.getException()); + assertNull(i.getImportedEndpointDescription()); + assertNull(i.getImportedService()); + assertEquals(i, i.getParent()); + } + + @Test + public void testDefaultCtor() { + IMocksControl c = EasyMock.createNiceControl(); + EndpointDescription endpoint = c.createMock(EndpointDescription.class); + RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class); + + c.replay(); + + ImportRegistrationImpl i = new ImportRegistrationImpl(endpoint, rsac); + + assertNull(i.getException()); + assertEquals(i, i.getParent()); + assertEquals(endpoint, i.getImportedEndpointDescription()); + } + + @SuppressWarnings("rawtypes") + @Test + public void testCloneAndClose() { + IMocksControl c = EasyMock.createControl(); + EndpointDescription endpoint = c.createMock(EndpointDescription.class); + RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class); + + ServiceRegistration sr = c.createMock(ServiceRegistration.class); + ServiceReference sref = c.createMock(ServiceReference.class); + EasyMock.expect(sr.getReference()).andReturn(sref).anyTimes(); + + c.replay(); + + ImportRegistrationImpl i1 = new ImportRegistrationImpl(endpoint, rsac); + + ImportRegistrationImpl i2 = new ImportRegistrationImpl(i1); + + ImportRegistrationImpl i3 = new ImportRegistrationImpl(i2); + + try { + i2.setImportedServiceRegistration(sr); + assertTrue("An exception should be thrown here !", false); + } catch (IllegalStateException e) { + // must be thrown here + } + + i1.setImportedServiceRegistration(sr); + + assertEquals(i1, i1.getParent()); + assertEquals(i1, i2.getParent()); + assertEquals(i1, i3.getParent()); + + assertEquals(endpoint, i1.getImportedEndpointDescription()); + assertEquals(endpoint, i2.getImportedEndpointDescription()); + assertEquals(endpoint, i3.getImportedEndpointDescription()); + + c.verify(); + c.reset(); + + rsac.removeImportRegistration(EasyMock.eq(i3)); + EasyMock.expectLastCall().once(); + + c.replay(); + + i3.close(); + i3.close(); // shouldn't change anything + + assertNull(i3.getImportedEndpointDescription()); + + c.verify(); + c.reset(); + + rsac.removeImportRegistration(EasyMock.eq(i1)); + EasyMock.expectLastCall().once(); + + c.replay(); + + i1.close(); + + c.verify(); + c.reset(); + + rsac.removeImportRegistration(EasyMock.eq(i2)); + EasyMock.expectLastCall().once(); + + sr.unregister(); + EasyMock.expectLastCall().once(); + + c.replay(); + + i2.close(); + + c.verify(); + } + + @Test + public void testCloseAll() { + IMocksControl c = EasyMock.createControl(); + EndpointDescription endpoint = c.createMock(EndpointDescription.class); + RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class); + + c.replay(); + + ImportRegistrationImpl i1 = new ImportRegistrationImpl(endpoint, rsac); + + ImportRegistrationImpl i2 = new ImportRegistrationImpl(i1); + + ImportRegistrationImpl i3 = new ImportRegistrationImpl(i2); + + assertEquals(i1, i1.getParent()); + assertEquals(i1, i2.getParent()); + assertEquals(i1, i3.getParent()); + + c.verify(); + c.reset(); + + rsac.removeImportRegistration(EasyMock.eq(i2)); + EasyMock.expectLastCall().once(); + + c.replay(); + + i2.close(); + + c.verify(); + c.reset(); + + rsac.removeImportRegistration(EasyMock.eq(i1)); + EasyMock.expectLastCall().once(); + rsac.removeImportRegistration(EasyMock.eq(i3)); + EasyMock.expectLastCall().once(); + + c.replay(); + i3.closeAll(); + c.verify(); + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java new file mode 100644 index 0000000..b0f58eb --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java @@ -0,0 +1,517 @@ +/** + * 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.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.dosgi.dsw.api.DistributionProvider; +import org.apache.cxf.dosgi.dsw.api.Endpoint; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.easymock.IMocksControl; +import org.junit.Assert; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; +import org.osgi.service.remoteserviceadmin.EndpointDescription; +import org.osgi.service.remoteserviceadmin.ExportRegistration; +import org.osgi.service.remoteserviceadmin.ImportRegistration; +import org.osgi.service.remoteserviceadmin.RemoteConstants; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.isA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings({ + "rawtypes", "unchecked", "deprecation" + }) +public class RemoteServiceAdminCoreTest { + + private static final String MYCONFIG = "myconfig"; + + @Test + public void testDontExportOwnServiceProxies() throws InvalidSyntaxException { + IMocksControl c = EasyMock.createControl(); + Bundle b = c.createMock(Bundle.class); + BundleContext bc = c.createMock(BundleContext.class); + + EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes(); + bc.addServiceListener(EasyMock.<ServiceListener>anyObject(), EasyMock.<String>anyObject()); + EasyMock.expectLastCall().anyTimes(); + bc.removeServiceListener(EasyMock.<ServiceListener>anyObject()); + EasyMock.expectLastCall().anyTimes(); + + Dictionary<String, String> d = new Hashtable<String, String>(); + EasyMock.expect(b.getHeaders()).andReturn(d).anyTimes(); + + ServiceReference sref = c.createMock(ServiceReference.class); + EasyMock.expect(sref.getBundle()).andReturn(b).anyTimes(); + EasyMock.expect(sref.getPropertyKeys()) + .andReturn(new String[]{"objectClass", "service.exported.interfaces"}).anyTimes(); + EasyMock.expect(sref.getProperty("objectClass")).andReturn(new String[] {"a.b.C"}).anyTimes(); + EasyMock.expect(sref.getProperty(RemoteConstants.SERVICE_IMPORTED)).andReturn(true).anyTimes(); + EasyMock.expect(sref.getProperty("service.exported.interfaces")).andReturn("*").anyTimes(); + + DistributionProvider provider = c.createMock(DistributionProvider.class); + + c.replay(); + + RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, bc, provider); + + // must return an empty List as sref if from the same bundle + List<ExportRegistration> exRefs = rsaCore.exportService(sref, null); + + assertNotNull(exRefs); + assertEquals(0, exRefs.size()); + + // must be empty + assertEquals(rsaCore.getExportedServices().size(), 0); + + c.verify(); + } + + @Test + public void testImport() { + IMocksControl c = EasyMock.createNiceControl(); + Bundle b = c.createMock(Bundle.class); + BundleContext bc = c.createMock(BundleContext.class); + + Dictionary<String, String> d = new Hashtable<String, String>(); + EasyMock.expect(b.getHeaders()).andReturn(d).anyTimes(); + + EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes(); + EasyMock.expect(b.getSymbolicName()).andReturn("BundleName").anyTimes(); + + EndpointDescription endpoint = creatEndpointDesc("unsupportedConfiguration"); + + DistributionProvider provider = c.createMock(DistributionProvider.class); + EasyMock.expect(provider.getSupportedTypes()) + .andReturn(new String[]{MYCONFIG}).atLeastOnce(); + c.replay(); + + RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, bc, provider); + + // must be null as the endpoint doesn't contain any usable configurations + assertNull(rsaCore.importService(endpoint)); + // must be empty + assertEquals(0, rsaCore.getImportedEndpoints().size()); + + EndpointDescription endpoint2 = creatEndpointDesc(MYCONFIG); + + ImportRegistration ireg = rsaCore.importService(endpoint2); + assertNotNull(ireg); + + assertEquals(1, rsaCore.getImportedEndpoints().size()); + + // lets import the same endpoint once more -> should get a copy of the ImportRegistration + ImportRegistration ireg2 = rsaCore.importService(endpoint2); + assertNotNull(ireg2); + assertEquals(2, rsaCore.getImportedEndpoints().size()); + + assertEquals(ireg.getImportReference(), (rsaCore.getImportedEndpoints().toArray())[0]); + + assertEquals(ireg.getImportReference().getImportedEndpoint(), ireg2.getImportReference() + .getImportedEndpoint()); + + // remove the registration + + // first call shouldn't remove the import + ireg2.close(); + assertEquals(1, rsaCore.getImportedEndpoints().size()); + + // second call should really close and remove the import + ireg.close(); + assertEquals(0, rsaCore.getImportedEndpoints().size()); + + c.verify(); + } + + private EndpointDescription creatEndpointDesc(String configType) { + Map<String, Object> p = new HashMap<String, Object>(); + p.put(RemoteConstants.ENDPOINT_ID, "http://google.de"); + p.put(Constants.OBJECTCLASS, new String[] { + "es.schaaf.my.class" + }); + p.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, configType); + EndpointDescription endpoint = new EndpointDescription(p); + return endpoint; + } + + @Test + public void testExport() throws Exception { + BundleContext bc = EasyMock.createMock(BundleContext.class); + EasyMock.expect(bc.getProperty(Constants.FRAMEWORK_VERSION)).andReturn(null).anyTimes(); + bc.addServiceListener(EasyMock.<ServiceListener>anyObject(), EasyMock.<String>anyObject()); + EasyMock.expectLastCall().anyTimes(); + bc.removeServiceListener(EasyMock.<ServiceListener>anyObject()); + EasyMock.expectLastCall().anyTimes(); + EasyMock.expect(bc.getServiceReferences(EasyMock.<String>anyObject(), + EasyMock.<String>anyObject())).andReturn(null).anyTimes(); + EasyMock.expect(bc.getAllServiceReferences(EasyMock.<String>anyObject(), + EasyMock.<String>anyObject())).andReturn(null).anyTimes(); + + Bundle b = createDummyRsaBundle(bc); + + final Map<String, Object> sProps = new HashMap<String, Object>(); + sProps.put("objectClass", new String[] {"java.lang.Runnable"}); + sProps.put("service.id", 51L); + sProps.put("myProp", "myVal"); + sProps.put("service.exported.interfaces", "*"); + ServiceReference sref = mockServiceReference(sProps); + + Runnable svcObject = EasyMock.createNiceMock(Runnable.class); + EasyMock.replay(svcObject); + + EasyMock.expect(bc.getService(sref)).andReturn(svcObject).anyTimes(); + EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes(); + EasyMock.expect(bc.createFilter("(service.id=51)")) + .andReturn(FrameworkUtil.createFilter("(service.id=51)")).anyTimes(); + EasyMock.expect(bc.getProperty(org.osgi.framework.Constants.FRAMEWORK_UUID)).andReturn("1111"); + EasyMock.expect(bc.getServiceReference(PackageAdmin.class)).andReturn(null); + EasyMock.replay(bc); + + Map<String, Object> eProps = new HashMap<String, Object>(sProps); + eProps.put("endpoint.id", "http://something"); + eProps.put("service.imported.configs", new String[] {"org.apache.cxf.ws"}); + final EndpointDescription epd = new EndpointDescription(eProps); + Endpoint er = new Endpoint() { + + @Override + public void close() throws IOException { + } + + @Override + public EndpointDescription description() { + return epd; + } + }; + + DistributionProvider handler = EasyMock.createMock(DistributionProvider.class); + EasyMock.expect(handler.exportService(anyObject(), + anyObject(BundleContext.class), + anyObject(Map.class), isA(Class[].class))).andReturn(er); + EasyMock.replay(handler); + + RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, bc, handler); + + // Export the service for the first time + List<ExportRegistration> ereg = rsaCore.exportService(sref, null); + assertEquals(1, ereg.size()); + assertNull(ereg.get(0).getException()); + assertSame(sref, ereg.get(0).getExportReference().getExportedService()); + EndpointDescription endpoint = ereg.get(0).getExportReference().getExportedEndpoint(); + + Map<String, Object> edProps = endpoint.getProperties(); + assertEquals("http://something", edProps.get("endpoint.id")); + assertNotNull(edProps.get("service.imported")); + assertTrue(Arrays.equals(new String[] {"java.lang.Runnable"}, + (Object[]) edProps.get("objectClass"))); + assertTrue(Arrays.equals(new String[] {"org.apache.cxf.ws"}, + (Object[]) edProps.get("service.imported.configs"))); + + // Ask to export the same service again, this should not go through the whole process again but simply return + // a copy of the first instance. + final Map<String, Object> sProps2 = new HashMap<String, Object>(); + sProps2.put("objectClass", new String[] {"java.lang.Runnable"}); + sProps2.put("service.id", 51L); + sProps2.put("service.exported.interfaces", "*"); + ServiceReference sref2 = mockServiceReference(sProps2); + Map<String, Object> props2 = new HashMap<String, Object>(); + props2.put("myProp", "myVal"); + List<ExportRegistration> ereg2 = rsaCore.exportService(sref2, props2); + + assertEquals(1, ereg2.size()); + assertNull(ereg2.get(0).getException()); + assertEquals(ereg.get(0).getExportReference().getExportedEndpoint().getProperties(), + ereg2.get(0).getExportReference().getExportedEndpoint().getProperties()); + + // Look at the exportedServices data structure + Field field = RemoteServiceAdminCore.class.getDeclaredField("exportedServices"); + field.setAccessible(true); + Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices = + (Map<Map<String, Object>, Collection<ExportRegistration>>) field.get(rsaCore); + + assertEquals("One service was exported", 1, exportedServices.size()); + assertEquals("There are 2 export registrations (identical copies)", + 2, exportedServices.values().iterator().next().size()); + + // Unregister one of the exports + rsaCore.removeExportRegistration((ExportRegistrationImpl) ereg.get(0)); + assertEquals("One service was exported", 1, exportedServices.size()); + assertEquals("There 1 export registrations left", + 1, exportedServices.values().iterator().next().size()); + + // Unregister the other export + rsaCore.removeExportRegistration((ExportRegistrationImpl) ereg2.get(0)); + assertEquals("No more exported services", 0, exportedServices.size()); + } + + private Bundle createDummyRsaBundle(BundleContext bc) { + Bundle b = EasyMock.createNiceMock(Bundle.class); + EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes(); + EasyMock.expect(b.getSymbolicName()).andReturn("rsabundle").anyTimes(); + EasyMock.expect(b.getHeaders()).andReturn(new Hashtable<String, String>()).anyTimes(); + EasyMock.replay(b); + return b; + } + + @Test + public void testExportException() throws Exception { + BundleContext bc = EasyMock.createNiceMock(BundleContext.class); + + Bundle b = createDummyRsaBundle(bc); + + final Map<String, Object> sProps = new HashMap<String, Object>(); + sProps.put("objectClass", new String[] {"java.lang.Runnable"}); + sProps.put("service.id", 51L); + sProps.put("service.exported.interfaces", "*"); + ServiceReference sref = mockServiceReference(sProps); + + Runnable svcObject = EasyMock.createNiceMock(Runnable.class); + EasyMock.replay(svcObject); + + EasyMock.expect(bc.getService(sref)).andReturn(svcObject).anyTimes(); + EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes(); + EasyMock.replay(bc); + + Map<String, Object> eProps = new HashMap<String, Object>(sProps); + eProps.put("endpoint.id", "http://something"); + eProps.put("service.imported.configs", new String[] {"org.apache.cxf.ws"}); + + DistributionProvider handler = EasyMock.createMock(DistributionProvider.class); + EasyMock.expect(handler.exportService(anyObject(), + anyObject(BundleContext.class), + anyObject(Map.class), isA(Class[].class))).andThrow(new TestException()); + EasyMock.replay(handler); + + RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, bc, handler); + + List<ExportRegistration> ereg = rsaCore.exportService(sref, sProps); + assertEquals(1, ereg.size()); + assertTrue(ereg.get(0).getException() instanceof TestException); + + // Look at the exportedServices data structure + Field field = RemoteServiceAdminCore.class.getDeclaredField("exportedServices"); + field.setAccessible(true); + Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices = + (Map<Map<String, Object>, Collection<ExportRegistration>>) field.get(rsaCore); + + assertEquals("One service was exported", 1, exportedServices.size()); + assertEquals("There is 1 export registration", + 1, exportedServices.values().iterator().next().size()); + + } + + private ServiceReference mockServiceReference(final Map<String, Object> sProps) throws ClassNotFoundException { + BundleContext bc = EasyMock.createNiceMock(BundleContext.class); + + Bundle b = EasyMock.createNiceMock(Bundle.class); + EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes(); + EasyMock.expect((Class)b.loadClass(Runnable.class.getName())).andReturn(Runnable.class); + EasyMock.replay(b); + + EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes(); + EasyMock.replay(bc); + + ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class); + EasyMock.expect(sref.getBundle()).andReturn(b).anyTimes(); + EasyMock.expect(sref.getPropertyKeys()).andReturn(sProps.keySet().toArray(new String[] {})).anyTimes(); + EasyMock.expect(sref.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() { + @Override + public Object answer() throws Throwable { + return sProps.get(EasyMock.getCurrentArguments()[0]); + } + }).anyTimes(); + EasyMock.replay(sref); + return sref; + } + + @SuppressWarnings("serial") + private static class TestException extends RuntimeException { + } + + @Test + public void testOverlayProperties() { + Map<String, Object> sProps = new HashMap<String, Object>(); + Map<String, Object> aProps = new HashMap<String, Object>(); + + RemoteServiceAdminCore.overlayProperties(sProps, aProps); + assertEquals(0, sProps.size()); + + sProps.put("aaa", "aval"); + sProps.put("bbb", "bval"); + sProps.put(Constants.OBJECTCLASS, new String[] {"X"}); + sProps.put(Constants.SERVICE_ID, 17L); + + aProps.put("AAA", "achanged"); + aProps.put("CCC", "CVAL"); + aProps.put(Constants.OBJECTCLASS, new String[] {"Y"}); + aProps.put(Constants.SERVICE_ID.toUpperCase(), 51L); + + Map<String, Object> aPropsOrg = new HashMap<String, Object>(aProps); + RemoteServiceAdminCore.overlayProperties(sProps, aProps); + assertEquals("The additional properties should not be modified", aPropsOrg, aProps); + + assertEquals(5, sProps.size()); + assertEquals("achanged", sProps.get("aaa")); + assertEquals("bval", sProps.get("bbb")); + assertEquals("CVAL", sProps.get("CCC")); + assertTrue("Should not be possible to override the objectClass property", + Arrays.equals(new String[] {"X"}, (Object[]) sProps.get(Constants.OBJECTCLASS))); + assertEquals("Should not be possible to override the service.id property", + 17L, sProps.get(Constants.SERVICE_ID)); + } + + @Test + public void testOverlayProperties2() { + Map<String, Object> original = new HashMap<String, Object>(); + + original.put("MyProp", "my value"); + original.put(Constants.OBJECTCLASS, "myClass"); + + Map<String, Object> copy = new HashMap<String, Object>(); + copy.putAll(original); + + // nothing should change here + Map<String, Object> overload = new HashMap<String, Object>(); + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size(), copy.size()); + for (Object key : original.keySet()) { + assertEquals(original.get(key), copy.get(key)); + } + + copy.clear(); + copy.putAll(original); + + // a property should be added + overload = new HashMap<String, Object>(); + overload.put("new", "prop"); + + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size() + 1, copy.size()); + for (Object key : original.keySet()) { + assertEquals(original.get(key), copy.get(key)); + } + assertNotNull(overload.get("new")); + assertEquals("prop", overload.get("new")); + + copy.clear(); + copy.putAll(original); + + // only one property should be added + overload = new HashMap<String, Object>(); + overload.put("new", "prop"); + overload.put("NEW", "prop"); + + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size() + 1, copy.size()); + for (Object key : original.keySet()) { + assertEquals(original.get(key), copy.get(key)); + } + assertNotNull(overload.get("new")); + assertEquals("prop", overload.get("new")); + + copy.clear(); + copy.putAll(original); + + // nothing should change here + overload = new HashMap<String, Object>(); + overload.put(Constants.OBJECTCLASS, "assd"); + overload.put(Constants.SERVICE_ID, "asasdasd"); + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size(), copy.size()); + for (Object key : original.keySet()) { + assertEquals(original.get(key), copy.get(key)); + } + + copy.clear(); + copy.putAll(original); + + // overwrite own prop + overload = new HashMap<String, Object>(); + overload.put("MyProp", "newValue"); + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size(), copy.size()); + for (Object key : original.keySet()) { + if (!"MyProp".equals(key)) { + assertEquals(original.get(key), copy.get(key)); + } + } + assertEquals("newValue", copy.get("MyProp")); + + copy.clear(); + copy.putAll(original); + + // overwrite own prop in different case + overload = new HashMap<String, Object>(); + overload.put("MYPROP", "newValue"); + RemoteServiceAdminCore.overlayProperties(copy, overload); + + assertEquals(original.size(), copy.size()); + for (Object key : original.keySet()) { + if (!"MyProp".equals(key)) { + assertEquals(original.get(key), copy.get(key)); + } + } + assertEquals("newValue", copy.get("MyProp")); + } + + @Test + public void testCreateEndpointProps() { + BundleContext bc = EasyMock.createNiceMock(BundleContext.class); + EasyMock.expect(bc.getProperty("org.osgi.framework.uuid")).andReturn("some_uuid1"); + EasyMock.replay(bc); + + Map<String, Object> sd = new HashMap<String, Object>(); + sd.put(org.osgi.framework.Constants.SERVICE_ID, 42); + DistributionProvider provider = null; + RemoteServiceAdminCore rsa = new RemoteServiceAdminCore(bc, bc, provider); + Map<String, Object> props = rsa.createEndpointProps(sd, new Class[]{String.class}); + + Assert.assertFalse(props.containsKey(org.osgi.framework.Constants.SERVICE_ID)); + assertEquals(42, props.get(RemoteConstants.ENDPOINT_SERVICE_ID)); + assertEquals("some_uuid1", props.get(RemoteConstants.ENDPOINT_FRAMEWORK_UUID)); + assertEquals(Arrays.asList("java.lang.String"), + Arrays.asList((Object[]) props.get(org.osgi.framework.Constants.OBJECTCLASS))); + assertEquals("0.0.0", props.get("endpoint.package.version.java.lang")); + } +} http://git-wip-us.apache.org/repos/asf/aries-rsa/blob/d73a3a7f/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/StringPlusTest.java ---------------------------------------------------------------------- diff --git a/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/StringPlusTest.java b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/StringPlusTest.java new file mode 100644 index 0000000..5447966 --- /dev/null +++ b/rsa/src/test/java/org/apache/cxf/dosgi/dsw/service/StringPlusTest.java @@ -0,0 +1,63 @@ +/** + * 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.Collection; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class StringPlusTest { + + @Test + public void testSplitString() { + String[] values = StringPlus.normalize("1, 2"); + assertEquals(2, values.length); + assertEquals(values[0], "1"); + assertEquals(values[1], "2"); + } + + @Test + public void testNormalizeStringPlus() { + String s1 = "s1"; + String s2 = "s2"; + String s3 = "s3"; + + String[] sa = new String[] { + s1, s2, s3 + }; + + Collection<Object> sl = new ArrayList<Object>(4); + sl.add(s1); + sl.add(s2); + sl.add(s3); + sl.add(new Object()); // must be skipped + + assertArrayEquals(null, StringPlus.normalize(new Object())); + assertArrayEquals(new String[] { + s1 + }, StringPlus.normalize(s1)); + assertArrayEquals(sa, StringPlus.normalize(sa)); + assertArrayEquals(sa, StringPlus.normalize(sl)); + } + +}
