org.apache.karaf.features.internal.service.BundleInstallSupportImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.karaf.features.core Show documentation
Show all versions of org.apache.karaf.features.core Show documentation
This bundle is the core implementation of the Karaf features support.
/*
* 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.karaf.features.internal.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.internal.region.DigraphHelper;
import org.apache.karaf.util.bundles.BundleUtils;
import org.eclipse.equinox.region.Region;
import org.eclipse.equinox.region.RegionDigraph;
import org.eclipse.equinox.region.RegionFilterBuilder;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.namespace.ExecutionEnvironmentNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Resource;
import org.osgi.resource.Wire;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Interaction with OSGi framework, where bundles are installed into it via {@link RegionDigraph}. After a bundle
* is installed, it may be controlled in standard way via {@link Bundle} interface.
*/
public class BundleInstallSupportImpl implements BundleInstallSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(BundleInstallSupportImpl.class);
private final RegionDigraph digraph;
private final Bundle ourBundle;
private final Bundle cmBundle;
private final BundleContext ourBundleContext;
private final FeatureConfigInstaller configInstaller;
/**
* The system bundle context.
* For all bundles related operations, we use the system bundle context
* to allow this bundle to be stopped and still allow the deployment to
* take place.
*/
private final BundleContext systemBundleContext;
private Map hooks = new ConcurrentHashMap<>();
private ServiceRegistration hookRegistration;
public BundleInstallSupportImpl(Bundle ourBundle,
BundleContext ourBundleContext,
BundleContext systemBundleContext,
Bundle cmBundle,
FeatureConfigInstaller configInstaller,
RegionDigraph digraph) {
this.ourBundle = ourBundle;
this.ourBundleContext = ourBundleContext;
this.systemBundleContext = systemBundleContext;
this.cmBundle = cmBundle;
this.configInstaller = configInstaller;
this.digraph = digraph;
if (systemBundleContext != null) {
hookRegistration = systemBundleContext.registerService(ResolverHookFactory.class,
triggers -> hooks.get(Thread.currentThread()), null);
}
}
public void unregister() {
if (hookRegistration != null) {
hookRegistration.unregister();
hookRegistration = null;
}
}
public void print(String message, boolean verbose) {
LOGGER.info(message);
if (verbose) {
System.out.println(message);
}
}
@Override
public void refreshPackages(Collection bundles) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
FrameworkWiring fw = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
fw.refreshBundles(bundles, (FrameworkListener) event -> {
if (event.getType() == FrameworkEvent.ERROR) {
LOGGER.error("Framework error", event.getThrowable());
}
latch.countDown();
});
latch.await();
}
@Override
public Bundle installBundle(String region, String uri, InputStream is) throws BundleException {
if (FeaturesService.ROOT_REGION.equals(region)) {
return digraph.getRegion(region).installBundleAtLocation(uri, is);
} else {
return digraph.getRegion(region).installBundle(uri, is);
}
}
@Override
public void updateBundle(Bundle bundle, String uri, InputStream is) throws BundleException {
File file = null;
// We need to wrap the bundle to insert a Bundle-UpdateLocation header
try {
file = BundleUtils.fixBundleWithUpdateLocation(is, uri);
bundle.update(new FileInputStream(file));
} catch (IOException e) {
throw new BundleException("Unable to update bundle", e);
} finally {
if (file != null) {
file.delete();
}
}
}
@Override
public void uninstall(Bundle bundle) throws BundleException {
bundle.uninstall();
}
@Override
public void startBundle(Bundle bundle) throws BundleException {
if (bundle != this.ourBundle || bundle.getState() != Bundle.STARTING) {
bundle.start();
}
}
@Override
public void stopBundle(Bundle bundle, int options) throws BundleException {
bundle.stop(options);
}
@Override
public void setBundleStartLevel(Bundle bundle, int startLevel) {
bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel);
}
@Override
public void resolveBundles(Set bundles, final Map> wiring, Map resToBnd) {
// Make sure it's only used for us
final Thread thread = Thread.currentThread();
// Translate wiring
final Map bndToRes = new HashMap<>();
for (Resource res : resToBnd.keySet()) {
bndToRes.put(resToBnd.get(res), res);
}
// Hook
final ResolverHook hook = new ResolverHook() {
@Override
public void filterResolvable(Collection candidates) {
}
@Override
public void filterSingletonCollisions(BundleCapability singleton, Collection collisionCandidates) {
}
@Override
public void filterMatches(BundleRequirement requirement, Collection candidates) {
if (Thread.currentThread() == thread) {
// osgi.ee capabilities are provided by the system bundle, so just ignore those
if (ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE
.equals(requirement.getNamespace())) {
return;
}
Bundle sourceBundle = requirement.getRevision().getBundle();
Resource sourceResource = bndToRes.get(sourceBundle);
List wires = wiring.get(sourceResource);
if (sourceBundle == null || wires == null) {
// This could be a bundle external to this resolution which
// is being resolve at the same time, so do not interfere
return;
}
Set wired = new HashSet<>();
// Get a list of allowed wired resources
wired.add(sourceResource);
for (Wire wire : wires) {
wired.add(wire.getProvider());
if (HostNamespace.HOST_NAMESPACE.equals(wire.getRequirement().getNamespace())) {
for (Wire hostWire : wiring.get(wire.getProvider())) {
wired.add(hostWire.getProvider());
}
}
}
// Remove candidates that are not allowed
for (Iterator candIter = candidates.iterator(); candIter.hasNext(); ) {
BundleCapability cand = candIter.next();
BundleRevision br = cand.getRevision();
if ((br.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
br = br.getWiring().getRequiredWires(null).get(0).getProvider();
}
Resource res = bndToRes.get(br.getBundle());
if (!wired.contains(br) && !wired.contains(res)) {
candIter.remove();
}
}
}
}
@Override
public void end() {
}
};
hooks.put(Thread.currentThread(), hook);
try {
FrameworkWiring frameworkWiring = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
frameworkWiring.resolveBundles(bundles);
} finally {
hooks.remove(Thread.currentThread());
}
}
@Override
public void replaceDigraph(Map>>> policies, Map> bundles) throws BundleException, InvalidSyntaxException {
RegionDigraph temp = digraph.copy();
// Remove everything
for (Region region : temp.getRegions()) {
temp.removeRegion(region);
}
// Re-create regions
for (String name : policies.keySet()) {
temp.createRegion(name);
}
// Dispatch bundles
for (Map.Entry> entry : bundles.entrySet()) {
Region region = temp.getRegion(entry.getKey());
for (long bundleId : entry.getValue()) {
region.addBundle(bundleId);
}
}
// Add policies
for (Map.Entry>>> entry1 : policies.entrySet()) {
Region region1 = temp.getRegion(entry1.getKey());
for (Map.Entry>> entry2 : entry1.getValue().entrySet()) {
Region region2 = temp.getRegion(entry2.getKey());
RegionFilterBuilder rfb = temp.createRegionFilterBuilder();
for (Map.Entry> entry3 : entry2.getValue().entrySet()) {
for (String flt : entry3.getValue()) {
rfb.allow(entry3.getKey(), flt);
}
}
region1.connectRegion(region2, rfb.build());
}
}
// Verify that no other bundles have been installed externally in the mean time
DigraphHelper.verifyUnmanagedBundles(systemBundleContext, temp);
// Do replace
digraph.replace(temp);
}
@Override
public void saveDigraph() {
DigraphHelper.saveDigraph(getDataFile(DigraphHelper.DIGRAPH_FILE), digraph);
}
@Override
public RegionDigraph getDiGraphCopy() throws BundleException {
return digraph.copy();
}
@Override
public void installConfigs(Feature feature) throws IOException, InvalidSyntaxException {
if (configInstaller != null) {
configInstaller.installFeatureConfigs(feature);
}
}
@Override
public void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException {
if (configInstaller != null) {
configInstaller.uninstallFeatureConfigs(feature);
}
}
@Override
public void installLibraries(Feature feature) {
// TODO: install libraries
}
@Override
public File getDataFile(String fileName) {
return ourBundleContext.getDataFile(fileName);
}
@Override
public FrameworkInfo getInfo() {
FrameworkInfo info = new FrameworkInfo();
info.ourBundle = ourBundle;
FrameworkStartLevel fsl = systemBundleContext.getBundle().adapt(FrameworkStartLevel.class);
info.initialBundleStartLevel = fsl.getInitialBundleStartLevel();
info.currentStartLevel = fsl.getStartLevel();
info.bundles = new HashMap<>();
for (Bundle bundle : systemBundleContext.getBundles()) {
info.bundles.put(bundle.getBundleId(), bundle);
}
info.cmBundle = cmBundle;
info.systemBundle = info.bundles.get(0L);
return info;
}
}