blob: 808ce34aaf4d8638d0486b7c0f1948c96c06ab1b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.deploymentadmin;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Manifest;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.service.deploymentadmin.BundleInfo;
import org.osgi.service.deploymentadmin.DeploymentException;
import org.osgi.service.deploymentadmin.DeploymentPackage;
import org.osgi.service.deploymentadmin.spi.ResourceProcessor;
/**
* Base class for various types of deployment packages. Indifferent in regard to how the
* deployment package data is obtained, this should be handled by extending classes.
*/
public abstract class AbstractDeploymentPackage implements DeploymentPackage {
/**
* Represents an empty deployment package.
*/
private static final class EmptyDeploymentPackage extends AbstractDeploymentPackage {
private static final String[] STRINGS = new String[] {};
private static final ResourceInfoImpl[] RESOURCE_INFO_IMPLS = new ResourceInfoImpl[] {};
private static final BundleInfoImpl[] BUNDLE_INFO_IMPLS = new BundleInfoImpl[] {};
public String getHeader(String header) {
if (Constants.DEPLOYMENTPACKAGE_SYMBOLICMAME.equals(header)) { return ""; }
else if (Constants.DEPLOYMENTPACKAGE_VERSION.equals(header)) { return Version.emptyVersion.toString(); }
else { return null; }
}
public Bundle getBundle(String symbolicName) { return null; }
public BundleInfo[] getBundleInfos() { return BUNDLE_INFO_IMPLS; }
public BundleInfoImpl[] getBundleInfoImpls() { return BUNDLE_INFO_IMPLS; }
public ResourceInfoImpl[] getResourceInfos() { return RESOURCE_INFO_IMPLS; }
public String getName() { return ""; }
public String getResourceHeader(String resource, String header) { return null; }
public ServiceReference getResourceProcessor(String resource) { return null; }
public String[] getResources() { return STRINGS; }
public Version getVersion() { return Version.emptyVersion; }
public boolean isStale() { return true; }
public void uninstall() throws DeploymentException { throw new IllegalStateException("Can not uninstall stale DeploymentPackage"); }
public boolean uninstallForced() throws DeploymentException { throw new IllegalStateException("Can not uninstall stale DeploymentPackage"); }
public InputStream getBundleStream(String symbolicName) throws IOException { return null; }
public BundleInfoImpl[] getOrderedBundleInfos() { return BUNDLE_INFO_IMPLS; }
public ResourceInfoImpl[] getOrderedResourceInfos() { return RESOURCE_INFO_IMPLS; }
public InputStream getCurrentEntryStream() { throw new UnsupportedOperationException(); }
public AbstractInfo getNextEntry() throws IOException { throw new UnsupportedOperationException(); }
public String getDisplayName() { return ""; }
public URL getIcon() { return null; }
}
protected static final AbstractDeploymentPackage EMPTY_PACKAGE = new EmptyDeploymentPackage();
private final BundleContext m_bundleContext;
private final DeploymentAdminImpl m_deploymentAdmin;
private final DeploymentPackageManifest m_manifest;
private final Map m_nameToBundleInfo = new HashMap();
private final Map m_pathToEntry = new HashMap();
private final BundleInfoImpl[] m_bundleInfos;
private final ResourceInfoImpl[] m_resourceInfos;
private final String[] m_resourcePaths;
private final boolean m_isFixPackage;
private boolean m_isStale;
/* Constructor only for use by the emptyPackage static variable */
private AbstractDeploymentPackage() {
m_bundleContext = null;
m_manifest = null;
m_bundleInfos = null;
m_resourceInfos = null;
m_resourcePaths = null;
m_isFixPackage = false;
m_deploymentAdmin = null;
}
/**
* Creates an instance of this class.
*
* @param manifest The manifest of the deployment package.
* @param bundleContext The bundle context.
* @throws DeploymentException Thrown if the specified manifest does not describe a valid deployment package.
*/
public AbstractDeploymentPackage(Manifest manifest, BundleContext bundleContext, DeploymentAdminImpl deploymentAdmin) throws DeploymentException {
m_manifest = new DeploymentPackageManifest(manifest);
m_isFixPackage = m_manifest.getFixPackage() != null;
m_bundleContext = bundleContext;
m_deploymentAdmin = deploymentAdmin;
m_bundleInfos = (BundleInfoImpl[]) m_manifest.getBundleInfos().toArray(new BundleInfoImpl[0]);
for(int i = 0; i < m_bundleInfos.length; i++) {
m_nameToBundleInfo.put(m_bundleInfos[i].getSymbolicName(), m_bundleInfos[i]);
m_pathToEntry.put(m_bundleInfos[i].getPath(), m_bundleInfos[i]);
}
m_resourceInfos = (ResourceInfoImpl[]) m_manifest.getResourceInfos().toArray(new ResourceInfoImpl[0]);
for (int i = 0; i < m_resourceInfos.length; i++) {
m_pathToEntry.put(m_resourceInfos[i].getPath(), m_resourceInfos[i]);
}
m_resourcePaths = (String[]) m_pathToEntry.keySet().toArray(new String[m_pathToEntry.size()]);
}
public Bundle getBundle(String symbolicName) {
if (isStale()) {
throw new IllegalStateException("Can not get bundle from stale deployment package.");
}
BundleInfo bundleInfo = (BundleInfo) m_nameToBundleInfo.get(symbolicName);
if (bundleInfo != null) {
Version version = bundleInfo.getVersion();
Bundle[] bundles = m_bundleContext.getBundles();
for (int i = 0; i < bundles.length; i++) {
if (symbolicName.equals(bundles[i].getSymbolicName()) && version.equals(bundles[i].getVersion())) {
return bundles[i];
}
}
}
return null;
}
public BundleInfo[] getBundleInfos() {
return (BundleInfo[]) m_bundleInfos.clone();
}
/**
* Returns the bundles of this deployment package as an array of <code>BundleInfoImpl</code> objects.
*
* @return Array containing <code>BundleInfoImpl</code> objects for each bundle this deployment package.
*/
public BundleInfoImpl[] getBundleInfoImpls() {
return (BundleInfoImpl[]) m_bundleInfos.clone();
}
/**
* Returns the processed resources of this deployment package as an array of <code>ResourceInfoImpl</code> objects.
*
* @return Array containing <code>ResourceInfoImpl</code> objects for each processed resource of this deployment package.
*/
public ResourceInfoImpl[] getResourceInfos() {
return (ResourceInfoImpl[]) m_resourceInfos.clone();
}
/**
* Determines whether this deployment package is a fix package.
*
* @return True if this deployment package is a fix package, false otherwise.
*/
public boolean isFixPackage() {
return m_isFixPackage;
}
public String getHeader(String header) {
return m_manifest.getHeader(header);
}
public String getName() {
return m_manifest.getSymbolicName();
}
public String getDisplayName() {
return getHeader("DeploymentPackage-Name");
}
public URL getIcon() {
String icon = getHeader("DeploymentPackage-Icon");
if (icon == null) {
return null;
}
else {
try {
// TODO spec states this must be a local resource, but we don't make
// sure of that yet
return new URL(icon);
}
catch (MalformedURLException e) {
return null;
}
}
}
public String getResourceHeader(String resource, String header) {
AbstractInfo info = (AbstractInfo) m_pathToEntry.get(resource);
if (info != null) {
return info.getHeader(header);
}
return null;
}
public ServiceReference getResourceProcessor(String resource) {
if (isStale()) {
throw new IllegalStateException("Can not get bundle from stale deployment package.");
}
AbstractInfo info = (AbstractInfo) m_pathToEntry.get(resource);
if (info instanceof ResourceInfoImpl) {
String processor = ((ResourceInfoImpl) info).getResourceProcessor();
if (processor != null) {
try {
ServiceReference[] services = m_bundleContext.getServiceReferences(ResourceProcessor.class.getName(), "(" + org.osgi.framework.Constants.SERVICE_PID + "=" + processor + ")");
if (services != null && services.length > 0) {
return services[0];
}
else {
return null;
}
}
catch (InvalidSyntaxException e) {
// TODO: log this
return null;
}
}
}
return null;
}
public String[] getResources() {
return (String[]) m_resourcePaths.clone();
}
public Version getVersion() {
return m_manifest.getVersion();
}
/**
* If this deployment package is a fix package this method determines the version range this deployment package can be applied to.
*
* @return <code>VersionRange</code> the fix package can be applied to or <code>null</code> if it is not a fix package.
*/
public VersionRange getVersionRange() {
return m_manifest.getFixPackage();
}
public boolean isStale() {
return m_isStale;
}
/**
* @return <code>true</code> if this package is actually an empty package used for
* installing new deployment packages, <code>false</code> otherwise.
*/
public boolean isNew() {
return this == EMPTY_PACKAGE;
}
public void setStale(boolean isStale) {
m_isStale = isStale;
}
public void uninstall() throws DeploymentException {
if (isStale()) {
throw new IllegalStateException("Deployment package is stale, cannot uninstall.");
}
try {
m_deploymentAdmin.uninstallDeploymentPackage(this, false /* force */);
} finally {
setStale(true);
}
}
public boolean uninstallForced() throws DeploymentException {
if (isStale()) {
throw new IllegalStateException("Deployment package is stale, cannot uninstall.");
}
try {
m_deploymentAdmin.uninstallDeploymentPackage(this, true /* force */);
} finally {
setStale(true);
}
return true;
}
/**
* Determines the bundles of this deployment package in the order in which they were originally received.
*
* @return Array containing <code>BundleInfoImpl</code> objects of the bundles in this deployment package, ordered in the way they appeared when the deployment package was first received.
*/
public abstract BundleInfoImpl[] getOrderedBundleInfos();
/**
* Determines the resources of this deployment package in the order in which they were originally received.
*
* @return Array containing <code>ResourceInfoImpl</code> objects of all processed resources in this deployment package, ordered in the way they appeared when the deployment package was first received
*/
public abstract ResourceInfoImpl[] getOrderedResourceInfos();
/**
* Determines the info about a processed resource based on it's path/resource-id.
*
* @param path String containing a (processed) resource path
* @return <code>ResourceInfoImpl</code> for the resource identified by the specified path or null if the path is unknown or does not describe a processed resource
*/
public ResourceInfoImpl getResourceInfoByPath(String path) {
AbstractInfo info = (AbstractInfo) m_pathToEntry.get(path);
if (info instanceof ResourceInfoImpl) {
return (ResourceInfoImpl) info;
}
return null;
}
/**
* Determines the info about either a bundle or processed resource based on it's path/resource-id.
*
* @param path String containing a resource path (either bundle or processed resource)
* @return <code>AbstractInfoImpl</code> for the resource identified by the specified path or null if the path is unknown
*/
protected AbstractInfo getAbstractInfoByPath(String path) {
return (AbstractInfo) m_pathToEntry.get(path);
}
/**
* Determines the info about a bundle based on it's path/resource-id.
*
* @param path String containing a bundle path
* @return <code>BundleInfoImpl</code> for the bundle resource identified by the specified path or null if the path is unknown or does not describe a bundle resource
*/
public BundleInfoImpl getBundleInfoByPath(String path) {
AbstractInfo info = (AbstractInfo) m_pathToEntry.get(path);
if (info instanceof BundleInfoImpl) {
return (BundleInfoImpl) info;
}
return null;
}
/**
* Determines the info about a bundle resource based on the bundle symbolic name.
*
* @param symbolicName String containing a bundle symbolic name
* @return <code>BundleInfoImpl</code> for the bundle identified by the specified symbolic name or null if the symbolic name is unknown
*/
public BundleInfoImpl getBundleInfoByName(String symbolicName) {
return (BundleInfoImpl) m_nameToBundleInfo.get(symbolicName);
}
/**
* Determines the data stream of a bundle resource based on the bundle symbolic name
*
* @param symbolicName Bundle symbolic name
* @return Stream to the bundle identified by the specified symbolic name or null if no such bundle exists in this deployment package.
* @throws IOException If the bundle can not be properly offered as an inputstream
*/
public abstract InputStream getBundleStream(String symbolicName) throws IOException;
/**
* Determines the next resource entry in this deployment package based on the order in which the resources appeared when the package was originally received.
*
* @return <code>AbstractInfo</code> describing the next resource entry (as determined by the order in which the deployment package was received originally) or null if there is no next entry
* @throws IOException if the next entry can not be properly determined
*/
public abstract AbstractInfo getNextEntry() throws IOException;
/**
* Determines the data stream to the current entry of this deployment package, use this together with the <code>getNextEntry</code> method.
*
* @return Stream to the current resource in the deployment package (as determined by the order in which the deployment package was received originally) or null if there is no entry
*/
public abstract InputStream getCurrentEntryStream();
}