Author: uiterlix Date: Tue Apr 3 14:42:23 2012 New Revision: 1308952 URL: http://svn.apache.org/viewvc?rev=1308952&view=rev Log: resolved FELIX-3424, resolved FELIX-3425.
Added: felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java Modified: felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java Modified: felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java?rev=1308952&r1=1308951&r2=1308952&view=diff ============================================================================== --- felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java (original) +++ felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/DependencyManager.java Tue Apr 3 14:42:23 2012 @@ -40,6 +40,7 @@ import org.apache.felix.dm.impl.dependen import org.apache.felix.dm.impl.dependencies.TemporalServiceDependencyImpl; import org.apache.felix.dm.impl.index.AspectFilterIndex; import org.apache.felix.dm.impl.index.MultiPropertyExactFilter; +import org.apache.felix.dm.impl.index.AdapterFilterIndex; import org.apache.felix.dm.impl.index.ServiceRegistryCache; import org.apache.felix.dm.impl.metatype.PropertyMetaDataImpl; import org.osgi.framework.Bundle; @@ -104,6 +105,8 @@ public class DependencyManager { for (int i = 0; i < props.length; i++) { if (props[i].equals("*aspect*")) { m_serviceRegistryCache.addFilterIndex(new AspectFilterIndex()); + } else if (props[i].equals("*adapter*")) { + m_serviceRegistryCache.addFilterIndex(new AdapterFilterIndex()); } else { String[] propList = props[i].split(","); @@ -264,7 +267,7 @@ public class DependencyManager { * @return a service that acts as a factory for generating aspects */ public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String autoConfig) { - return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null); + return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, autoConfig, null, null, null, null); } /** * Creates a new aspect. The aspect will be applied to any service that @@ -289,7 +292,7 @@ public class DependencyManager { * @return a service that acts as a factory for generating aspects */ public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking) { - return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null); + return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, null, null, null, null); } /** * Creates a new aspect. The aspect will be applied to any service that @@ -317,7 +320,37 @@ public class DependencyManager { * @return a service that acts as a factory for generating aspects */ public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove) { - return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove); + return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, null); + } + + /** + * Creates a new aspect. The aspect will be applied to any service that + * matches the specified interface and filter. For each matching service + * an aspect will be created based on the aspect implementation class. + * The aspect will be registered with the same interface and properties + * as the original service, plus any extra properties you supply here. + * It will also inherit all dependencies, and if you declare the original + * service as a member it will be injected. + * + * <h3>Usage Example</h3> + * + * <blockquote><pre> + * manager.createAspectService(ExistingService.class, "(foo=bar)", 10, "add", "change", "remove") + * .setImplementation(ExistingServiceAspect.class) + * ); + * </pre></blockquote> + * + * @param serviceInterface the service interface to apply the aspect to + * @param serviceFilter the filter condition to use with the service interface + * @param ranking the level used to organize the aspect chain ordering + * @param add name of the callback method to invoke on add + * @param change name of the callback method to invoke on change + * @param remove name of the callback method to invoke on remove + * @param swap name of the callback method to invoke on swap + * @return a service that acts as a factory for generating aspects + */ + public Component createAspectService(Class serviceInterface, String serviceFilter, int ranking, String add, String change, String remove, String swap) { + return new AspectServiceImpl(this, serviceInterface, serviceFilter, ranking, null, add, change, remove, swap); } /** Modified: felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java?rev=1308952&r1=1308951&r2=1308952&view=diff ============================================================================== --- felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java (original) +++ felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/AspectServiceImpl.java Tue Apr 3 14:42:23 2012 @@ -38,10 +38,10 @@ import org.osgi.framework.ServiceReferen * @author <a href="mailto:d...@felix.apache.org">Felix Project Team</a> */ public class AspectServiceImpl extends FilterService { - public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove) + public AspectServiceImpl(DependencyManager dm, Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) { super(dm.createComponent()); // This service will be filtered by our super class, allowing us to take control. - m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove)) + m_component.setImplementation(new AspectImpl(aspectInterface, aspectFilter, ranking, autoConfig, add, change, remove, swap)) .add(dm.createServiceDependency() .setService(aspectInterface, createDependencyFilterForAspect(aspectFilter)) .setAutoConfig(false) @@ -70,8 +70,9 @@ public class AspectServiceImpl extends F private final String m_add; private final String m_change; private final String m_remove; + private final String m_swap; - public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove) { + public AspectImpl(Class aspectInterface, String aspectFilter, int ranking, String autoConfig, String add, String change, String remove, String swap) { m_aspectInterface = aspectInterface; m_aspectFilter = aspectFilter; m_ranking = ranking; @@ -79,6 +80,7 @@ public class AspectServiceImpl extends F m_add = add; m_change = change; m_remove = remove; + m_swap = swap; } public Component createService(Object[] params) { @@ -93,8 +95,8 @@ public class AspectServiceImpl extends F if (m_autoConfig != null) { dependency.setAutoConfig(m_autoConfig); } - if (m_add != null || m_change != null || m_remove != null) { - dependency.setCallbacks(m_add, m_change, m_remove); + if (m_add != null || m_change != null || m_remove != null || m_swap != null) { + dependency.setCallbacks(m_add, m_change, m_remove, m_swap); } Component service = m_manager.createComponent() .setInterface(serviceInterfaces, serviceProperties) Added: felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java URL: http://svn.apache.org/viewvc/felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java?rev=1308952&view=auto ============================================================================== --- felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java (added) +++ felix/trunk/dependencymanager/core/src/main/java/org/apache/felix/dm/impl/index/AdapterFilterIndex.java Tue Apr 3 14:42:23 2012 @@ -0,0 +1,250 @@ +/* + * 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.dm.impl.index; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.felix.dm.DependencyManager; +import org.apache.felix.dm.FilterIndex; +import org.apache.felix.dm.ServiceUtil; +import org.apache.felix.dm.tracker.ServiceTracker; +import org.apache.felix.dm.tracker.ServiceTrackerCustomizer; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; + +/** + * @author <a href="mailto:d...@felix.apache.org">Felix Project Team</a> + */ +public class AdapterFilterIndex implements FilterIndex, ServiceTrackerCustomizer { + // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) + private static final String FILTER_START = "(&(" + Constants.OBJECTCLASS + "="; + private static final String FILTER_SUBSTRING_0 = ")(|(" + Constants.SERVICE_ID + "="; + private static final String FILTER_SUBSTRING_1 = ")(" + DependencyManager.ASPECT + "="; + private static final String FILTER_END = ")))"; + private final Object m_lock = new Object(); + private ServiceTracker m_tracker; + private BundleContext m_context; + private final Map /* <Long, SortedSet<ServiceReference>> */ m_sidToServiceReferencesMap = new HashMap(); + private final Map /* <String, List<ServiceListener>> */ m_sidToListenersMap = new HashMap(); + private final Map /* <ServiceListener, String> */ m_listenerToFilterMap = new HashMap(); + + public void open(BundleContext context) { + synchronized (m_lock) { + if (m_context != null) { + throw new IllegalStateException("Filter already open."); + } + try { + m_tracker = new ServiceTracker(context, context.createFilter("(" + Constants.OBJECTCLASS + "=*)"), this); + } + catch (InvalidSyntaxException e) { + throw new Error(); + } + m_context = context; + } + m_tracker.open(true, true); + } + + public void close() { + ServiceTracker tracker; + synchronized (m_lock) { + if (m_context == null) { + throw new IllegalStateException("Filter already closed."); + } + tracker = m_tracker; + m_tracker = null; + m_context = null; + } + tracker.close(); + } + + public boolean isApplicable(String clazz, String filter) { + return getFilterData(clazz, filter) != null; + } + + /** Returns a value object with the relevant filter data, or <code>null</code> if this filter was not valid. */ + private FilterData getFilterData(String clazz, String filter) { + // something like: + // (&(objectClass=com.beinformed.product.platform.interfaces.Resource)(|(service.id=18233)(org.apache.felix.dependencymanager.aspect=18233))) + if ((filter != null) + && (filter.startsWith(FILTER_START)) + && (filter.endsWith(FILTER_END)) + ) { + // service-id = + int i0 = filter.indexOf(FILTER_SUBSTRING_0); + if (i0 == -1) { + return null; + } + // org.apache.felix.dependencymanager.aspect = + int i1 = filter.indexOf(FILTER_SUBSTRING_1); + if (i1 == -1 || i1 <= i0) { + return null; + } + long sid = Long.parseLong(filter.substring(i0 + FILTER_SUBSTRING_0.length(), i1)); + long sid2 = Long.parseLong(filter.substring(i1 + FILTER_SUBSTRING_1.length(), filter.length() - FILTER_END.length())); + if (sid != sid2) { + return null; + } + FilterData result = new FilterData(); + result.serviceId = sid; + return result; + } + return null; + } + + public List getAllServiceReferences(String clazz, String filter) { + List /* <ServiceReference> */ result = new ArrayList(); + FilterData data = getFilterData(clazz, filter); + if (data != null) { + SortedSet /* <ServiceReference> */ list = null; + synchronized (m_sidToServiceReferencesMap) { + list = (SortedSet) m_sidToServiceReferencesMap.get(Long.valueOf(data.serviceId)); + } + if (list != null) { + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + result.add((ServiceReference) iterator.next()); + } + } + } + return result; + } + + public void serviceChanged(ServiceEvent event) { + ServiceReference reference = event.getServiceReference(); + Long sid = ServiceUtil.getServiceIdObject(reference); + synchronized (m_sidToListenersMap) { + List /* <Integer, ServiceListener> */ list = (ArrayList) m_sidToListenersMap.get(sid); + if (list != null) { + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + ServiceListener listener = (ServiceListener) iterator.next(); + listener.serviceChanged(event); + } + } + } + } + + public void addServiceListener(ServiceListener listener, String filter) { + FilterData data = getFilterData(null, filter); + if (data != null) { + Long sidObject = Long.valueOf(data.serviceId); + synchronized (m_sidToListenersMap) { + List /* <ServiceListener> */ listeners = (List) m_sidToListenersMap.get(sidObject); + if (listeners == null) { + listeners = new ArrayList(); + m_sidToListenersMap.put(sidObject, listeners); + } + listeners.add(listener); + } + } + } + + public void removeServiceListener(ServiceListener listener) { + synchronized (m_sidToListenersMap) { + String filter = (String) m_listenerToFilterMap.remove(listener); + FilterData data = getFilterData(null, filter); + if (data != null) { + Long sidObject = Long.valueOf(data.serviceId); + List /* ServiceListener */ listeners = (List) m_sidToListenersMap.get(sidObject); + if (listeners != null) { + listeners.remove(listener); + } + } + } + } + + public Object addingService(ServiceReference reference) { + BundleContext context; + synchronized (m_lock) { + context = m_context; + } + if (context != null) { + return context.getService(reference); + } + else { + throw new IllegalStateException("No valid bundle context."); + } + } + + public void addedService(ServiceReference reference, Object service) { + add(reference); + } + + public void modifiedService(ServiceReference reference, Object service) { + modify(reference); + } + + public void removedService(ServiceReference reference, Object service) { + remove(reference); + } + + public void add(ServiceReference reference) { + Long sid = ServiceUtil.getServiceIdObject(reference); + synchronized (m_sidToServiceReferencesMap) { + Set list = (Set) m_sidToServiceReferencesMap.get(sid); + if (list == null) { + list = new TreeSet(); + m_sidToServiceReferencesMap.put(sid, list); + } + list.add(reference); + } + } + + public void modify(ServiceReference reference) { + remove(reference); + add(reference); + } + + public void remove(ServiceReference reference) { + Long sid = ServiceUtil.getServiceIdObject(reference); + synchronized (m_sidToServiceReferencesMap) { + Set list = (Set) m_sidToServiceReferencesMap.get(sid); + if (list != null) { + list.remove(reference); + } + } + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("AdapterFilterIndex["); + sb.append("S2L: " + m_sidToListenersMap.size()); + sb.append(", S2SR: " + m_sidToServiceReferencesMap.size()); + sb.append(", L2F: " + m_listenerToFilterMap.size()); + sb.append("]"); + return sb.toString(); + } + + /** Structure to hold internal filter data. */ + private static class FilterData { + public long serviceId; + } + +}