/** * 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.aries.spifly; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.ServiceLoader; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.wiring.BundleRevision; import org.osgi.framework.wiring.BundleWire; import org.osgi.framework.wiring.BundleWiring; import org.osgi.service.log.LogService; import org.osgi.util.tracker.BundleTracker; public abstract class BaseActivator implements BundleActivator { private static final Set<WeavingData> NON_WOVEN_BUNDLE = Collections.emptySet(); // Static access to the activator used by the woven code, therefore // this bundle must be a singleton. // TODO see if we can get rid of the static access. public static BaseActivator activator; private BundleContext bundleContext; private List<LogService> logServices = new CopyOnWriteArrayList<LogService>(); private BundleTracker consumerBundleTracker; private BundleTracker providerBundleTracker; private final ConcurrentMap<Bundle, Set<WeavingData>> bundleWeavingData = new ConcurrentHashMap<Bundle, Set<WeavingData>>(); private final ConcurrentMap<String, SortedMap<Long, Pair<Bundle, Map<String, Object>>>> registeredProviders = new ConcurrentHashMap<String, SortedMap<Long, Pair<Bundle, Map<String, Object>>>>(); private final ConcurrentMap<Bundle, Map<ConsumerRestriction, List<BundleDescriptor>>> consumerRestrictions = new ConcurrentHashMap<Bundle, Map<ConsumerRestriction, List<BundleDescriptor>>>(); public synchronized void start(BundleContext context, final String consumerHeaderName) throws Exception { bundleContext = context; providerBundleTracker = new BundleTracker(context, Bundle.ACTIVE, new ProviderBundleTrackerCustomizer(this, context.getBundle())); providerBundleTracker.open(); consumerBundleTracker = new BundleTracker(context, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE, new ConsumerBundleTrackerCustomizer(this, consumerHeaderName)); consumerBundleTracker.open(); for (Bundle bundle : context.getBundles()) { addConsumerWeavingData(bundle, consumerHeaderName); } activator = this; } public void addConsumerWeavingData(Bundle bundle, String consumerHeaderName) throws Exception { if (bundleWeavingData.containsKey(bundle)) { // This bundle was already processed return; } Map<String, List<String>> allHeaders = new HashMap<String, List<String>>(); allHeaders.put(consumerHeaderName, getAllHeaders(consumerHeaderName, bundle)); allHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, getAllHeaders(SpiFlyConstants.REQUIRE_CAPABILITY, bundle)); Set<WeavingData> wd = new HashSet<WeavingData>(); for (Map.Entry<String, List<String>> entry : allHeaders.entrySet()) { String headerName = entry.getKey(); for (String headerVal : entry.getValue()) { wd.addAll(ConsumerHeaderProcessor.processHeader(headerName, headerVal)); } } if (!wd.isEmpty()) { bundleWeavingData.put(bundle, Collections.unmodifiableSet(wd)); for (WeavingData w : wd) { registerConsumerBundle(bundle, w.getArgRestrictions(), w.getAllowedBundles()); } } else { bundleWeavingData.put(bundle, NON_WOVEN_BUNDLE); } } private List<String> getAllHeaders(String headerName, Bundle bundle) { List<Bundle> bundlesFragments = new ArrayList<Bundle>(); bundlesFragments.add(bundle); BundleRevision rev = bundle.adapt(BundleRevision.class); if (rev != null) { BundleWiring wiring = rev.getWiring(); if (wiring != null) { for (BundleWire wire : wiring.getProvidedWires("osgi.wiring.host")) { bundlesFragments.add(wire.getRequirement().getRevision().getBundle()); } } } List<String> l = new ArrayList<String>(); for (Bundle bf : bundlesFragments) { String header = bf.getHeaders().get(headerName); if (header != null) { l.add(header); } } return l; } public void removeWeavingData(Bundle bundle) { bundleWeavingData.remove(bundle); } @Override public synchronized void stop(BundleContext context) throws Exception { activator = null; consumerBundleTracker.close(); providerBundleTracker.close(); } public void log(int level, String message) { synchronized (logServices) { for (LogService log : logServices) { log.log(level, message); } } } public void log(int level, String message, Throwable th) { synchronized (logServices) { for (LogService log : logServices) { log.log(level, message, th); } } } public Set<WeavingData> getWeavingData(Bundle b) { // Simply return the value as it's already an immutable set. Set<WeavingData> wd = bundleWeavingData.get(b); if (wd == null) return null; if (wd.size() == 0) return null; return wd; } public void registerProviderBundle(String registrationClassName, Bundle bundle, Map<String, Object> customAttributes) { registrationClassName = registrationClassName.trim(); registeredProviders.putIfAbsent(registrationClassName, Collections.synchronizedSortedMap(new TreeMap<Long, Pair<Bundle, Map<String, Object>>>())); SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.get(registrationClassName); map.put(bundle.getBundleId(), new Pair<Bundle, Map<String, Object>>(bundle, customAttributes)); } public void unregisterProviderBundle(Bundle bundle) { for (Map<Long, Pair<Bundle, Map<String, Object>>> value : registeredProviders.values()) { for(Iterator<Entry<Long, Pair<Bundle, Map<String, Object>>>> it = value.entrySet().iterator(); it.hasNext(); ) { Entry<Long, Pair<Bundle, Map<String, Object>>> entry = it.next(); if (entry.getValue().getLeft().equals(bundle)) { it.remove(); } } } } public Collection<Bundle> findProviderBundles(String name) { SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.get(name); if (map == null) return Collections.emptyList(); List<Bundle> bundles = new ArrayList<Bundle>(map.size()); for(Pair<Bundle, Map<String, Object>> value : map.values()) { bundles.add(value.getLeft()); } return bundles; } public Map<String, Object> getCustomBundleAttributes(String name, Bundle b) { SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.get(name); if (map == null) return Collections.emptyMap(); Pair<Bundle, Map<String, Object>> data = map.get(b.getBundleId()); if (data == null) return Collections.emptyMap(); return data.getRight(); } public void registerConsumerBundle(Bundle consumerBundle, Set<ConsumerRestriction> restrictions, List<BundleDescriptor> allowedBundles) { consumerRestrictions.putIfAbsent(consumerBundle, new HashMap<ConsumerRestriction, List<BundleDescriptor>>()); Map<ConsumerRestriction, List<BundleDescriptor>> map = consumerRestrictions.get(consumerBundle); for (ConsumerRestriction restriction : restrictions) { map.put(restriction, allowedBundles); } } public Collection<Bundle> findConsumerRestrictions(Bundle consumer, String className, String methodName, Map<Pair<Integer, String>, String> args) { Map<ConsumerRestriction, List<BundleDescriptor>> restrictions = consumerRestrictions.get(consumer); if (restrictions == null) { // Null means: no restrictions return null; } for (Map.Entry<ConsumerRestriction, List<BundleDescriptor>> entry : restrictions.entrySet()) { if (entry.getKey().matches(className, methodName, args)) { return getBundles(entry.getValue(), className, methodName, args); } } // Empty collection: nothing matches return Collections.emptySet(); } private Collection<Bundle> getBundles(List<BundleDescriptor> descriptors, String className, String methodName, Map<Pair<Integer, String>, String> args) { if (descriptors == null) { return null; } List<Bundle> bundles = new ArrayList<Bundle>(); for (Bundle b : bundleContext.getBundles()) { for (BundleDescriptor desc : descriptors) { if (desc.getBundleID() != BundleDescriptor.BUNDLE_ID_UNSPECIFIED) { if (b.getBundleId() == desc.getBundleID()) { bundles.add(b); } } else if (desc.getFilter() != null) { Hashtable<String, Object> d = new Hashtable<String, Object>(); if (ServiceLoader.class.getName().equals(className) && "load".equals(methodName)) { String type = args.get(new Pair<Integer, String>(0, Class.class.getName())); if (type != null) { d.put(SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE, type); d.putAll(getCustomBundleAttributes(type, b)); } } if (desc.getFilter().match(d)) bundles.add(b); } else { if (b.getSymbolicName().equals(desc.getSymbolicName())) { if (desc.getVersion() == null || b.getVersion().equals(desc.getVersion())) { bundles.add(b); } } } } } return bundles; } }