You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by nb...@apache.org on 2018/03/08 14:57:58 UTC
svn commit: r1826244 [2/3] - in /felix/trunk: ./ fileinstall-plugins/
fileinstall-plugins/installer.test/ fileinstall-plugins/installer.test/src/
fileinstall-plugins/installer.test/src/test/
fileinstall-plugins/installer.test/src/test/java/ fileinstall...
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/DeploymentInstaller.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/DeploymentInstaller.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/DeploymentInstaller.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/DeploymentInstaller.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,715 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import static org.apache.felix.fileinstall.plugins.installer.impl.Locking.withLock;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Supplier;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+
+import org.apache.felix.fileinstall.ArtifactInstaller;
+import org.apache.felix.fileinstall.plugins.installer.Artifact;
+import org.apache.felix.fileinstall.plugins.installer.FrameworkInstaller;
+import org.apache.felix.fileinstall.plugins.installer.Hash;
+import org.apache.felix.fileinstall.plugins.installer.InstallableListener;
+import org.apache.felix.fileinstall.plugins.installer.InstallableManager;
+import org.apache.felix.fileinstall.plugins.installer.InstallableUnit;
+import org.apache.felix.fileinstall.plugins.installer.InstallableUnitEvent;
+import org.apache.felix.fileinstall.plugins.installer.State;
+import org.apache.felix.fileinstall.plugins.resolver.PluginResolver;
+import org.apache.felix.fileinstall.plugins.resolver.ResolveRequest;
+import org.apache.felix.fileinstall.plugins.resolver.ResolveResult;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.Version;
+import org.osgi.framework.namespace.IdentityNamespace;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.log.LogService;
+import org.osgi.service.repository.ContentNamespace;
+import org.osgi.util.promise.Promise;
+import org.osgi.util.promise.Promises;
+
+import aQute.lib.hex.Hex;
+
+@Component(immediate = true)
+public class DeploymentInstaller implements ArtifactInstaller, InstallableManager {
+
+ /**
+ * Set environment variable "org.apache.felix.fileinstall.plugins.installer.debug" to enable develeroper-level debug output to the console.
+ */
+ public static final boolean DEBUG = Boolean.getBoolean("org.apache.felix.fileinstall.plugins.installer.debug");
+
+ // If Deployment version is not specified, the version to return
+ private static final String UNKNOWN_DEPLOYMENT_VERSION = "0.0.0";
+
+ /**
+ * OSGi Repository indexes are documented to use SHA-256 for their content hashes.
+ * @see ContentNamespace#CONTENT_NAMESPACE
+ */
+ private static final String SHA256 = "SHA-256";
+
+ // Magic number (not officially but good enough for our purposes) of a ZIP file is PK\0x03\0x04
+ private static final byte[] ZIP_FILE_MAGIC = new byte[] { 0x50, 0x4b, 0x03, 0x04 };
+
+ // Queues for pending resolve and install operations.
+ private final Queue<Job<List<Bundle>>> pendingInstalls = new LinkedList<>();
+ private final Map<File, Job<InstallableUnitImpl>> pendingResolves = new LinkedHashMap<>();
+
+ // Currently available Installable Units, and a lock for modifying that map safely.
+ private final Map<File, InstallableUnitImpl> units = new HashMap<>();
+ private final ReadWriteLock unitsLock = new ReentrantReadWriteLock();
+
+ // Listeners for install/uninstall events
+ private final List<InstallableListener> installListeners = new CopyOnWriteArrayList<>();
+
+ // The framework listener invalidates any RESOLVED install units whenever the OSGi Framework packages are refreshed.
+ private final FrameworkListener frameworkListener = new FrameworkListener() {
+ @Override
+ public void frameworkEvent(FrameworkEvent event) {
+ if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
+ invalidateAllUnits("Packaged Refreshed");
+ }
+ }
+ };
+ // The bundle listener invalidated any RESOLVED install units whenever OSGi Bundles are installed, resolved, unresolved, updated or uninstalled.
+ private final BundleListener bundleListener = new BundleListener() {
+ @Override
+ public void bundleChanged(BundleEvent event) {
+ int mask = BundleEvent.INSTALLED | BundleEvent.RESOLVED | BundleEvent.UNRESOLVED | BundleEvent.UPDATED | BundleEvent.UNINSTALLED;
+ if ((mask & event.getType()) > 0) {
+ invalidateAllUnits(String.format("Bundle %s entered state %s", event.getBundle().getSymbolicName(), bundleEventToString(event.getType())));
+ }
+ }
+ };
+
+ // Component instance state.
+ private BundleContext context;
+ private Thread processorThread;
+
+ // The PluginResolver service that calculates resolution results for us, but doesn't produce any effects.
+ @Reference(target = "(!(" + org.osgi.framework.Constants.SERVICE_BUNDLEID + "=0))")
+ PluginResolver resolver;
+
+ // The FrameworkInstaller service is responsible for ensuring required bundles are present in the OSGi Framework -- installing them when necessary.
+ @Reference
+ FrameworkInstaller frameworkInstaller;
+
+ @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+ void addInstallListener(InstallableListener listener) {
+ this.installListeners.add(listener);
+ }
+ void removeInstallListener(InstallableListener listener) {
+ this.installListeners.remove(listener);
+ }
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
+ LogService log;
+
+ @Activate
+ void activate(BundleContext context) {
+ this.context = context;
+ context.addFrameworkListener(this.frameworkListener);
+ context.addBundleListener(this.bundleListener);
+
+ this.processorThread = new Thread(() -> {
+ this.log.log(LogService.LOG_INFO, "Deployment Installer thread starting");
+ debug("Deployment Install thread starting");
+ while (!Thread.interrupted()) {
+ try {
+ Runnable job = waitForJob();
+ job.run();
+ } catch (InterruptedException e) {
+ debug("Deployment Install thread interrupted");
+ this.log.log(LogService.LOG_INFO, "Deployment Installer thread interrupted");
+ break;
+ } catch (Exception e) {
+ this.log.log(LogService.LOG_ERROR, "Error processing job on Deployment Installer thread", e);
+ }
+ }
+ debug("Deployment Install thread terminated.");
+ this.log.log(LogService.LOG_INFO, "Deployment Installer thread terminated");
+ });
+ this.processorThread.start();
+ }
+
+ @Deactivate
+ void deactivate() {
+ this.processorThread.interrupt();
+ this.context.removeBundleListener(this.bundleListener);
+ this.context.removeFrameworkListener(this.frameworkListener);
+ }
+
+ /**
+ * Wait for the next job to do from the two pending queues. Note that we
+ * always favour the install queue, because performing an install/uninstall
+ * will invalidate any completed resolves. Therefore the resolves are only
+ * processed when the install queue is empty.
+ */
+ private Job<?> waitForJob() throws InterruptedException {
+ synchronized (this.processorThread) {
+ while (true) {
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+
+ // Try to get an InstallableUnit to install
+ Job<List<Bundle>> installJob = this.pendingInstalls.poll();
+ if (installJob != null) {
+ return installJob;
+ }
+
+ // Try to get a File to resolve. An iterator is needed to remove the first entry from the LinkedHashMap.
+ Iterator<Job<InstallableUnitImpl>> resolvesIter = this.pendingResolves.values().iterator();
+ if (resolvesIter.hasNext()) {
+ Job<InstallableUnitImpl> resolveJob = resolvesIter.next();
+ resolvesIter.remove();
+ return resolveJob;
+ }
+
+ // Wait for a notification
+ this.processorThread.wait();
+ }
+ }
+ }
+
+ /**
+ * Put an install job onto the queue
+ */
+ Promise<List<Bundle>> putInstallJob(InstallableUnitImpl unit) {
+ Job<List<Bundle>> job = new Job<>(() -> installArtifacts(unit));
+ synchronized (this.processorThread) {
+ this.pendingInstalls.add(job);
+ this.processorThread.notifyAll();
+ }
+ return job.getPromise();
+ }
+
+ /**
+ * Put an uninstall job onto the queue
+ */
+ Promise<List<Bundle>> putUninstallJob(InstallableUnitImpl unit) {
+ Job<List<Bundle>> job = new Job<>(() -> uninstallArtifacts(unit));
+ synchronized (this.processorThread) {
+ this.pendingInstalls.add(job);
+ this.processorThread.notifyAll();
+ }
+ return job.getPromise();
+ }
+
+ private List<Bundle> installArtifacts(InstallableUnitImpl unit) {
+ Supplier<List<Bundle>> func = () -> {
+ State oldState = unit.getState();
+ unit.setState(State.INSTALLING);
+ notifyListeners(Collections.singleton(new InstallableUnitEvent(oldState, State.INSTALLING, unit)));
+
+ try {
+ // Install Bundles
+ Collection<Artifact> artifacts = unit.getArtifacts();
+ List<String> locations = artifacts.stream().map(Artifact::getLocation).collect(Collectors.toList());
+ List<Bundle> installed = this.frameworkInstaller.addLocations(unit, locations);
+
+ // Start bundles
+ for (Bundle bundle : installed) {
+ if (!isFragment(bundle)) {
+ bundle.start();
+ }
+ }
+
+ // Mark unit installed
+ oldState = unit.getState();
+ unit.setState(State.INSTALLED);
+ notifyListeners(Collections.singleton(new InstallableUnitEvent(oldState, State.INSTALLED, unit)));
+
+ return installed;
+ } catch (BundleException | IOException e) {
+ oldState = unit.getState();
+ unit.setState(State.ERROR);
+ unit.setErrorMessage(e.getMessage());
+ if (log != null) {
+ log.log(LogService.LOG_ERROR, "Error installing artifact(s)", e);
+ }
+ notifyListeners(Collections.singleton(new InstallableUnitEvent(oldState, State.ERROR, unit)));
+ return Collections.emptyList();
+ }
+ };
+ return withLock(this.unitsLock.writeLock(), func);
+ }
+
+ private List<Bundle> uninstallArtifacts(InstallableUnitImpl unit) {
+ List<Bundle> bundles = this.frameworkInstaller.removeSponsor(unit);
+
+ // Mark unit uninstalled
+ withLock(this.unitsLock.writeLock(), () -> {
+ State oldState = unit.getState();
+ if (unit.setState(State.REMOVED)) {
+ notifyListeners(Collections.singleton(new InstallableUnitEvent(oldState, State.REMOVED, unit)));
+ }
+ });
+
+ return bundles;
+ }
+
+ /**
+ * Put a resolve job onto the queue. The resolve queue is a little
+ * different: because there is no need to resolve the same file twice, we
+ * check whether there's already a pending resolve job for the given file.
+ * If so, just return the same promise.
+ */
+ private Promise<InstallableUnitImpl> putResolveJob(File file) {
+ Promise<InstallableUnitImpl> promise;
+
+ Job<InstallableUnitImpl> newJob = new Job<>(() -> {
+ InstallableUnitImpl unit = performResolve(file);
+ replaceUnit(file, null, unit);
+ return unit;
+ });
+ synchronized (this.processorThread) {
+ Job<InstallableUnitImpl> existing = this.pendingResolves.putIfAbsent(file, newJob);
+ if (existing != null) {
+ // no need to unblock the processor thread because we haven't added anything
+ debug("Not adding resolve job for %s as it is already on the queue", file);
+ promise = existing.getPromise();
+ } else {
+ debug("Added resolve job for %s", file);
+ promise = newJob.getPromise();
+ this.processorThread.notifyAll();
+ }
+ }
+ return promise;
+ }
+
+ private InstallableUnitImpl performResolve(File file) {
+ debug("Starting resolve for file: %s", file);
+ ResolveRequest request;
+ try {
+ request = analyseFile(file);
+ } catch (Exception e) {
+ debug("Failed to analyse file %s: %s", file, e.getMessage());
+ return newFailedUnit(file, file.getName(), file.getName(), "UNKNOWN_VERSION", String.format("Error reading archive file %s: %s", file.getAbsolutePath(), e.getMessage()));
+ }
+
+ ResolveResult result;
+ try {
+ result = this.resolver.resolve(request);
+ } catch (Exception e) {
+ debug("Failed to resolve file %s: %s", file, e.getMessage());
+ return newFailedUnit(file, request.getName(), request.getSymbolicName(), request.getVersion(), "Resolution failed: " + e.getMessage());
+
+ }
+
+ List<Artifact> artifacts = new ArrayList<>(result.getResources().size());
+ for (Entry<Resource, String> resourceEntry : result.getResources().entrySet()) {
+ Capability idCap = getIdentityCapability(resourceEntry.getKey());
+ ArtifactImpl artifact = new ArtifactImpl(getIdentity(idCap), getVersion(idCap), resourceEntry.getValue(), getContentHash(resourceEntry.getKey()));
+ artifacts.add(artifact);
+ }
+ debug("Sucessful resolve for file %s: Deployment-Name=%s, Deployment-SymbolicName=%s, Deployment-Version= %s", file, request.getName(), request.getSymbolicName(), request.getVersion());
+ return newResolvedUnit(file, request.getName(), request.getSymbolicName(), request.getVersion(), artifacts);
+
+ }
+
+ private InstallableUnitImpl newResolvedUnit(File file, String name, String symbolicName, String version, List<Artifact> artifacts) {
+ InstallableUnitImpl newUnit = new InstallableUnitImpl(this, file, name, symbolicName, version, artifacts);
+ newUnit.setState(State.RESOLVED);
+ return newUnit;
+ }
+
+ private InstallableUnitImpl newFailedUnit(File file, String name, String symbolicName, String version, String message) {
+ InstallableUnitImpl newUnit = new InstallableUnitImpl(this, file, name, symbolicName, version, Collections.emptyList());
+ newUnit.setState(State.ERROR);
+ newUnit.setErrorMessage(message);
+ return newUnit;
+ }
+
+ @Override
+ public boolean canHandle(File file) {
+ String fileName = file.getName();
+ if (!fileName.toLowerCase().endsWith(Constants.ARTIFACT_EXTENSION.toLowerCase())) {
+ log(LogService.LOG_DEBUG, null, "Ignoring %s, does not end with '%s' extension", file.getAbsolutePath(), Constants.ARTIFACT_EXTENSION);
+ return false;
+ }
+
+ // Check if it's a valid ZIP
+ try {
+ if (!isZipFile(file)) {
+ log(LogService.LOG_WARNING, null, "Not a valid ZIP file, ignoring file: %s", file.getAbsolutePath());
+ return false;
+ }
+ } catch (IOException e) {
+ log(LogService.LOG_ERROR, e, "Failed to check ZIP header on %s", file.getAbsolutePath());
+ return false;
+ }
+
+ // Read the requires header and index
+ try (JarFile jar = new JarFile(file, true)) {
+ Manifest manifest = jar.getManifest();
+ if (manifest == null) {
+ log(LogService.LOG_WARNING, null, "Not a valid bundle archive: no META-INF/MANIFEST.MF in %s", file.getAbsolutePath());
+ return false;
+ }
+ String requireBundleStr = manifest.getMainAttributes().getValue(org.osgi.framework.Constants.REQUIRE_BUNDLE);
+ String requireCapsStr = manifest.getMainAttributes().getValue(org.osgi.framework.Constants.REQUIRE_CAPABILITY);
+ if (requireBundleStr == null && requireCapsStr == null) {
+ log(LogService.LOG_WARNING, null, "Not a valid bundle archive: missing %s or %s header in manifest in %s", org.osgi.framework.Constants.REQUIRE_BUNDLE, org.osgi.framework.Constants.REQUIRE_CAPABILITY, file.getAbsolutePath());
+ return false;
+ }
+
+ JarEntry indexEntry = findEntry(jar, Constants.INDEX_FILE, Constants.DEFAULT_INDEX_FILE);
+ if (indexEntry == null) {
+ log(LogService.LOG_WARNING, null, "Not a valid bundle archive: no index entry found in %s", file.getAbsolutePath());
+ return false;
+ }
+
+ // Everything seems present and correct
+ log(LogService.LOG_INFO, null, "Detected valid bundle archive in file %s", file.getAbsolutePath());
+ return true;
+ } catch (IOException e) {
+ log(LogService.LOG_ERROR, e, "Failed to check PKZIP header for candidate archive file %s", file.getAbsolutePath());
+ return false;
+ }
+ }
+
+ private ResolveRequest analyseFile(File file) throws IOException {
+ log(LogService.LOG_INFO, null, "Resolving bundle archive: %s", file.getAbsolutePath());
+
+ String fileUriStr = file.toURI().toString();
+ String indexUriStr;
+ String name;
+ String symbolicName;
+ String version = "";
+
+ List<Requirement> requirements = new LinkedList<>();
+ try (JarFile jar = new JarFile(file)) {
+ Attributes manifestAttribs = jar.getManifest().getMainAttributes();
+
+ symbolicName = manifestAttribs.getValue(Constants.DEPLOYMENT_SYMBOLIC_NAME);
+ if (symbolicName == null) {
+ symbolicName = file.getName();
+ }
+
+ name = manifestAttribs.getValue(Constants.DEPLOYMENT_NAME);
+ if (name == null) {
+ name = symbolicName;
+ }
+
+ version = manifestAttribs.getValue(Constants.DEPLOYMENT_VERSION);
+ if (version == null) {
+ version = UNKNOWN_DEPLOYMENT_VERSION;
+ }
+ requirements.addAll(RequirementParser.parseRequireBundle(manifestAttribs.getValue(org.osgi.framework.Constants.REQUIRE_BUNDLE)));
+ requirements.addAll(RequirementParser.parseRequireCapability(manifestAttribs.getValue(org.osgi.framework.Constants.REQUIRE_CAPABILITY)));
+ if (requirements.isEmpty()) {
+ throw new IllegalArgumentException(String.format("Missing %s or %s header in manifest in %s", org.osgi.framework.Constants.REQUIRE_BUNDLE, org.osgi.framework.Constants.REQUIRE_CAPABILITY, file.getAbsolutePath()));
+ }
+
+ JarEntry indexEntry = findEntry(jar, Constants.INDEX_FILE, Constants.DEFAULT_INDEX_FILE);
+ if (indexEntry == null) {
+ throw new IllegalArgumentException("Missing index entry in " + file.getAbsolutePath());
+ }
+ indexUriStr = "jar:" + fileUriStr + "!/" + indexEntry.getName();
+ }
+
+ try {
+ ResolveRequest request = new ResolveRequest(name, symbolicName, version,
+ Collections.singletonList(new URI(indexUriStr)), requirements);
+
+ return request;
+ } catch (URISyntaxException e) {
+ throw new IOException("Unable to convert index URI " + indexUriStr, e);
+ }
+ }
+
+ @Override
+ public void install(File file) {
+ log(LogService.LOG_INFO, null, "Installing bundle archive: %s", file.getAbsolutePath());
+ putResolveJob(file);
+ }
+
+ private static String getIdentity(Capability identityCap) {
+ Object idObj = identityCap.getAttributes().get(IdentityNamespace.IDENTITY_NAMESPACE);
+ if (!(idObj instanceof String)) {
+ throw new IllegalArgumentException("Missing identity capability on resource, or incorrect type");
+ }
+
+ return (String) idObj;
+ }
+
+ private static String getVersion(Capability identityCap) {
+ Object versionObj = identityCap.getAttributes().get(IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ if (versionObj == null) {
+ return Version.emptyVersion.toString();
+ }
+ if (versionObj instanceof Version) {
+ return ((Version) versionObj).toString();
+ }
+ if (versionObj instanceof String) {
+ return Version.parseVersion((String) versionObj).toString();
+ }
+ throw new IllegalArgumentException("Incorrect type on identity version");
+ }
+
+ private static Capability getIdentityCapability(Resource resource) {
+ List<Capability> caps = resource.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE);
+ if (caps == null || caps.isEmpty()) {
+ throw new IllegalArgumentException("Missing identity capability on resource");
+ }
+ return caps.get(0);
+ }
+
+ @Override
+ public void uninstall(File file) throws Exception {
+ log(LogService.LOG_INFO, null, "Uninstalling bundle archive: %s", file.getAbsolutePath());
+ unsponsorUnit(file);
+ }
+
+ @Override
+ public void update(File file) throws Exception {
+ log(LogService.LOG_INFO, null, "Uninstalling bundle archive: %s", file.getAbsolutePath());
+ unsponsorUnit(file).flatMap(lb -> {
+ return putResolveJob(file);
+ });
+ }
+
+ @Override
+ public Collection<InstallableUnit> getInstallableUnits() {
+ return withLock(this.unitsLock.readLock(), ArrayList<InstallableUnit>::new, this.units.values());
+ }
+
+ /**
+ * Unsponsor the installable unit. Returns a promise which is resolved when the uninstall is completed.
+ */
+ private Promise<List<Bundle>> unsponsorUnit(File file) {
+ List<InstallableUnitEvent> events = new LinkedList<>();
+
+ Promise<List<Bundle>> bundles = withLock(this.unitsLock.writeLock(), () -> {
+ Promise<List<Bundle>> promise = Promises.resolved(Collections.emptyList());
+ InstallableUnitImpl existing = this.units.remove(file);
+ if (existing != null) {
+ State origState = existing.getState();
+ if (origState.equals(State.INSTALLED)) {
+ promise = putUninstallJob(existing);
+ }
+
+ if (existing.setState(State.REMOVED)) {
+ events.add(new InstallableUnitEvent(origState, State.REMOVED, existing));
+ }
+ }
+ return promise;
+ });
+
+ notifyListeners(events);
+ return bundles;
+ }
+
+ private void invalidateAllUnits(String reason) {
+ List<File> affectedFiles = new LinkedList<>();
+ List<InstallableUnitEvent> events = new LinkedList<>();
+ withLock(this.unitsLock.writeLock(), () -> {
+ for (Entry<File, InstallableUnitImpl> entry : this.units.entrySet()) {
+ InstallableUnitImpl unit = entry.getValue();
+ State currentState = unit.getState();
+ if (EnumSet.of(State.RESOLVED, State.ERROR).contains(currentState)) {
+ unit.setState(State.REMOVED);
+ affectedFiles.add(entry.getKey());
+ events.add(new InstallableUnitEvent(currentState, State.REMOVED, unit));
+ debug("invalidated %s because: %s%n", entry.getKey(), reason);
+ }
+ }
+ });
+ notifyListeners(events);
+
+ // Schedule re-resolution of the affected files.
+ for (File file : affectedFiles) {
+ putResolveJob(file);
+ }
+ }
+
+ private void replaceUnit(File file, State origState, InstallableUnitImpl newUnit) {
+ Collection<InstallableUnitEvent> events = new ArrayList<>(2);
+ events.add(new InstallableUnitEvent(origState, newUnit.getState(), newUnit));
+ events.addAll(withLock(this.unitsLock.writeLock(), () -> {
+ InstallableUnitImpl existing = this.units.put(file, newUnit);
+ if (existing != null) {
+ State oldState = existing.getState();
+ if (existing.setState(State.REMOVED)) {
+ return Collections.singleton(new InstallableUnitEvent(oldState, State.REMOVED, existing));
+ }
+ }
+ return Collections.emptyList();
+ }));
+ notifyListeners(events);
+ }
+
+ private void notifyListeners(Collection<InstallableUnitEvent> events) {
+ if (events == null || events.isEmpty()) {
+ return;
+ }
+ for (InstallableListener listener : this.installListeners) {
+ try {
+ listener.installableUnitsChanged(events);
+ } catch (Exception e) {
+ log(LogService.LOG_ERROR, e, "Error dispatching installable unit event to registered listener");
+ }
+ }
+ }
+
+ private void log(int level, Throwable t, String format, Object... args) {
+ if (this.log != null) {
+ this.log.log(level, String.format(format, args), t);
+ }
+ }
+
+ private static boolean isZipFile(File file) throws IOException {
+ byte[] tmp = new byte[4];
+
+ try (InputStream in = new FileInputStream(file)) {
+ int bytesRead = in.read(tmp, 0, 4);
+ if (bytesRead < 4) {
+ throw new IOException("Insufficient bytes read for ZIP header test: possibly truncated file?");
+ }
+ }
+
+ return Arrays.equals(tmp, ZIP_FILE_MAGIC);
+ }
+
+ private static JarEntry findEntry(JarFile jar, String headerName, String defaultPath) throws IOException {
+ String path = defaultPath;
+ String headerPath = jar.getManifest().getMainAttributes().getValue(headerName);
+ if (headerPath != null) {
+ path = headerPath.trim();
+ }
+
+ if (path == null) {
+ // Could happen if no default and no header
+ return null;
+ }
+
+ return jar.getJarEntry(path);
+ }
+
+ private static boolean isFragment(Bundle bundle) {
+ return (bundle.adapt(BundleRevision.class).getTypes() & BundleRevision.TYPE_FRAGMENT) > 0;
+ }
+
+ private static String bundleEventToString(int type) {
+ String s;
+ switch (type) {
+ case BundleEvent.INSTALLED:
+ s = "INSTALLED";
+ break;
+ case BundleEvent.RESOLVED:
+ s = "RESOLVED";
+ break;
+ case BundleEvent.STARTING:
+ s = "STARTING";
+ break;
+ case BundleEvent.STARTED:
+ s = "STARTED";
+ break;
+ case BundleEvent.STOPPING:
+ s = "STOPPING";
+ break;
+ case BundleEvent.STOPPED:
+ s = "STOPPED";
+ break;
+ case BundleEvent.UNRESOLVED:
+ s = "UNRESOLVED";
+ break;
+ case BundleEvent.UPDATED:
+ s = "UPDATED";
+ break;
+ case BundleEvent.UNINSTALLED:
+ s = "UNINSTALLED";
+ break;
+ case BundleEvent.LAZY_ACTIVATION:
+ s = "LAZY_ACTIVATION";
+ break;
+ default:
+ s = "UNKNOWN";
+ }
+ return s;
+ }
+
+ private static Hash getContentHash(Resource resource) throws IllegalArgumentException {
+ Hash result = null;
+
+ List<Capability> contentCaps = resource.getCapabilities(ContentNamespace.CONTENT_NAMESPACE);
+ if (contentCaps != null) {
+ for (Capability contentCap : contentCaps) {
+ String hashHexStr = (String) contentCap.getAttributes().get(ContentNamespace.CONTENT_NAMESPACE);
+ byte[] hashHex = Hex.toByteArray(hashHexStr);
+ Hash hash = new Hash(SHA256, hashHex);
+
+ if (result != null && !hash.equals(result)) {
+ throw new IllegalArgumentException("Resource '" + resource + "' has multiple inconsistent content hashes.");
+ }
+ result = hash;
+ }
+ }
+
+ return result;
+ }
+
+ private void debug(String format, Object... args) {
+ if (DEBUG) {
+ System.out.printf("DEBUG: " + format + "%n", args);
+ }
+ }
+
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/FrameworkInstallerComponent.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/FrameworkInstallerComponent.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/FrameworkInstallerComponent.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/FrameworkInstallerComponent.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.felix.fileinstall.plugins.installer.FrameworkInstaller;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.log.LogService;
+
+@Component
+public class FrameworkInstallerComponent implements FrameworkInstaller {
+
+ private final Map<Long, Set<Object>> bundleSponsors = new HashMap<>();
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL)
+ private LogService log;
+
+ private BundleContext context;
+
+ @Activate
+ void activate(BundleContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public synchronized List<Bundle> addLocations(Object sponsor, List<String> bundleLocations) throws BundleException, IOException {
+ List<Bundle> installed = new ArrayList<>(bundleLocations.size());
+
+ for (String location : bundleLocations) {
+ // Find an existing bundle with that location.
+ Bundle existing = null;
+ for (Bundle bundle : this.context.getBundles()) {
+ if (bundle.getLocation().equals(location)) {
+ existing = bundle;
+
+ // If the existing bundle was previously installed by us then add to the sponsors.
+ Set<Object> sponsors = this.bundleSponsors.get(bundle.getBundleId());
+ if (sponsors != null) {
+ sponsors.add(sponsor);
+ }
+ break;
+ }
+ }
+
+ if (existing == null) {
+ // No existing bundle with that location. Install it and add the sponsor.
+ try {
+ // Ensure that the URLConnection doesn't cache. Mostly useful for JarURLConnection,
+ // which leaks file handles if this is not done.
+ URI locationUri = new URI(location);
+ URLConnection connection = locationUri.toURL().openConnection();
+ connection.setUseCaches(false);
+ try (InputStream stream = connection.getInputStream()) {
+ Bundle bundle = this.context.installBundle(location, stream);
+ installed.add(bundle);
+
+ Set<Object> sponsors = new HashSet<>();
+ sponsors.add(sponsor);
+ this.bundleSponsors.put(bundle.getBundleId(), sponsors);
+ } catch (BundleException e) {
+ if (e.getType() == BundleException.DUPLICATE_BUNDLE_ERROR) {
+ log.log(LogService.LOG_WARNING, "Duplicate bundle symbolic-name/version in install for location " + location, e);
+ } else {
+ throw e;
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new IOException("Invalid bundle location URI: " + location, e);
+ }
+ }
+ }
+
+ return installed;
+ }
+
+ @Override
+ public synchronized List<Bundle> removeSponsor(Object sponsor) {
+ List<Long> toRemove = new LinkedList<>();
+
+ for (Iterator<Entry<Long, Set<Object>>> iter = this.bundleSponsors.entrySet().iterator(); iter.hasNext(); ) {
+ Entry<Long, Set<Object>> entry = iter.next();
+ long bundleId = entry.getKey();
+ Set<Object> sponsors = entry.getValue();
+
+ if (sponsors.remove(sponsor) && sponsors.isEmpty()) {
+ // We removed our sponsor and the sponsor set is now empty => this bundle should be removed.
+ toRemove.add(bundleId);
+ // Also remove the entry from the sponsor map.
+ iter.remove();
+ }
+ }
+
+ List<Bundle> removed = new LinkedList<>();
+ for (long bundleId : toRemove) {
+ Bundle bundle = this.context.getBundle(bundleId);
+ if (bundle != null) { // just in case the bundle was already removed by somebody else
+ try {
+ bundle.uninstall();
+ removed.add(bundle);
+ } catch (BundleException e) {
+ if (this.log != null) {
+ this.log.log(LogService.LOG_ERROR, "Error uninstalling bundle: " + bundle.getSymbolicName(), e);
+ }
+ }
+ }
+ }
+ return removed;
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/InstallableUnitImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/InstallableUnitImpl.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/InstallableUnitImpl.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/InstallableUnitImpl.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.felix.fileinstall.plugins.installer.Artifact;
+import org.apache.felix.fileinstall.plugins.installer.InstallableUnit;
+import org.apache.felix.fileinstall.plugins.installer.State;
+import org.osgi.framework.Bundle;
+import org.osgi.util.promise.Promise;
+
+class InstallableUnitImpl implements InstallableUnit {
+
+ private final DeploymentInstaller installer;
+ private final File originFile;
+ private final String name;
+ private final String symbolicName;
+ private final String version;
+ private final Collection<Artifact> artifacts;
+
+ private State state;
+ private String errorMessage = null;
+
+ InstallableUnitImpl(DeploymentInstaller installer, File originFile, String name, String symbolicName, String version, Collection<Artifact> artifacts) {
+ this.installer = installer;
+ this.originFile = originFile;
+ this.name = name;
+ this.symbolicName = symbolicName;
+ this.artifacts = artifacts;
+ this.version = version;
+ }
+
+ @Override
+ public File getOrigin() {
+ return this.originFile;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getSymbolicName() {
+ return this.symbolicName;
+ }
+
+ @Override
+ public Collection<Artifact> getArtifacts() {
+ return Collections.unmodifiableCollection(this.artifacts);
+ }
+
+ @Override
+ public State getState() {
+ return this.state;
+ }
+
+ @Override
+ public Promise<List<Bundle>> install() {
+ if (!this.state.equals(State.RESOLVED)) {
+ throw new IllegalStateException(String.format("Cannot install unit %s: not in the %s state", this.symbolicName, State.RESOLVED));
+ }
+ return this.installer.putInstallJob(this);
+ }
+
+ @Override
+ public Promise<List<Bundle>> uninstall() {
+ return this.installer.putUninstallJob(this);
+ }
+
+ /**
+ * Set the state, return true if the state changed.
+ */
+ boolean setState(State state) {
+ if (state == null) {
+ throw new IllegalArgumentException("null state not permitted!");
+ }
+ State oldState = this.state;
+
+ this.state = state;
+ return !this.state.equals(oldState);
+ }
+
+ void setErrorMessage(String message) {
+ this.errorMessage = message;
+ }
+
+ @Override
+ public String getErrorMessage() {
+ return this.errorMessage;
+ }
+
+ @Override
+ public String getVersion() {
+ return this.version;
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Job.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Job.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Job.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Job.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.util.concurrent.Callable;
+
+import org.osgi.util.promise.Deferred;
+import org.osgi.util.promise.Promise;
+
+class Job<T> implements Runnable {
+
+ private final Deferred<T> deferred = new Deferred<>();
+ private final Callable<T> call;
+
+ Job(Callable<T> call) {
+ this.call = call;
+ }
+
+ @Override
+ public void run() {
+ // Avoid repeating the work
+ if (this.deferred.getPromise().isDone()) {
+ return;
+ }
+
+ try {
+ T result = this.call.call();
+ this.deferred.resolve(result);
+ } catch (Exception e) {
+ this.deferred.fail(e);
+ }
+ }
+
+ public Promise<T> getPromise() {
+ return this.deferred.getPromise();
+ }
+
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Locking.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Locking.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Locking.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/Locking.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.util.concurrent.locks.Lock;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+final class Locking {
+
+ public static void withLock(Lock lock, Runnable cmd) {
+ lock.lock();
+ try {
+ cmd.run();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public static <R> R withLock(Lock lock, Supplier<R> fun) {
+ lock.lock();
+ try {
+ return fun.get();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public static <T, R> R withLock(Lock lock, Function<T, R> fun, T in) {
+ lock.lock();
+ try {
+ return fun.apply(in);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParser.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParser.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParser.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,130 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.felix.utils.manifest.Attribute;
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Directive;
+import org.apache.felix.utils.manifest.Parser;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
+
+import aQute.bnd.osgi.resource.RequirementBuilder;
+
+class RequirementParser {
+
+ static List<Requirement> parseRequireBundle(String header) throws IllegalArgumentException {
+ if (header == null) {
+ return Collections.emptyList();
+ }
+
+ Clause[] clauses = Parser.parseHeader(header);
+ List<Requirement> requirements = new ArrayList<>(clauses.length);
+ for (Clause requireClause : clauses) {
+ String bsn = requireClause.getName();
+ String versionRangeStr = requireClause.getAttribute(org.osgi.framework.Constants.BUNDLE_VERSION_ATTRIBUTE);
+
+ String filter = toBundleFilter(bsn, versionRangeStr);
+ Requirement requirement = new RequirementBuilder(BundleNamespace.BUNDLE_NAMESPACE)
+ .addDirective(Namespace.REQUIREMENT_FILTER_DIRECTIVE, filter).buildSyntheticRequirement();
+ requirements.add(requirement);
+ }
+ return requirements;
+ }
+
+ static List<Requirement> parseRequireCapability(String header) throws IllegalArgumentException {
+ if (header == null) {
+ return Collections.emptyList();
+ }
+
+ Clause[] clauses = Parser.parseHeader(header);
+ List<Requirement> reqs = new ArrayList<>(clauses.length);
+ for (Clause clause : clauses) {
+ String namespace = clause.getName();
+
+ RequirementBuilder reqBuilder = new RequirementBuilder(namespace);
+ for (Attribute attrib : clause.getAttributes()) {
+ try {
+ reqBuilder.addAttribute(attrib.getName(), attrib.getValue());
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Invalid requirement attribute", e);
+ }
+ }
+ for (Directive directive : clause.getDirectives()) {
+ reqBuilder.addDirective(directive.getName(), directive.getValue());
+ }
+
+ reqs.add(reqBuilder.buildSyntheticRequirement());
+ }
+
+ return reqs;
+ }
+
+ private static String toBundleFilter(String bsn, String versionRangeStr) {
+ final String filterStr;
+
+ String bsnFilter = String.format("(%s=%s)", BundleNamespace.BUNDLE_NAMESPACE, bsn);
+
+ if (versionRangeStr == null) {
+ filterStr = bsnFilter;
+ } else {
+ VersionRange versionRange = new VersionRange(versionRangeStr);
+ if (versionRange.isExact()) {
+ String exactVersionFilter = String.format("(%s=%s)",
+ BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE, versionRange.getLeft());
+ filterStr = String.format("(&%s%s)", bsnFilter, exactVersionFilter);
+ } else if (versionRange.getRight() == null) {
+ filterStr = String.format("(&%s%s)", bsnFilter, lowerVersionFilter(versionRange));
+ } else {
+ filterStr = String.format("(&%s%s%s)", bsnFilter, lowerVersionFilter(versionRange),
+ upperVersionFilter(versionRange));
+ }
+ }
+ return filterStr;
+ }
+
+ private static String upperVersionFilter(VersionRange versionRange) {
+ String upperVersionFilter;
+ if (versionRange.getRightType() == VersionRange.RIGHT_CLOSED) {
+ upperVersionFilter = String.format("(%s<=%s)", BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE,
+ versionRange.getRight());
+ } else {
+ upperVersionFilter = String.format("(!(%s>=%s))", BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE,
+ versionRange.getRight());
+ }
+ return upperVersionFilter;
+ }
+
+ private static String lowerVersionFilter(VersionRange versionRange) {
+ String lowerVersionFilter;
+ if (versionRange.getLeftType() == VersionRange.LEFT_CLOSED) {
+ lowerVersionFilter = String.format("(%s>=%s)", BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE,
+ versionRange.getLeft());
+ } else {
+ lowerVersionFilter = String.format("(!(%s<=%s))", BundleNamespace.CAPABILITY_BUNDLE_VERSION_ATTRIBUTE,
+ versionRange.getLeft());
+ }
+ return lowerVersionFilter;
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/package-info.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/package-info.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/package-info.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/main/java/org/apache/felix/fileinstall/plugins/installer/package-info.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.
+ *******************************************************************************/
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.felix.fileinstall.plugins.installer;
Added: felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/CompositeBundleInstallerTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/CompositeBundleInstallerTest.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/CompositeBundleInstallerTest.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/CompositeBundleInstallerTest.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.apache.felix.fileinstall.plugins.installer.impl.DeploymentInstaller;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CompositeBundleInstallerTest {
+
+ private TestLogService log;
+ private DeploymentInstaller installer;
+
+ @Before
+ public void setup() {
+ log = new TestLogService();
+ installer = new DeploymentInstaller();
+ installer.log = log;
+ }
+
+ @Test
+ public void testValidArchiveCanBeHandled() {
+ File jar = new File("target/test-classes/valid1.bar");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertTrue("test archive not handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing info message", log.logged.get(0).startsWith("INFO: Detected valid bundle archive"));
+ }
+
+ @Test
+ public void testIndexPathInManifest() {
+ File jar = new File("target/test-classes/valid2.bar");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertTrue("test archive not handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing info message", log.logged.get(0).startsWith("INFO: Detected valid bundle archive"));
+ }
+
+ @Test
+ public void testInvalidExtension() {
+ File jar = new File("target/test-classes/invalid-wrong-extension.xxx");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertFalse("invalid test archive should not be handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing debug message", log.logged.get(0).startsWith("DEBUG: Ignoring"));
+ }
+
+ @Test
+ public void testMissingRequireHeader() {
+ File jar = new File("target/test-classes/invalid-missing-requires.bar");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertFalse("invalid test archive should not be handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing warning message", log.logged.get(0).startsWith("WARNING: Not a valid bundle archive"));
+ }
+
+ @Test
+ public void testNoIndex() {
+ File jar = new File("target/test-classes/invalid-no-index.bar");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertFalse("invalid test archive should not be handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing warning message", log.logged.get(0).startsWith("WARNING: Not a valid bundle archive"));
+ }
+
+ @Test
+ public void testMissingIndexPath() {
+ File jar = new File("target/test-classes/invalid-missing-index-path.bar");
+
+ assertTrue("test archive not found", jar.isFile());
+ assertFalse("invalid test archive should not be handled", installer.canHandle(jar));
+
+ assertEquals(1, log.logged.size());
+ assertTrue("missing warning message", log.logged.get(0).startsWith("WARNING: Not a valid bundle archive"));
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParserTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParserTest.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParserTest.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/RequirementParserTest.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.apache.felix.fileinstall.plugins.installer.impl.RequirementParser;
+import org.junit.Test;
+import org.osgi.resource.Requirement;
+
+public class RequirementParserTest {
+
+ @Test
+ public void testParseRequireBundle() {
+ List<Requirement> actual = RequirementParser.parseRequireBundle("foo;bundle-version=1.0.0, bar;bundle-version=\"[1.0,1.1)\", baz;bundle-version=\"[2.0,3.0)\", fnarg");
+
+ assertEquals(4, actual.size());
+ assertEquals("(&(osgi.wiring.bundle=foo)(bundle-version>=1.0.0))", actual.get(0).getDirectives().get("filter"));
+ assertEquals("(&(osgi.wiring.bundle=bar)(bundle-version>=1.0.0)(!(bundle-version>=1.1.0)))", actual.get(1).getDirectives().get("filter"));
+ assertEquals("(&(osgi.wiring.bundle=baz)(bundle-version>=2.0.0)(!(bundle-version>=3.0.0)))", actual.get(2).getDirectives().get("filter"));
+ assertEquals("(osgi.wiring.bundle=fnarg)", actual.get(3).getDirectives().get("filter"));
+ }
+
+ @Test
+ public void testParseRequireCapability() {
+ List<Requirement> actual = RequirementParser.parseRequireCapability("osgi.extender; filter:=\"(&(osgi.extender=osgi.ds)(version>=1.0))\"; effective:=active, osgi.service; filter:=\"(objectClass=org.example.Foo)\"");
+
+ assertEquals(2, actual.size());
+ assertEquals("(&(osgi.extender=osgi.ds)(version>=1.0))", actual.get(0).getDirectives().get("filter"));
+ assertEquals("active", actual.get(0).getDirectives().get("effective"));
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/TestLogService.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/TestLogService.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/TestLogService.java (added)
+++ felix/trunk/fileinstall-plugins/installer/src/test/java/org/apache/felix/fileinstall/plugins/installer/impl/TestLogService.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.installer.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+class TestLogService implements LogService {
+
+ List<String> logged = new LinkedList<>();
+
+ @Override
+ public void log(int level, String message) {
+ log(null, level, message, null);
+ }
+
+ @Override
+ public void log(int level, String message, Throwable exception) {
+ log(null, level, message, exception);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void log(ServiceReference sr, int level, String message) {
+ log(sr, level, message, null);
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public void log(ServiceReference sr, int level, String message, Throwable exception) {
+ String levelStr;
+ switch (level) {
+ case LogService.LOG_DEBUG:
+ levelStr = "DEBUG";
+ break;
+ case LogService.LOG_INFO:
+ levelStr = "INFO";
+ break;
+ case LogService.LOG_WARNING:
+ levelStr = "WARNING";
+ break;
+ case LogService.LOG_ERROR:
+ levelStr = "ERROR";
+ break;
+ default:
+ levelStr = "UNKNOWN";
+ }
+ String formatted = String.format("%s: %s %s", levelStr, message, exception != null ? exception : "");
+ this.logged.add(formatted);
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-index-path.bar
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-index-path.bar?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-index-path.bar (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-index-path.bar Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-requires.bar
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-requires.bar?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-requires.bar (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-missing-requires.bar Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-no-index.bar
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-no-index.bar?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-no-index.bar (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-no-index.bar Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-wrong-extension.xxx
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-wrong-extension.xxx?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-wrong-extension.xxx (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/invalid-wrong-extension.xxx Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/valid1.bar
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/valid1.bar?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/valid1.bar (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/valid1.bar Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/installer/src/test/resources/valid2.bar
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/installer/src/test/resources/valid2.bar?rev=1826244&view=auto
==============================================================================
Binary files felix/trunk/fileinstall-plugins/installer/src/test/resources/valid2.bar (added) and felix/trunk/fileinstall-plugins/installer/src/test/resources/valid2.bar Thu Mar 8 14:57:57 2018 differ
Added: felix/trunk/fileinstall-plugins/resolver/.gitignore
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/.gitignore?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/.gitignore (added)
+++ felix/trunk/fileinstall-plugins/resolver/.gitignore Thu Mar 8 14:57:57 2018
@@ -0,0 +1 @@
+/bin/
Added: felix/trunk/fileinstall-plugins/resolver/bnd.bnd
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/bnd.bnd?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/bnd.bnd (added)
+++ felix/trunk/fileinstall-plugins/resolver/bnd.bnd Thu Mar 8 14:57:57 2018
@@ -0,0 +1,4 @@
+-conditionalpackage: net.jcip.annotations
+
+-exportcontents: ${packages;VERSIONED}
+
\ No newline at end of file
Added: felix/trunk/fileinstall-plugins/resolver/pom.xml
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/pom.xml?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/pom.xml (added)
+++ felix/trunk/fileinstall-plugins/resolver/pom.xml Thu Mar 8 14:57:57 2018
@@ -0,0 +1,84 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>felix-parent</artifactId>
+ <version>3</version>
+ <relativePath>../../pom/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.felix.fileinstall.plugins.resolver</artifactId>
+ <name>Apache Felix File Install Resolver Plugin</name>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/felix/trunk/fileinstall-plugins/resolver</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/felix/trunk/fileinstall-plugins/resolver</developerConnection>
+ <url>http://svn.apache.org/repos/asf/felix/fileinstall</url>
+ </scm>
+
+ <properties>
+ <felix.java.version>7</felix.java.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.core</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.cmpn</artifactId>
+ <version>6.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>osgi.annotation</artifactId>
+ <version>6.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>biz.aQute.bndlib</artifactId>
+ <version>3.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>biz.aQute.repository</artifactId>
+ <version>3.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>biz.aQute.bnd</groupId>
+ <artifactId>bnd-maven-plugin</artifactId>
+ <version>3.4.0</version>
+ <executions>
+ <execution>
+ <id>default-bnd-process</id>
+ <goals>
+ <goal>bnd-process</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/PluginResolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/PluginResolver.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/PluginResolver.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/PluginResolver.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+/**
+ * Service interface for a Plugin Resolver. Takes a request consisting of a list
+ * of index URIs and returns a resolution result, consisting of a list of
+ * resources to be installed.
+ */
+@ProviderType
+public interface PluginResolver {
+
+ ResolveResult resolve(ResolveRequest request) throws Exception;
+
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveRequest.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveRequest.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveRequest.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveRequest.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.osgi.resource.Requirement;
+
+/**
+ * Represents a request to resolve certain requirements in the context of the
+ * current OSGi Framework.
+ */
+public class ResolveRequest {
+
+ private final String name;
+ private final String symbolicName;
+ private final String version;
+ private final List<URI> indexes;
+ private final Collection<Requirement> requirements;
+
+ /**
+ * Construct a request.
+ *
+ * @param indexes
+ * The list of repository indexes to search for installable
+ * resources.
+ * @param requirements
+ * The list of requirements to resolve.
+ */
+ public ResolveRequest(String name, String symbolicName, String version, List<URI> indexes, Collection<Requirement> requirements) {
+ this.name = name;
+ this.symbolicName = symbolicName;
+ this.version = version;
+ this.indexes = new ArrayList<>(indexes);
+ this.requirements = new ArrayList<>(requirements);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getSymbolicName() {
+ return this.symbolicName;
+ }
+
+ public String getVersion() {
+ return this.version;
+ }
+
+ public List<URI> getIndexes() {
+ return Collections.unmodifiableList(this.indexes);
+ }
+
+ public Collection<Requirement> getRequirements() {
+ return Collections.unmodifiableCollection(this.requirements);
+ }
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveResult.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveResult.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveResult.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/ResolveResult.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver;
+
+import java.util.Map;
+
+import org.osgi.resource.Resource;
+
+public interface ResolveResult {
+
+ /**
+ * Return the request that was satisfied by this result.
+ */
+ ResolveRequest getRequest();
+
+ /**
+ * Return the resources to install. Each resource is mapped to a location,
+ * which is either the location field of an existing installed bundle or the
+ * physical URL of a location from which the bundle can be installed.
+ */
+ Map<Resource, String> getResources();
+
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/BasicRegistry.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/BasicRegistry.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/BasicRegistry.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/BasicRegistry.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import aQute.bnd.service.Registry;
+
+class BasicRegistry implements Registry {
+
+ private final Map<Class<?>, List<Object>> plugins = new HashMap<>();
+
+ synchronized <T> BasicRegistry put(Class<T> clazz, T plugin) {
+ List<Object> list = plugins.get(clazz);
+ if (list == null) {
+ list = new LinkedList<>();
+ plugins.put(clazz, list);
+ }
+ list.add(plugin);
+ return this;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public synchronized <T> List<T> getPlugins(Class<T> clazz) {
+ List<T> objs = (List<T>) plugins.get(clazz);
+ return objs != null ? Collections.<T>unmodifiableList(objs) : Collections.<T>emptyList();
+ }
+
+ @Override
+ public <T> T getPlugin(Class<T> clazz) {
+ List<T> l = getPlugins(clazz);
+ return (l != null && !l.isEmpty()) ? l.get(0) : null;
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapReqBase.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapReqBase.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapReqBase.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapReqBase.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver.impl;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.resource.Resource;
+
+public class CapReqBase {
+
+ protected final String namespace;
+ protected final Map<String, String> directives;
+ protected final Map<String, Object> attribs;
+ protected final Resource resource;
+
+ public CapReqBase(String namespace, Map<String,String> directives, Map<String, Object> attribs, Resource resource) {
+ this.namespace = namespace;
+ this.directives = new HashMap<>(directives);
+ this.attribs = new HashMap<>(attribs);
+ this.resource = resource;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public Map<String, String> getDirectives() {
+ return Collections.unmodifiableMap(directives);
+ }
+
+ public Map<String, Object> getAttributes() {
+ return Collections.unmodifiableMap(attribs);
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapabilityImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapabilityImpl.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapabilityImpl.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/CapabilityImpl.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+public class CapabilityImpl extends CapReqBase implements Capability {
+
+ public static CapabilityImpl copy(Capability cap, Resource resource) {
+ return new CapabilityImpl(cap.getNamespace(), cap.getDirectives(), cap.getAttributes(), resource);
+ }
+
+ public CapabilityImpl(String namespace, Map<String, String> directives, Map<String, Object> attribs, Resource resource) {
+ super(namespace, directives, attribs, resource);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.namespace);
+
+ boolean first = true;
+ for (Entry<String,String> directive : this.directives.entrySet()) {
+ if (first) {
+ sb.append(":");
+ }
+ first = false;
+ sb.append(", ").append(directive.getKey()).append(":=").append(directive.getValue());
+ }
+ for (Entry<String,Object> attrib : this.attribs.entrySet()) {
+ if (first) {
+ sb.append(":");
+ }
+ first = false;
+ sb.append(", ").append(attrib.getKey()).append("=").append(attrib.getValue());
+ }
+
+ return sb.toString();
+ }
+
+}
Added: felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/JarURLConnector.java
URL: http://svn.apache.org/viewvc/felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/JarURLConnector.java?rev=1826244&view=auto
==============================================================================
--- felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/JarURLConnector.java (added)
+++ felix/trunk/fileinstall-plugins/resolver/src/main/java/org/apache/felix/fileinstall/plugins/resolver/impl/JarURLConnector.java Thu Mar 8 14:57:57 2018
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) Intel Corporation
+ * Copyright (c) 2017
+ *
+ * Licensed 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.fileinstall.plugins.resolver.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+
+import aQute.bnd.service.url.TaggedData;
+import aQute.bnd.service.url.URLConnector;
+
+public class JarURLConnector implements URLConnector {
+
+ @Override
+ public TaggedData connectTagged(URL url) throws Exception {
+ URLConnection connection = url.openConnection();
+ if (connection instanceof JarURLConnection) {
+ connection.setUseCaches(false);
+ }
+ return new TaggedData(connection, connection.getInputStream());
+ }
+
+ @Override
+ public InputStream connect(URL url) throws IOException, Exception {
+ return connectTagged(url).getInputStream();
+ }
+
+ @Override
+ public TaggedData connectTagged(URL url, String tag) throws Exception {
+ return connectTagged(url);
+ }
+
+}