/* * 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.felix.connect.felix.framework; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.Enumeration; import java.util.Map; import java.util.Set; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.ServiceException; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleRevision; import org.apache.felix.connect.felix.framework.util.MapToDictionary; import org.apache.felix.connect.felix.framework.util.StringMap; import org.apache.felix.connect.felix.framework.util.Util; class ServiceRegistrationImpl<T> implements ServiceRegistration<T> { // Service registry. private final ServiceRegistry m_registry; // Bundle providing the service. private final Bundle m_bundle; // Interfaces associated with the service object. private final String[] m_classes; // Service Id associated with the service object. private final Long m_serviceId; // Service object. private volatile Object m_svcObj; // Service factory interface. private volatile ServiceFactory<T> m_factory; // Associated property dictionary. private volatile Map m_propMap = new StringMap(false); // Re-usable service reference. private final ServiceReferenceImpl<T> m_ref; // Flag indicating that we are unregistering. private volatile boolean m_isUnregistering = false; public ServiceRegistrationImpl(ServiceRegistry registry, Bundle bundle, String[] classes, Long serviceId, Object svcObj, Dictionary dict) { m_registry = registry; m_bundle = bundle; m_classes = classes; m_serviceId = serviceId; m_svcObj = svcObj; m_factory = (m_svcObj instanceof ServiceFactory) ? (ServiceFactory) m_svcObj : null; initializeProperties(dict); // This reference is the "standard" reference for this // service and will always be returned by getReference(). m_ref = new ServiceReferenceImpl(); } protected synchronized boolean isValid() { return (m_svcObj != null); } protected synchronized void invalidate() { m_svcObj = null; } public synchronized ServiceReferenceImpl<T> getReference() { // Make sure registration is valid. if (!isValid()) { throw new IllegalStateException( "The service registration is no longer valid."); } return m_ref; } public void setProperties(Dictionary dict) { Map oldProps; synchronized (this) { // Make sure registration is valid. if (!isValid()) { throw new IllegalStateException( "The service registration is no longer valid."); } // Remember old properties. oldProps = m_propMap; // Set the properties. initializeProperties(dict); } // Tell registry about it. m_registry.servicePropertiesModified(this, new MapToDictionary(oldProps)); } public void unregister() { synchronized (this) { if (!isValid() || m_isUnregistering) { throw new IllegalStateException("Service already unregistered."); } m_isUnregistering = true; } m_registry.unregisterService(m_bundle, this); synchronized (this) { m_svcObj = null; m_factory = null; } } // // Utility methods. // Object getProperty(String key) { return m_propMap.get(key); } private String[] getPropertyKeys() { Set s = m_propMap.keySet(); return (String[]) s.toArray(new String[s.size()]); } private Bundle[] getUsingBundles() { return m_registry.getUsingBundles(m_ref); } /** * This method provides direct access to the associated service object; it * generally should not be used by anyone other than the service registry * itself. * * @return The service object associated with the registration. */ Object getService() { return m_svcObj; } Object getService(Bundle acqBundle) { // If the service object is a service factory, then // let it create the service object. if (m_factory != null) { Object svcObj = null; svcObj = getFactoryUnchecked(acqBundle); return svcObj; } else { return m_svcObj; } } void ungetService(Bundle relBundle, T svcObj) { // If the service object is a service factory, then // let it release the service object. if (m_factory != null) { try { ungetFactoryUnchecked(relBundle, svcObj); } catch (Exception ex) { ex.printStackTrace(); } } } private void initializeProperties(Dictionary dict) { // Create a case-insensitive map for the properties. Map props = new StringMap(false); if (dict != null) { // Make sure there are no duplicate keys. Enumeration keys = dict.keys(); while (keys.hasMoreElements()) { Object key = keys.nextElement(); if (props.get(key) == null) { props.put(key, dict.get(key)); } else { throw new IllegalArgumentException( "Duplicate service property: " + key); } } } // Add the framework assigned properties. props.put(Constants.OBJECTCLASS, m_classes); props.put(Constants.SERVICE_ID, m_serviceId); // Update the service property map. m_propMap = props; } private Object getFactoryUnchecked(Bundle bundle) { Object svcObj = null; try { svcObj = m_factory.getService(bundle, this); } catch (Throwable th) { throw new ServiceException("Service factory exception: " + th.getMessage(), ServiceException.FACTORY_EXCEPTION, th); } if (svcObj != null) { for (String className : m_classes) { Class<?> clazz = Util.loadClassUsingClass(svcObj.getClass(), className); if ((clazz == null) || !clazz.isAssignableFrom(svcObj.getClass())) { if (clazz == null) { throw new ServiceException( "Service cannot be cast due to missing class: " + className, ServiceException.FACTORY_ERROR); } else { throw new ServiceException( "Service cannot be cast: " + className, ServiceException.FACTORY_ERROR); } } } } else { throw new ServiceException("Service factory returned null.", ServiceException.FACTORY_ERROR); } return svcObj; } private void ungetFactoryUnchecked(Bundle bundle, T svcObj) { m_factory.ungetService(bundle, this, svcObj); } // // ServiceReference implementation // class ServiceReferenceImpl<T> implements ServiceReference<T>, BundleCapability { private final ServiceReferenceMap m_map; private ServiceReferenceImpl() { m_map = new ServiceReferenceMap(); } ServiceRegistrationImpl getRegistration() { return ServiceRegistrationImpl.this; } // // Capability methods. // @Override public BundleRevision getResource() { return getRevision(); } @Override public BundleRevision getRevision() { throw new UnsupportedOperationException("Not supported yet."); } @Override public String getNamespace() { return "service-reference"; } @Override public Map<String, String> getDirectives() { return Collections.emptyMap(); } @Override public Map<String, Object> getAttributes() { return m_map; } @Override public Object getProperty(String s) { return ServiceRegistrationImpl.this.getProperty(s); } @Override public String[] getPropertyKeys() { return ServiceRegistrationImpl.this.getPropertyKeys(); } @Override public Bundle getBundle() { // The spec says that this should return null if // the service is unregistered. return (isValid()) ? m_bundle : null; } @Override public Bundle[] getUsingBundles() { return ServiceRegistrationImpl.this.getUsingBundles(); } public String toString() { String[] ocs = (String[]) getProperty("objectClass"); String oc = "["; for (int i = 0; i < ocs.length; i++) { oc = oc + ocs[i]; if (i < ocs.length - 1) { oc = oc + ", "; } } oc = oc + "]"; return oc; } @Override public boolean isAssignableTo(Bundle requester, String className) { // Always return true if the requester is the same as the provider. if (requester == m_bundle) { return true; } // Boolean flag. boolean allow = true; /* // Get the package. * String pkgName = Util.getClassPackage(className); * Module requesterModule = ((BundleImpl) * requester).getCurrentModule(); // Get package wiring from service * requester. Wire requesterWire = Util.getWire(requesterModule, * pkgName); // Get package wiring from service provider. Module * providerModule = ((BundleImpl) m_bundle).getCurrentModule(); Wire * providerWire = Util.getWire(providerModule, pkgName); * * // There are four situations that may occur here: // 1. Neither * the requester, nor provider have wires for the package. // 2. The * requester does not have a wire for the package. // 3. The * provider does not have a wire for the package. // 4. Both the * requester and provider have a wire for the package. // For case * 1, if the requester does not have access to the class at // all, * we assume it is using reflection and do not filter. If the // * requester does have access to the class, then we make sure it is * // the same class as the service. For case 2, we do not filter if * the // requester is the exporter of the package to which the * provider of // the service is wired. Otherwise, as in case 1, if * the requester // does not have access to the class at all, we do * not filter, but if // it does have access we check if it is the * same class accessible to // the providing module. For case 3, the * provider will not have a wire // if it is exporting the package, * so we determine if the requester // is wired to it or somehow * using the same class. For case 4, we // simply compare the * exporting modules from the package wiring to // determine if we * need to filter the service reference. * * // Case 1: Both requester and provider have no wire. if * ((requesterWire == null) && (providerWire == null)) { // If * requester has no access then true, otherwise service // * registration must have same class as requester. try { Class * requestClass = requesterModule.getClassByDelegation(className); * allow = getRegistration().isClassAccessible(requestClass); } * catch (Exception ex) { // Requester has no access to the class, * so allow it, since // we assume the requester is using * reflection. allow = true; } } // Case 2: Requester has no wire, * but provider does. else if ((requesterWire == null) && * (providerWire != null)) { // Allow if the requester is the * exporter of the provider's wire. if * (providerWire.getExporter().equals(requesterModule)) { allow = * true; } // Otherwise, check if the requester has access to the * class and, // if so, if it is the same class as the provider. * else { try { // Try to load class from requester. Class * requestClass = requesterModule.getClassByDelegation(className); * try { // If requester has access to the class, verify it is the * // same class as the provider. allow = * (providerWire.getClass(className) == requestClass); } catch * (Exception ex) { allow = false; } } catch (Exception ex) { // * Requester has no access to the class, so allow it, since // we * assume the requester is using reflection. allow = true; } } } // * Case 3: Requester has a wire, but provider does not. else if * ((requesterWire != null) && (providerWire == null)) { // If the * provider is the exporter of the requester's package, then check * // if the requester is wired to the latest version of the * provider, if so // then allow else don't (the provider has been * updated but not refreshed). if (((BundleImpl) * m_bundle).hasModule(requesterWire.getExporter())) { allow = * providerModule.equals(requesterWire.getExporter()); } // If the * provider is not the exporter of the requester's package, // then * try to use the service registration to see if the requester's // * class is accessible. else { try { // Load the class from the * requesting bundle. Class requestClass = * requesterModule.getClassByDelegation(className); // Get the * service registration and ask it to check // if the service object * is assignable to the requesting // bundle's class. allow = * getRegistration().isClassAccessible(requestClass); } catch * (Exception ex) { // Filter to be safe. allow = false; } } } // * Case 4: Both requester and provider have a wire. else { // * Include service reference if the wires have the // same source * module. allow = * providerWire.getExporter().equals(requesterWire.getExporter()); } */ return allow; } @Override public int compareTo(Object reference) { ServiceReference other = (ServiceReference) reference; Long id = (Long) getProperty(Constants.SERVICE_ID); Long otherId = (Long) other.getProperty(Constants.SERVICE_ID); if (id.equals(otherId)) { return 0; // same service } Object rankObj = getProperty(Constants.SERVICE_RANKING); Object otherRankObj = other.getProperty(Constants.SERVICE_RANKING); // If no rank, then spec says it defaults to zero. rankObj = (rankObj == null) ? new Integer(0) : rankObj; otherRankObj = (otherRankObj == null) ? new Integer(0) : otherRankObj; // If rank is not Integer, then spec says it defaults to zero. Integer rank = (rankObj instanceof Integer) ? (Integer) rankObj : new Integer(0); Integer otherRank = (otherRankObj instanceof Integer) ? (Integer) otherRankObj : new Integer(0); // Sort by rank in ascending order. if (rank.compareTo(otherRank) < 0) { return -1; // lower rank } else if (rank.compareTo(otherRank) > 0) { return 1; // higher rank } // If ranks are equal, then sort by service id in descending order. return (id.compareTo(otherId) < 0) ? 1 : -1; } } private class ServiceReferenceMap implements Map<String, Object> { @Override public int size() { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean isEmpty() { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean containsKey(Object o) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean containsValue(Object o) { throw new UnsupportedOperationException("Not supported yet."); } @Override public Object get(Object o) { return ServiceRegistrationImpl.this.getProperty((String) o); } @Override public Object put(String k, Object v) { throw new UnsupportedOperationException("Not supported yet."); } @Override public Object remove(Object o) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void putAll(Map<? extends String, ? extends Object> map) { throw new UnsupportedOperationException("Not supported yet."); } @Override public void clear() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Set<String> keySet() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Collection<Object> values() { throw new UnsupportedOperationException("Not supported yet."); } @Override public Set<Entry<String, Object>> entrySet() { return Collections.emptySet(); } } }