org.apache.geronimo.system.configuration.DependencyManager Maven / Gradle / Ivy
/*
* 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.geronimo.system.configuration;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.bundlerepository.RepositoryAdmin;
import org.apache.geronimo.gbean.GBeanLifecycle;
import org.apache.geronimo.gbean.annotation.GBean;
import org.apache.geronimo.gbean.annotation.OsgiService;
import org.apache.geronimo.gbean.annotation.ParamReference;
import org.apache.geronimo.gbean.annotation.ParamSpecial;
import org.apache.geronimo.gbean.annotation.SpecialAttributeType;
import org.apache.geronimo.kernel.config.InvalidConfigException;
import org.apache.geronimo.kernel.config.NoSuchConfigException;
import org.apache.geronimo.kernel.repository.AbstractRepository;
import org.apache.geronimo.kernel.repository.Artifact;
import org.apache.geronimo.kernel.repository.ArtifactResolver;
import org.apache.geronimo.kernel.repository.MissingDependencyException;
import org.apache.geronimo.kernel.repository.Repository;
import org.apache.geronimo.kernel.util.BundleUtil;
import org.apache.geronimo.kernel.util.IOUtils;
import org.apache.geronimo.system.plugin.model.DependencyType;
import org.apache.geronimo.system.plugin.model.PluginArtifactType;
import org.apache.geronimo.system.plugin.model.PluginType;
import org.apache.geronimo.system.plugin.model.PluginXmlUtil;
import org.apache.xbean.osgi.bundle.util.BundleDescription.ExportPackage;
import org.apache.xbean.osgi.bundle.util.BundleUtils;
import org.apache.xbean.osgi.bundle.util.HeaderParser;
import org.apache.xbean.osgi.bundle.util.HeaderParser.HeaderElement;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.Version;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @version $Rev: 1331657 $ $Date: 2012-04-28 10:17:12 +0800 (Sat, 28 Apr 2012) $
*/
@GBean
@OsgiService
public class DependencyManager implements SynchronousBundleListener, GBeanLifecycle {
private static final Logger log = LoggerFactory.getLogger(DependencyManager.class);
private final BundleContext bundleContext;
private final Collection repositories;
private RepositoryAdmin repositoryAdmin;
private final ArtifactResolver artifactResolver;
private final Map pluginMap = Collections.synchronizedMap(new WeakHashMap());
private final Map> dependentBundleIdsMap = new ConcurrentHashMap>();
private final Map> fullDependentBundleIdsMap = new ConcurrentHashMap>();
private final Map> bundleExportPackagesMap = new HashMap>();
private final Map artifactBundleMap = new ConcurrentHashMap();
private final Map bundleIdArtifactMap = new ConcurrentHashMap();
private ServiceReference respositoryAdminReference;
public DependencyManager(@ParamSpecial(type = SpecialAttributeType.bundleContext) BundleContext bundleContext,
@ParamReference(name = "Repositories", namingType = "Repository") Collection repositories,
@ParamReference(name = "ArtifactResolver", namingType = "ArtifactResolver") ArtifactResolver artifactResolver) {
this.bundleContext = bundleContext;
this.repositories = repositories;
this.artifactResolver = artifactResolver;
}
public void bundleChanged(BundleEvent bundleEvent) {
int eventType = bundleEvent.getType();
//TODO Need to optimize the codes, as we will not receive the INSTALLED event after the cache is created
if (eventType == BundleEvent.INSTALLED || eventType == BundleEvent.RESOLVED) {
installed(bundleEvent.getBundle());
} else if (eventType == BundleEvent.STARTING) {
starting(bundleEvent.getBundle());
} else if (eventType == BundleEvent.UNINSTALLED) {
uninstall(bundleEvent.getBundle());
}
}
public Set getExportedPackages(Bundle bundle) {
return getExportedPackages(bundle.getBundleId());
}
public Set getExportedPackages(Long bundleId) {
synchronized (bundleExportPackagesMap) {
Set exportPackages = bundleExportPackagesMap.get(bundleId);
if (exportPackages == null) {
exportPackages = getExportPackagesInternal(bundleContext.getBundle(bundleId));
bundleExportPackagesMap.put(bundleId, exportPackages);
}
return exportPackages;
}
}
public List getDependentBundles(Bundle bundle) {
Set dependentBundleIds = getDependentBundleIds(bundle);
if (dependentBundleIds.size() == 0) {
return Collections. emptyList();
}
List dependentBundles = new ArrayList(dependentBundleIds.size());
for (Long dependentBundleId : dependentBundleIds) {
Bundle b = bundleContext.getBundle(dependentBundleId);
if (b!=null) {
dependentBundles.add(b);
}
}
return dependentBundles;
}
public Set getDependentBundleIds(Bundle bundle) {
Set dependentBundleIds = dependentBundleIdsMap.get(bundle.getBundleId());
return dependentBundleIds == null ? Collections. emptySet() : new HashSet(dependentBundleIds);
}
public Set getFullDependentBundles(Bundle bundle) {
return getFullDependentBundles(bundle.getBundleId());
}
public Set getFullDependentBundles(Long bundleId) {
Set fullDependentBundleIds = getFullDependentBundleIds(bundleId);
if (fullDependentBundleIds.size() == 0) {
return Collections. emptySet();
}
Set dependentBundles = new HashSet(fullDependentBundleIds.size());
for (Long dependentBundleId : fullDependentBundleIds) {
Bundle b = bundleContext.getBundle(dependentBundleId);
if (b!=null) {
dependentBundles.add(b);
}
}
return dependentBundles;
}
public Set getFullDependentBundleIds(Bundle bundle) {
return getFullDependentBundleIds(bundle.getBundleId());
}
public Set getFullDependentBundleIds(Long bundleId) {
Set fullDependentBundleIds = fullDependentBundleIdsMap.get(bundleId);
return fullDependentBundleIds == null ? Collections. emptySet() : new HashSet(fullDependentBundleIds);
}
public Bundle getBundle(Artifact artifact) {
if (!artifact.isResolved()) {
try {
if (artifactResolver != null) {
artifact = artifactResolver.resolveInClassLoader(artifact);
}
} catch (MissingDependencyException e) {
}
}
return artifactBundleMap.get(artifact);
}
public Artifact getArtifact(long bundleId) {
return bundleIdArtifactMap.get(bundleId);
}
public Artifact toArtifact(String installationLocation) {
if (installationLocation == null) {
return null;
}
if (installationLocation.startsWith("mvn:")) {
String[] artifactFragments = installationLocation.substring(4).split("[/]");
if (artifactFragments.length < 2) {
return null;
}
return new Artifact(artifactFragments[0], artifactFragments[1], artifactFragments.length > 2 ? artifactFragments[2] : "",
artifactFragments.length > 3 && artifactFragments[3].length() > 0 ? artifactFragments[3] : "jar");
} else if(installationLocation.startsWith(BundleUtils.REFERENCE_SCHEME)) {
//TODO a better way for this ???
String bundleFilePath = BundleUtils.toFile(installationLocation).getAbsolutePath();
for (Repository repo : repositories) {
if (repo instanceof AbstractRepository) {
File rootFile = ((AbstractRepository) repo).getRootFile();
if (bundleFilePath.startsWith(rootFile.getAbsolutePath())) {
String artifactString = bundleFilePath.substring(rootFile.getAbsolutePath().length());
if (artifactString.startsWith(File.separator)) {
artifactString = artifactString.substring(File.separator.length());
}
String[] filePathFragments = artifactString.split("[" + (File.separator.equals("\\") ? "\\\\" : File.separator) + "]");
if (filePathFragments.length >= 4) {
StringBuilder groupId = new StringBuilder(filePathFragments[0]);
for (int i = 1; i <= filePathFragments.length - 4; i++) {
groupId.append(".").append(filePathFragments[i]);
}
return new Artifact(groupId.toString(), filePathFragments[filePathFragments.length - 3], filePathFragments[filePathFragments.length - 2],
filePathFragments[filePathFragments.length - 1].substring(filePathFragments[filePathFragments.length - 1].lastIndexOf('.') + 1));
}
}
}
}
}
return null;
}
public void updatePluginMetadata(Bundle bundle) {
Long bundleId = bundle.getBundleId();
dependentBundleIdsMap.remove(bundleId);
fullDependentBundleIdsMap.remove(bundleId);
pluginMap.remove(bundleId);
PluginArtifactType pluginArtifactType = getCachedPluginMetadata(bundle);
if (pluginArtifactType != null) {
List dependencies = pluginArtifactType.getDependency();
Set dependentBundleIds = new HashSet();
Set fullDependentBundleIds = new HashSet();
try {
for (DependencyType dependencyType : dependencies) {
Artifact artifact = dependencyType.toArtifact();
Bundle dependentBundle = getBundle(artifact);
if(dependentBundle == null) {
log.warn("Dependent artifact " + artifact + " could not be resolved, it will be ignored");
continue;
}
Long dependentBundleId = dependentBundle.getBundleId();
dependentBundleIds.add(dependentBundleId);
if (fullDependentBundleIds.add(dependentBundleId)) {
Set parentDependentBundleIds = fullDependentBundleIdsMap.get(dependentBundleId);
if (parentDependentBundleIds != null) {
fullDependentBundleIds.addAll(parentDependentBundleIds);
}
}
}
fullDependentBundleIdsMap.put(bundle.getBundleId(), fullDependentBundleIds);
dependentBundleIdsMap.put(bundle.getBundleId(), dependentBundleIds);
} catch (Exception e) {
log.error("Could not update bundle dependecy", e);
}
}
}
public static void updatePluginMetadata(BundleContext bundleContext, Bundle bundle) {
ServiceReference serviceReference = null;
try {
serviceReference = bundleContext.getServiceReference(DependencyManager.class.getName());
DependencyManager dependencyManager = null;
if (serviceReference != null) {
dependencyManager = (DependencyManager) bundleContext.getService(serviceReference);
dependencyManager.updatePluginMetadata(bundle);
}
} finally {
if (serviceReference != null) {
bundleContext.ungetService(serviceReference);
}
}
}
private PluginArtifactType addArtifactBundleEntry(Bundle bundle) {
PluginArtifactType pluginArtifactType = getCachedPluginMetadata(bundle);
Artifact artifact;
if (pluginArtifactType == null) {
artifact = toArtifact(bundle.getLocation());
} else {
artifact = pluginArtifactType.getModuleId().toArtifact();
}
if (artifact != null) {
artifactBundleMap.put(artifact, bundle);
bundleIdArtifactMap.put(bundle.getBundleId(), artifact);
} else {
if (log.isDebugEnabled()) {
log.debug("fail to resovle artifact from the bundle location " + bundle.getLocation());
}
}
return pluginArtifactType;
}
private void removeArtifactBundleEntry(Bundle bundle) {
Artifact artifact = bundleIdArtifactMap.remove(bundle.getBundleId());
if (artifact != null) {
artifactBundleMap.remove(artifact);
}
}
private Set getExportPackagesInternal(Bundle bundle) {
ServiceReference reference = null;
try {
reference = bundleContext.getServiceReference(PackageAdmin.class.getName());
if (reference == null) {
log.warn("No PackageAdmin service is found, fail to get export packages of " + bundle.getLocation());
return Collections. emptySet();
}
String exportPackageHeader = bundle.getHeaders().get(Constants.EXPORT_PACKAGE);
Map nameVersionExportPackageMap = new HashMap();
if (exportPackageHeader != null) {
List headerElements = HeaderParser.parseHeader(exportPackageHeader);
for (HeaderElement headerElement : headerElements) {
String version = headerElement.getAttribute(Constants.VERSION_ATTRIBUTE);
if (version == null) {
headerElement.addAttribute(Constants.VERSION_ATTRIBUTE, "0.0.0");
nameVersionExportPackageMap.put(headerElement.getName() + "0.0.0", headerElement);
} else {
nameVersionExportPackageMap.put(headerElement.getName() + new Version(version).toString(), headerElement);
}
}
}
PackageAdmin packageAdmin = (PackageAdmin) bundleContext.getService(reference);
ExportedPackage[] exportedPackages = packageAdmin.getExportedPackages(bundle);
if (exportedPackages != null) {
Set exportPackageNames = new HashSet();
for (ExportedPackage exportedPackage : exportedPackages) {
HeaderElement headerElement = nameVersionExportPackageMap.get(exportedPackage.getName() + exportedPackage.getVersion());
if (headerElement != null) {
exportPackageNames.add(new ExportPackage(headerElement.getName(), headerElement.getAttributes(), headerElement.getDirectives()));
}
}
return exportPackageNames;
}
return Collections. emptySet();
} finally {
if (reference != null) {
bundleContext.ungetService(reference);
}
}
}
private PluginArtifactType getPluginMetadata(Bundle bundle) {
PluginArtifactType pluginArtifactType = null;
InputStream in = null;
try {
URL info = bundle.getEntry("META-INF/geronimo-plugin.xml");
if (info != null) {
if (log.isDebugEnabled()) {
log.debug("found geronimo-plugin.xml for bundle " + bundle);
}
in = info.openStream();
} else if (bundle.getBundleContext() != null) {
File pluginMetadataFile = bundle.getBundleContext().getDataFile("geronimo-plugin.xml");
if (pluginMetadataFile.exists()) {
in = new FileInputStream(pluginMetadataFile);
}
}
if (in != null) {
PluginType pluginType = PluginXmlUtil.loadPluginMetadata(in);
pluginArtifactType = pluginType.getPluginArtifact().get(0);
} else {
if (log.isDebugEnabled()) {
log.debug("did not find geronimo-plugin.xml for bundle " + bundle);
}
}
} catch (Throwable e) {
log.warn("Could not read Geronimo metadata for bundle: " + bundle, e);
} finally {
IOUtils.close(in);
}
return pluginArtifactType;
}
private void uninstall(Bundle bundle) {
removeArtifactBundleEntry(bundle);
dependentBundleIdsMap.remove(bundle.getBundleId());
fullDependentBundleIdsMap.remove(bundle.getBundleId());
pluginMap.remove(bundle.getBundleId());
}
private void installRepository(Bundle bundle) {
if (repositoryAdmin != null) {
URL info = bundle.getEntry("OSGI-INF/obr/repository.xml");
if (info != null) {
if (log.isDebugEnabled()) {
log.debug("found repository.xml for bundle " + bundle);
}
try {
repositoryAdmin.addRepository(info);
} catch (Exception e) {
log.info("Error adding respository.xml for bundle " + bundle, e);
}
} else {
if (log.isDebugEnabled()) {
log.debug("did not find respository.xml for bundle " + bundle);
}
}
}
}
public PluginArtifactType getCachedPluginMetadata(Bundle bundle) {
PluginArtifactType pluginArtifactType = pluginMap.get(bundle.getBundleId());
if (pluginArtifactType == null) {
pluginArtifactType = getPluginMetadata(bundle);
if (pluginArtifactType != null) {
pluginMap.put(bundle.getBundleId(), pluginArtifactType);
}
//take this opportunity to install obr repo fragment
// installRepository(bundle);
}
return pluginArtifactType;
}
public void installed(Bundle bundle) {
if (bundleIdArtifactMap.containsKey(bundle.getBundleId())) {
return;
}
PluginArtifactType pluginArtifactType = addArtifactBundleEntry(bundle);
if (pluginArtifactType == null) {
return;
}
List dependencies = pluginArtifactType.getDependency();
Set dependentBundleIds = new HashSet();
Set fullDependentBundleIds = new HashSet();
try {
for (DependencyType dependencyType : dependencies) {
if (log.isDebugEnabled()) {
log.debug("Installing artifact: " + dependencyType);
}
Artifact artifact = dependencyType.toArtifact();
if (artifactResolver != null) {
artifact = artifactResolver.resolveInClassLoader(artifact);
}
String location = locateBundle(artifact);
try {
Bundle installedDependentBundle = bundleContext.installBundle(location);
long installedDependentBundleId = installedDependentBundle.getBundleId();
dependentBundleIds.add(installedDependentBundleId);
if (fullDependentBundleIds.add(installedDependentBundleId)) {
Set parentDependentBundleIds = fullDependentBundleIdsMap.get(installedDependentBundleId);
if (parentDependentBundleIds != null) {
fullDependentBundleIds.addAll(parentDependentBundleIds);
}
}
} catch (BundleException e) {
log.warn("Could not install bundle for artifact: " + artifact, e);
}
}
fullDependentBundleIdsMap.put(bundle.getBundleId(), fullDependentBundleIds);
dependentBundleIdsMap.put(bundle.getBundleId(), dependentBundleIds);
} catch (Exception e) {
log.error("Could not install bundle dependency", e);
}
}
private void starting(Bundle bundle) {
PluginArtifactType pluginArtifactType = getCachedPluginMetadata(bundle);
if (pluginArtifactType != null) {
List bundles = new ArrayList();
List dependencies = pluginArtifactType.getDependency();
boolean dependencyHierarchyBuildingRequired = !dependentBundleIdsMap.containsKey(bundle.getBundleId());
Set dependentBundleIds = null;
Set fullDependentBundleIds = null;
if (dependencyHierarchyBuildingRequired) {
dependentBundleIds = new HashSet();
fullDependentBundleIds = new HashSet();
}
try {
for (DependencyType dependencyType : dependencies) {
if (log.isDebugEnabled()) {
log.debug("Starting artifact: " + dependencyType);
}
Artifact artifact = dependencyType.toArtifact();
if (artifactResolver != null) {
artifact = artifactResolver.resolveInClassLoader(artifact);
}
String location = locateBundle(artifact);
Bundle b = bundleContext.installBundle(location);
if (dependencyHierarchyBuildingRequired) {
long startingBundleId = b.getBundleId();
dependentBundleIds.add(startingBundleId);
if (fullDependentBundleIds.add(startingBundleId)) {
Set parentDependentBundleIds = fullDependentBundleIdsMap.get(startingBundleId);
if (parentDependentBundleIds != null) {
fullDependentBundleIds.addAll(parentDependentBundleIds);
}
}
}
if (b.getState() != Bundle.ACTIVE ) {
bundles.add(b);
}
}
for (Bundle b : bundles) {
if (BundleUtils.canStart(b)) {
try {
b.start(Bundle.START_TRANSIENT);
} catch (BundleException e) {
log.warn("Could not start bundle: " + b, e);
}
}
}
if (dependencyHierarchyBuildingRequired) {
fullDependentBundleIdsMap.put(bundle.getBundleId(), fullDependentBundleIds);
dependentBundleIdsMap.put(bundle.getBundleId(), dependentBundleIds);
}
} catch (Exception e) {
log.error("Could not install bundle dependecy", e);
}
}
}
private String locateBundle(Artifact configurationId) throws NoSuchConfigException, IOException, InvalidConfigException {
for (Repository repo : repositories) {
if (repo.contains(configurationId)) {
return BundleUtils.toReferenceFileLocation(repo.getLocation(configurationId));
}
}
throw new NoSuchConfigException(configurationId);
}
@Override
public void doStart() throws Exception {
bundleContext.addBundleListener(this);
respositoryAdminReference = bundleContext.getServiceReference(RepositoryAdmin.class.getName());
repositoryAdmin = respositoryAdminReference == null ? null : (RepositoryAdmin) bundleContext.getService(respositoryAdminReference);
//init installed bundles
for (Bundle bundle : bundleContext.getBundles()) {
installed(bundle);
}
//Check the car who loads me ...
try {
PluginArtifactType pluginArtifact = getCachedPluginMetadata(bundleContext.getBundle());
if (pluginArtifact != null) {
Set dependentBundleIds = new HashSet();
for (DependencyType dependency : pluginArtifact.getDependency()) {
Bundle dependentBundle = getBundle(dependency.toArtifact());
if (dependentBundle != null) {
dependentBundleIds.add(dependentBundle.getBundleId());
}
}
long bundleId = bundleContext.getBundle().getBundleId();
dependentBundleIdsMap.put(bundleId, dependentBundleIds);
fullDependentBundleIdsMap.put(bundleId, dependentBundleIds);
}
} catch (Exception e) {
log.error("Fail to read the dependency info from bundle " + bundleContext.getBundle().getLocation());
}
}
@Override
public void doStop() throws Exception {
if (respositoryAdminReference != null) {
try {
bundleContext.ungetService(respositoryAdminReference);
} catch (Exception e) {
}
}
bundleContext.removeBundleListener(this);
//Some clean up work
pluginMap.clear();
dependentBundleIdsMap.clear();
fullDependentBundleIdsMap.clear();
bundleExportPackagesMap.clear();
artifactBundleMap.clear();
bundleIdArtifactMap.clear();
}
@Override
public void doFail() {
try {
doStop();
} catch (Exception e) {
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy