/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.aries.subsystem.core.internal; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.apache.aries.subsystem.core.archive.BundleManifest; import org.apache.aries.subsystem.core.archive.BundleRequiredExecutionEnvironmentHeader; import org.apache.aries.subsystem.core.archive.BundleSymbolicNameHeader; import org.apache.aries.subsystem.core.archive.BundleVersionHeader; import org.apache.aries.subsystem.core.archive.ExportPackageHeader; import org.apache.aries.subsystem.core.archive.FragmentHostCapability; import org.apache.aries.subsystem.core.archive.FragmentHostHeader; import org.apache.aries.subsystem.core.archive.FragmentHostRequirement; import org.apache.aries.subsystem.core.archive.ImportPackageHeader; import org.apache.aries.subsystem.core.archive.ProvideBundleCapability; import org.apache.aries.subsystem.core.archive.ProvideCapabilityCapability; import org.apache.aries.subsystem.core.archive.ProvideCapabilityHeader; import org.apache.aries.subsystem.core.archive.RequireBundleHeader; import org.apache.aries.subsystem.core.archive.RequireBundleRequirement; import org.apache.aries.subsystem.core.archive.RequireCapabilityHeader; import org.apache.aries.subsystem.core.archive.RequireCapabilityRequirement; import org.apache.aries.subsystem.core.archive.RequirementHeader; import org.apache.aries.util.filesystem.IDirectory; import org.apache.aries.util.filesystem.IFile; import org.apache.aries.util.io.IOUtils; import org.osgi.namespace.service.ServiceNamespace; import org.osgi.resource.Capability; import org.osgi.resource.Requirement; import org.osgi.resource.Resource; import org.osgi.service.subsystem.SubsystemException; public class BundleResource implements Resource, org.apache.aries.subsystem.core.repository.RepositoryContent { private static BundleManifest computeManifest(IDirectory directory) { return new BundleManifest(org.apache.aries.util.manifest.BundleManifest .fromBundle(directory) .getRawManifest()); } private final List<Capability> capabilities = new ArrayList<Capability>(); private final IFile content; private final BundleManifest manifest; private final List<Requirement> requirements = new ArrayList<Requirement>(); public BundleResource(IFile content) { this.content = content; IDirectory dir = content.isDirectory() ? content.convert() : content.convertNested(); manifest = computeManifest(dir); computeRequirementsAndCapabilities(dir); } public List<Capability> getCapabilities(String namespace) { if (namespace == null) return Collections.unmodifiableList(capabilities); ArrayList<Capability> result = new ArrayList<Capability>(capabilities.size()); for (Capability capability : capabilities) if (namespace.equals(capability.getNamespace())) result.add(capability); result.trimToSize(); return Collections.unmodifiableList(result); } public String getLocation() { return getFileName(content); } @Override public InputStream getContent() { try { if (content.isFile()) return content.open(); try { // Give the IDirectory a shot at opening in case it supports it. return content.open(); } catch (UnsupportedOperationException e) { // As a last ditch effort, try to jar up the contents. ByteArrayOutputStream baos = new ByteArrayOutputStream(); JarOutputStream out = new JarOutputStream(baos, manifest.getManifest()); try { jar(out, "", content.convert()); } finally { IOUtils.close(out); } return new ByteArrayInputStream(baos.toByteArray()); } } catch (Exception e) { throw new SubsystemException(e); } } public List<Requirement> getRequirements(String namespace) { if (namespace == null) return Collections.unmodifiableList(requirements); ArrayList<Requirement> result = new ArrayList<Requirement>(requirements.size()); for (Requirement requirement : requirements) if (namespace.equals(requirement.getNamespace())) result.add(requirement); result.trimToSize(); return Collections.unmodifiableList(result); } @Override public String toString() { return content.toString(); } private void computeCapabilitiesOtherThanService() { computeOsgiIdentityCapability(); computeOsgiWiringPackageCapabilities(); computeOsgiWiringBundleCapability(); computeGenericCapabilities(); } private void computeGenericCapabilities() { ProvideCapabilityHeader pch = (ProvideCapabilityHeader)manifest.getHeader(ProvideCapabilityHeader.NAME); if (pch != null) for (ProvideCapabilityHeader.Clause clause : pch.getClauses()) capabilities.add(new ProvideCapabilityCapability(clause, this)); } private void computeGenericRequirements() { RequireCapabilityHeader rch = (RequireCapabilityHeader)manifest.getHeader(RequireCapabilityHeader.NAME); if (rch != null) for (RequireCapabilityHeader.Clause clause : rch.getClauses()) requirements.add(new RequireCapabilityRequirement(clause, this)); } private void computeOsgiExecutionEnvironmentRequirement() { RequirementHeader<?> header = (RequirementHeader<?>)manifest.getHeader(BundleRequiredExecutionEnvironmentHeader.NAME); if (header == null) return; requirements.addAll(header.toRequirements(this)); } private void computeOsgiIdentityCapability() { capabilities.add(new OsgiIdentityCapability(this, manifest)); } private void computeOsgiWiringBundleCapability() { if (manifest.getHeader(org.osgi.framework.Constants.FRAGMENT_HOST) != null) { // The osgi.wiring.bundle capability is not provided by fragments. return; } BundleSymbolicNameHeader bsnh = (BundleSymbolicNameHeader)manifest.getHeader(BundleSymbolicNameHeader.NAME); BundleVersionHeader bvh = (BundleVersionHeader)manifest.getHeader(BundleVersionHeader.NAME); capabilities.add(new ProvideBundleCapability(bsnh, bvh, this)); } private void computeOsgiWiringBundleRequirements() { RequireBundleHeader rbh = (RequireBundleHeader)manifest.getHeader(RequireBundleHeader.NAME); if (rbh != null) for (RequireBundleHeader.Clause clause : rbh.getClauses()) requirements.add(new RequireBundleRequirement(clause, this)); } private void computeOsgiWiringHostCapability() { if (manifest.getHeader(org.osgi.framework.Constants.FRAGMENT_HOST) != null) { // The osgi.wiring.host capability is not provided by fragments. return; } BundleSymbolicNameHeader bsnh = (BundleSymbolicNameHeader)manifest.getHeader(BundleSymbolicNameHeader.NAME); BundleVersionHeader bvh = (BundleVersionHeader)manifest.getHeader(BundleVersionHeader.NAME); capabilities.add(new FragmentHostCapability(bsnh, bvh, this)); } private void computeOsgiWiringHostRequirement() { FragmentHostHeader fhh = (FragmentHostHeader)manifest.getHeader(FragmentHostHeader.NAME); if (fhh != null) { requirements.add(new FragmentHostRequirement(fhh.getClauses().iterator().next(), this)); } } private void computeOsgiWiringPackageCapabilities() { ExportPackageHeader eph = (ExportPackageHeader)manifest.getHeader(ExportPackageHeader.NAME); if (eph != null) capabilities.addAll(eph.toCapabilities(this)); } private void computeOsgiWiringPackageRequirements() { ImportPackageHeader iph = (ImportPackageHeader)manifest.getHeader(ImportPackageHeader.NAME); if (iph != null) requirements.addAll(iph.toRequirements(this)); } private void computeRequirementsAndCapabilities(IDirectory directory) { // Compute all requirements and capabilities other than those related // to services. computeRequirementsOtherThanService(); computeCapabilitiesOtherThanService(); // OSGi RFC 201 for R6: The presence of any Require/Provide-Capability // clauses in the osgi.service namespace overrides any service related // requirements or capabilities that might have been found by other // means. boolean computeServiceRequirements = getRequirements(ServiceNamespace.SERVICE_NAMESPACE).isEmpty(); boolean computeServiceCapabilities = getCapabilities(ServiceNamespace.SERVICE_NAMESPACE).isEmpty(); if (!(computeServiceCapabilities || computeServiceRequirements)) return; // Compute service requirements and capabilities if the optional // ModelledResourceManager service is present. ServiceModeller modeller = getServiceModeller(); if (modeller == null) return; ServiceModeller.ServiceModel model = modeller.computeRequirementsAndCapabilities(this, directory); if (computeServiceCapabilities) capabilities.addAll(model.getServiceCapabilities()); if (computeServiceRequirements) requirements.addAll(model.getServiceRequirements()); } private void computeRequirementsOtherThanService() { computeOsgiWiringPackageRequirements(); computeGenericRequirements(); computeOsgiWiringBundleRequirements(); computeOsgiExecutionEnvironmentRequirement(); computeOsgiWiringHostRequirement(); computeOsgiWiringHostCapability(); } private String getFileName(IFile file) { String name = file.getName(); if ("".equals(name)) { // The file is the root directory of an archive. Use the URL // instead. Using the empty string will likely result in duplicate // locations during installation. try { name = file.toURL().toString(); } catch (MalformedURLException e) { throw new SubsystemException(e); } } int index = name.lastIndexOf('/'); if (index == -1 || index == name.length() - 1) return name; return name.substring(index + 1); } private ServiceModeller getServiceModeller() { return Activator.getInstance().getServiceModeller(); } private void jar(JarOutputStream out, String prefix, IDirectory directory) throws IOException { List<IFile> files = directory.listFiles(); for (IFile f : files) { String fileName; if (f.isDirectory()) fileName = prefix + getFileName(f) + "/"; else fileName = prefix + getFileName(f); if ("META-INF/".equalsIgnoreCase(fileName) || "META-INF/MANIFEST.MF".equalsIgnoreCase(fileName)) continue; JarEntry entry = new JarEntry(fileName); entry.setSize(f.getSize()); entry.setTime(f.getLastModified()); out.putNextEntry(entry); if (f.isDirectory()) jar(out, fileName, f.convert()); else IOUtils.copy(f.open(), out); } } }