blob: 75e592b44c1a19b000dde10ca9578c35f0fa6cf1 [file] [log] [blame]
/**
* 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.Arrays;
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.Optional;
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.logging.Level;
import java.util.logging.Logger;
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.util.tracker.BundleTracker;
import aQute.bnd.header.Parameters;
import aQute.bnd.stream.MapStream;
import aQute.libg.glob.Glob;
public abstract class BaseActivator implements BundleActivator {
private static final Set<WeavingData> NON_WOVEN_BUNDLE = Collections.emptySet();
private static final Logger logger = Logger.getLogger(BaseActivator.class.getName());
// 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;
@SuppressWarnings("rawtypes")
private BundleTracker consumerBundleTracker;
@SuppressWarnings("rawtypes")
private BundleTracker providerBundleTracker;
private Optional<Parameters> autoConsumerInstructions;
private Optional<Parameters> autoProviderInstructions;
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>>>();
@SuppressWarnings({ "unchecked", "rawtypes" })
public synchronized void start(BundleContext context, final String consumerHeaderName) throws Exception {
bundleContext = context;
try {
autoConsumerInstructions = Optional.ofNullable(
bundleContext.getProperty("org.apache.aries.spifly.auto.consumers")
).map(Parameters::new);
autoProviderInstructions = Optional.ofNullable(
bundleContext.getProperty("org.apache.aries.spifly.auto.providers")
).map(Parameters::new);
}
catch (Throwable t) {
logger.log(Level.SEVERE, t.getMessage(), t);
}
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>>();
Set<String> addedHeaders = new HashSet<String>();
List<String> added = allHeaders.put(consumerHeaderName, getAllHeaders(consumerHeaderName, bundle));
if (added != null) {
added.stream().forEach(addedHeaders::add);
}
added = allHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, getAllHeaders(SpiFlyConstants.REQUIRE_CAPABILITY, bundle));
if (added != null) {
added.stream().forEach(addedHeaders::add);
}
if (addedHeaders.isEmpty()) {
getAutoConsumerInstructions().map(Parameters::stream).orElseGet(MapStream::empty).filterKey(
i -> Glob.toPattern(i).asPredicate().test(bundle.getSymbolicName())
).findFirst().ifPresent(
un -> allHeaders.put(
SpiFlyConstants.REQUIRE_CAPABILITY,
Arrays.asList(
SpiFlyConstants.CLIENT_REQUIREMENT.concat(",osgi.serviceloader;filter:='(osgi.serviceloader=*)'")))
);
}
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 boolean isLogEnabled(Level level) {
return logger.isLoggable(level);
}
public void log(int level, String message) {
log(level, message, null);
}
public void log(Level level, String message) {
log(level, message, null);
}
public void log(int level, String message, Throwable th) {
Level levelObject;
if (Level.ALL.intValue() == level) {
levelObject = Level.ALL;
}
else if (Level.CONFIG.intValue() == level) {
levelObject = Level.CONFIG;
}
else if (Level.FINE.intValue() == level) {
levelObject = Level.FINE;
}
else if (Level.FINER.intValue() == level) {
levelObject = Level.FINER;
}
else if (Level.FINEST.intValue() == level) {
levelObject = Level.FINEST;
}
else if (Level.INFO.intValue() == level) {
levelObject = Level.INFO;
}
else if (Level.SEVERE.intValue() == level) {
levelObject = Level.SEVERE;
}
else if (Level.WARNING.intValue() == level) {
levelObject = Level.WARNING;
}
else {
levelObject = Level.OFF;
}
log(levelObject, message, th);
}
public void log(Level level, String message, Throwable th) {
logger.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) {
SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.computeIfAbsent(registrationClassName,
k -> Collections.synchronizedSortedMap(new TreeMap<Long, Pair<Bundle, Map<String, Object>>>()));
map.compute(
bundle.getBundleId(),
(k,v) -> {
if (v == null) {
return new Pair<Bundle, Map<String, Object>>(bundle, customAttributes);
}
else {
v.getRight().putAll(customAttributes);
return v;
}
});
}
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();
}
public Optional<Parameters> getAutoConsumerInstructions() {
if (autoConsumerInstructions == null) return Optional.empty();
return autoConsumerInstructions;
}
public void setAutoConsumerInstructions(Optional<Parameters> autoConsumerInstructions) {
this.autoConsumerInstructions = autoConsumerInstructions;
}
public Optional<Parameters> getAutoProviderInstructions() {
if (autoProviderInstructions == null) return Optional.empty();
return autoProviderInstructions;
}
public void setAutoProviderInstructions(Optional<Parameters> autoProviderInstructions) {
this.autoProviderInstructions = autoProviderInstructions;
}
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;
}
}