| /* |
| * 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.aries.subsystem.core.internal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.MalformedURLException; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.jar.Manifest; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.aries.subsystem.ContentHandler; |
| import org.apache.aries.subsystem.core.archive.Attribute; |
| import org.apache.aries.subsystem.core.archive.DeploymentManifest; |
| import org.apache.aries.subsystem.core.archive.Header; |
| import org.apache.aries.subsystem.core.archive.ImportPackageHeader; |
| import org.apache.aries.subsystem.core.archive.RequireBundleHeader; |
| import org.apache.aries.subsystem.core.archive.RequireCapabilityHeader; |
| import org.apache.aries.subsystem.core.archive.SubsystemContentHeader; |
| import org.apache.aries.subsystem.core.archive.SubsystemContentHeader.Clause; |
| import org.apache.aries.subsystem.core.archive.SubsystemImportServiceHeader; |
| import org.apache.aries.subsystem.core.archive.SubsystemManifest; |
| import org.apache.aries.subsystem.core.archive.SubsystemSymbolicNameHeader; |
| import org.apache.aries.subsystem.core.archive.SubsystemTypeHeader; |
| import org.apache.aries.subsystem.core.archive.SubsystemVersionHeader; |
| import org.apache.aries.util.filesystem.FileSystem; |
| import org.apache.aries.util.filesystem.IDirectory; |
| import org.apache.aries.util.filesystem.IFile; |
| import org.apache.aries.util.io.IOUtils; |
| import org.apache.aries.util.manifest.ManifestHeaderProcessor; |
| import org.apache.aries.util.manifest.ManifestProcessor; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.namespace.BundleNamespace; |
| import org.osgi.framework.namespace.ExecutionEnvironmentNamespace; |
| import org.osgi.framework.namespace.IdentityNamespace; |
| import org.osgi.framework.namespace.PackageNamespace; |
| 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.repository.Repository; |
| import org.osgi.service.resolver.ResolutionException; |
| import org.osgi.service.subsystem.Subsystem.State; |
| import org.osgi.service.subsystem.SubsystemConstants; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| public class RawSubsystemResource implements Resource { |
| private static final Logger logger = LoggerFactory.getLogger(RawSubsystemResource.class); |
| |
| private static final Pattern PATTERN = Pattern.compile("([^@/\\\\]+)(?:@(.+))?.esa"); |
| private static final String APPLICATION_IMPORT_SERVICE_HEADER = "Application-ImportService"; |
| |
| private static SubsystemManifest computeExistingSubsystemManifest(IDirectory directory) throws IOException { |
| Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/SUBSYSTEM.MF"); |
| if (manifest == null) |
| return null; |
| return new SubsystemManifest(manifest); |
| } |
| |
| private static SubsystemManifest computeNewSubsystemManifest() { |
| return new SubsystemManifest.Builder().build(); |
| } |
| |
| private static SubsystemManifest computeSubsystemManifest(IDirectory directory) throws IOException { |
| SubsystemManifest result = computeExistingSubsystemManifest(directory); |
| if (result == null) |
| result = computeNewSubsystemManifest(); |
| return result; |
| } |
| |
| private static String convertFileToLocation(IFile file) throws MalformedURLException { |
| String result = convertFileNameToLocation(file.getName()); |
| if (result == null) |
| result = file.toURL().toString(); |
| return result; |
| } |
| |
| private static String convertFileNameToLocation(String fileName) { |
| Matcher matcher = PATTERN.matcher(fileName); |
| if (!matcher.matches()) |
| return null; |
| String version = matcher.group(2); |
| return new SubsystemUri(matcher.group(1), version == null ? null |
| : Version.parseVersion(version), null).toString(); |
| } |
| |
| private final List<Capability> capabilities; |
| private final DeploymentManifest deploymentManifest; |
| private final long id; |
| private final Repository localRepository; |
| private final Location location; |
| private final BasicSubsystem parentSubsystem; |
| private final List<Requirement> requirements; |
| private final Collection<Resource> resources; |
| private final Resource fakeImportServiceResource; |
| private final SubsystemManifest subsystemManifest; |
| |
| public RawSubsystemResource(String location, IDirectory content, BasicSubsystem parent) throws URISyntaxException, IOException, ResolutionException { |
| id = SubsystemIdentifier.getNextId(); |
| this.location = new Location(location); |
| this.parentSubsystem = parent; |
| if (content == null) |
| content = this.location.open(); |
| try { |
| SubsystemManifest manifest = computeSubsystemManifest(content); |
| resources = computeResources(content, manifest); |
| fakeImportServiceResource = createFakeResource(manifest); |
| localRepository = computeLocalRepository(); |
| manifest = computeSubsystemManifestBeforeRequirements(manifest); |
| requirements = computeRequirements(manifest); |
| subsystemManifest = computeSubsystemManifestAfterRequirements(manifest); |
| capabilities = computeCapabilities(); |
| deploymentManifest = computeDeploymentManifest(content); |
| } |
| finally { |
| IOUtils.close(content.toCloseable()); |
| } |
| } |
| |
| public RawSubsystemResource(File file, BasicSubsystem parent) throws IOException, URISyntaxException, ResolutionException { |
| this(FileSystem.getFSRoot(file), parent); |
| } |
| |
| public RawSubsystemResource(IDirectory idir, BasicSubsystem parent) throws IOException, URISyntaxException, ResolutionException { |
| resources = Collections.emptyList(); |
| fakeImportServiceResource = null; // not needed for persistent subsystems |
| localRepository = computeLocalRepository(); |
| subsystemManifest = initializeSubsystemManifest(idir); |
| requirements = subsystemManifest.toRequirements(this); |
| capabilities = subsystemManifest.toCapabilities(this); |
| deploymentManifest = initializeDeploymentManifest(idir); |
| id = Long.parseLong(deploymentManifest.getHeaders().get(DeploymentManifest.ARIESSUBSYSTEM_ID).getValue()); |
| location = new Location(deploymentManifest.getHeaders().get(DeploymentManifest.ARIESSUBSYSTEM_LOCATION).getValue()); |
| parentSubsystem = parent; |
| } |
| |
| private static Resource createFakeResource(SubsystemManifest manifest) { |
| Header<?> importServiceHeader = manifest.getHeaders().get(APPLICATION_IMPORT_SERVICE_HEADER); |
| if (importServiceHeader == null) { |
| return null; |
| } |
| List<Capability> modifiableCaps = new ArrayList<Capability>(); |
| final List<Capability> fakeCapabilities = Collections.unmodifiableList(modifiableCaps); |
| Resource fakeResource = new Resource() { |
| |
| @Override |
| public List<Capability> getCapabilities(String namespace) { |
| if (namespace == null) { |
| return fakeCapabilities; |
| } |
| List<Capability> results = new ArrayList<Capability>(); |
| for (Capability capability : fakeCapabilities) { |
| if (namespace.equals(capability.getNamespace())) { |
| results.add(capability); |
| } |
| } |
| return results; |
| } |
| |
| @Override |
| public List<Requirement> getRequirements(String namespace) { |
| return Collections.emptyList(); |
| } |
| }; |
| |
| modifiableCaps.add(new OsgiIdentityCapability(fakeResource, Constants.ResourceTypeSynthesized, new Version(1,0,0), Constants.ResourceTypeSynthesized)); |
| Map<String, Map<String, String>> serviceImports = ManifestHeaderProcessor.parseImportString(importServiceHeader.getValue()); |
| for (Entry<String, Map<String, String>> serviceImport : serviceImports.entrySet()) { |
| Collection<String> objectClasses = new ArrayList<String>(Arrays.asList(serviceImport.getKey())); |
| String filter = serviceImport.getValue().get(IdentityNamespace.REQUIREMENT_FILTER_DIRECTIVE); |
| BasicCapability.Builder capBuilder = new BasicCapability.Builder(); |
| capBuilder.namespace(ServiceNamespace.SERVICE_NAMESPACE); |
| capBuilder.attribute(ServiceNamespace.CAPABILITY_OBJECTCLASS_ATTRIBUTE, objectClasses); |
| if (filter != null) |
| capBuilder.attributes(new HashMap<String, Object>(ManifestHeaderProcessor.parseFilter(filter))); |
| capBuilder.attribute("service.imported", ""); |
| capBuilder.resource(fakeResource); |
| modifiableCaps.add(capBuilder.build()); |
| } |
| |
| return fakeResource; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) |
| return true; |
| if (!(o instanceof RawSubsystemResource)) |
| return false; |
| RawSubsystemResource that = (RawSubsystemResource)o; |
| return getLocation().equals(that.getLocation()); |
| } |
| |
| @Override |
| 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 DeploymentManifest getDeploymentManifest() { |
| return deploymentManifest; |
| } |
| |
| public long getId() { |
| return id; |
| } |
| |
| public Repository getLocalRepository() { |
| return localRepository; |
| } |
| |
| public Location getLocation() { |
| return location; |
| } |
| |
| @Override |
| 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); |
| } |
| |
| public Collection<Resource> getResources() { |
| return Collections.unmodifiableCollection(resources); |
| } |
| |
| public SubsystemManifest getSubsystemManifest() { |
| return subsystemManifest; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = 17; |
| result = 31 * result + getLocation().hashCode(); |
| return result; |
| } |
| |
| private void addHeader(SubsystemManifest.Builder builder, Header<?> header) { |
| if (header == null) |
| return; |
| builder.header(header); |
| } |
| |
| private void addImportPackageHeader(SubsystemManifest.Builder builder) { |
| addHeader(builder, computeImportPackageHeader()); |
| } |
| |
| private void addRequireBundleHeader(SubsystemManifest.Builder builder) { |
| addHeader(builder, computeRequireBundleHeader()); |
| } |
| |
| private void addRequireCapabilityHeader(SubsystemManifest.Builder builder) { |
| addHeader(builder, computeRequireCapabilityHeader()); |
| } |
| |
| private void addSubsystemContentHeader(SubsystemManifest.Builder builder, SubsystemManifest manifest) { |
| addHeader(builder, computeSubsystemContentHeader(manifest)); |
| } |
| |
| private void addSubsystemImportServiceHeader(SubsystemManifest.Builder builder) { |
| addHeader(builder, computeSubsystemImportServiceHeader()); |
| } |
| |
| private void addSubsystemSymbolicNameHeader(SubsystemManifest.Builder builder, SubsystemManifest manifest) { |
| addHeader(builder, computeSubsystemSymbolicNameHeader(manifest)); |
| } |
| |
| private void addSubsystemVersionHeader(SubsystemManifest.Builder builder, SubsystemManifest manifest) { |
| addHeader(builder, computeSubsystemVersionHeader(manifest)); |
| } |
| |
| private List<Capability> computeCapabilities() { |
| return subsystemManifest.toCapabilities(this); |
| } |
| |
| private DeploymentManifest computeDeploymentManifest(IDirectory directory) throws IOException { |
| return computeExistingDeploymentManifest(directory); |
| } |
| |
| private DeploymentManifest computeExistingDeploymentManifest(IDirectory directory) throws IOException { |
| Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(directory, "OSGI-INF/DEPLOYMENT.MF"); |
| if (manifest == null) |
| return null; |
| return new DeploymentManifest(manifest); |
| } |
| |
| private ImportPackageHeader computeImportPackageHeader() { |
| if (requirements.isEmpty()) |
| return null; |
| ArrayList<ImportPackageHeader.Clause> clauses = new ArrayList<ImportPackageHeader.Clause>(requirements.size()); |
| for (Requirement requirement : requirements) { |
| if (!PackageNamespace.PACKAGE_NAMESPACE.equals(requirement.getNamespace())) |
| continue; |
| clauses.add(new ImportPackageHeader.Clause(requirement)); |
| } |
| if (clauses.isEmpty()) |
| return null; |
| clauses.trimToSize(); |
| return new ImportPackageHeader(clauses); |
| } |
| |
| private Repository computeLocalRepository() { |
| if (fakeImportServiceResource != null) { |
| Collection<Resource> temp = new ArrayList<Resource>(resources); |
| temp.add(fakeImportServiceResource); |
| return new LocalRepository(temp); |
| } |
| return new LocalRepository(resources); |
| } |
| |
| private RequireBundleHeader computeRequireBundleHeader() { |
| if (requirements.isEmpty()) |
| return null; |
| ArrayList<RequireBundleHeader.Clause> clauses = new ArrayList<RequireBundleHeader.Clause>(requirements.size()); |
| for (Requirement requirement : requirements) { |
| if (!BundleNamespace.BUNDLE_NAMESPACE.equals(requirement.getNamespace())) |
| continue; |
| clauses.add(new RequireBundleHeader.Clause(requirement)); |
| } |
| if (clauses.isEmpty()) |
| return null; |
| clauses.trimToSize(); |
| return new RequireBundleHeader(clauses); |
| } |
| |
| private RequireCapabilityHeader computeRequireCapabilityHeader() { |
| if (requirements.isEmpty()) |
| return null; |
| ArrayList<RequireCapabilityHeader.Clause> clauses = new ArrayList<RequireCapabilityHeader.Clause>(); |
| for (Requirement requirement : requirements) { |
| String namespace = requirement.getNamespace(); |
| if (namespace.startsWith("osgi.") && |
| // Don't filter out the osgi.ee namespace. |
| !namespace.equals(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE)) |
| continue; |
| clauses.add(new RequireCapabilityHeader.Clause(requirement)); |
| } |
| if (clauses.isEmpty()) |
| return null; |
| clauses.trimToSize(); |
| return new RequireCapabilityHeader(clauses); |
| } |
| |
| private List<Requirement> computeRequirements(SubsystemManifest manifest) throws ResolutionException { |
| if (isComposite(manifest)) |
| return manifest.toRequirements(this); |
| SubsystemContentHeader header = manifest.getSubsystemContentHeader(); |
| if (header == null) |
| return Collections.emptyList(); |
| // TODO Need the system repository in here. Preferred provider as well? |
| LocalRepository localRepo = new LocalRepository(resources); |
| RepositoryServiceRepository serviceRepo = new RepositoryServiceRepository(Activator.getInstance().getBundleContext()); |
| CompositeRepository compositeRepo = new CompositeRepository(localRepo, serviceRepo); |
| List<Requirement> requirements = header.toRequirements(this); |
| List<Resource> resources = new ArrayList<Resource>(requirements.size()); |
| for (Requirement requirement : requirements) { |
| Collection<Capability> capabilities = compositeRepo.findProviders(requirement); |
| if (capabilities.isEmpty()) |
| continue; |
| resources.add(capabilities.iterator().next().getResource()); |
| } |
| return new DependencyCalculator(resources).calculateDependencies(); |
| } |
| |
| private Collection<Resource> computeResources(IDirectory directory, SubsystemManifest manifest) throws IOException, URISyntaxException, ResolutionException { |
| List<IFile> files = directory.listFiles(); |
| if (files.isEmpty()) |
| return Collections.emptyList(); |
| ArrayList<Resource> result = new ArrayList<Resource>(files.size()); |
| for (IFile file : directory.listFiles()) { |
| if (file.isFile()) { |
| addResource(file, file.convertNested(), manifest, result); |
| } else { |
| addResource(file, file.convert(), manifest, result); |
| } |
| } |
| result.trimToSize(); |
| return result; |
| } |
| |
| private void addResource(IFile file, IDirectory content, SubsystemManifest manifest, ArrayList<Resource> result) throws URISyntaxException, |
| IOException, ResolutionException, MalformedURLException { |
| String name = file.getName(); |
| if (name.endsWith(".esa")) { |
| result.add(new RawSubsystemResource(convertFileToLocation(file), content, parentSubsystem)); |
| } else if (name.endsWith(".jar")) { |
| result.add(new BundleResource(file)); |
| } else { |
| // This is a different type of file. Add a file resource for it if there is a custom content handler for it. |
| FileResource fr = new FileResource(file); |
| fr.setCapabilities(computeFileCapabilities(fr, file, manifest)); |
| List<Capability> idcaps = fr.getCapabilities(IdentityNamespace.IDENTITY_NAMESPACE); |
| if (idcaps.size() > 0) { |
| Capability idcap = idcaps.get(0); |
| Object type = idcap.getAttributes().get(IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE); |
| if (type instanceof String && parentSubsystem != null) { |
| if (CustomResources.getCustomContentHandler(parentSubsystem, (String) type) != null) { |
| // Yes, there is a custom content handler, add it. |
| result.add(fr); |
| return; |
| } |
| } |
| } |
| |
| // There is no custom handler for this resource, let's check if it turns out to be a bundle |
| try { |
| result.add(new BundleResource(file)); |
| } catch (Exception e) { |
| // Ignore if the resource is an invalid bundle or not a bundle at all. |
| if (logger.isDebugEnabled()) { |
| logger.debug("File \"" + file.getName() + "\" in subsystem with location \"" + location + "\" will be ignored because it is not recognized as a supported resource", e); |
| } |
| } |
| } |
| } |
| |
| private List<Capability> computeFileCapabilities(FileResource resource, IFile file, SubsystemManifest manifest) { |
| SubsystemContentHeader ssch = manifest.getSubsystemContentHeader(); |
| if (ssch == null) |
| return Collections.emptyList(); |
| |
| for (Clause c : ssch.getClauses()) { |
| Attribute er = c.getAttribute(ContentHandler.EMBEDDED_RESOURCE_ATTRIBUTE); |
| if (er != null) { |
| if (file.getName().equals(er.getValue())) { |
| Map<String, Object> attrs = new HashMap<String, Object>(); |
| attrs.put(ContentHandler.EMBEDDED_RESOURCE_ATTRIBUTE, er.getValue()); |
| return Collections.<Capability> singletonList( |
| new OsgiIdentityCapability(resource, c.getSymbolicName(), c.getVersionRange().getLeft(), c.getType(), attrs)); |
| } |
| } |
| } |
| return Collections.emptyList(); |
| } |
| |
| private SubsystemContentHeader computeSubsystemContentHeader(SubsystemManifest manifest) { |
| SubsystemContentHeader header = manifest.getSubsystemContentHeader(); |
| if (header == null && !resources.isEmpty()) |
| header = SubsystemContentHeader.newInstance(resources); |
| return header; |
| } |
| |
| private SubsystemImportServiceHeader computeSubsystemImportServiceHeader() { |
| if (requirements.isEmpty()) |
| return null; |
| ArrayList<SubsystemImportServiceHeader.Clause> clauses = new ArrayList<SubsystemImportServiceHeader.Clause>(requirements.size()); |
| for (Requirement requirement : requirements) { |
| if (!ServiceNamespace.SERVICE_NAMESPACE.equals(requirement.getNamespace())) |
| continue; |
| clauses.add(new SubsystemImportServiceHeader.Clause(requirement)); |
| } |
| if (clauses.isEmpty()) |
| return null; |
| clauses.trimToSize(); |
| return new SubsystemImportServiceHeader(clauses); |
| } |
| |
| private SubsystemManifest computeSubsystemManifestAfterRequirements(SubsystemManifest manifest) { |
| if (isComposite(manifest)) |
| return manifest; |
| SubsystemManifest.Builder builder = new SubsystemManifest.Builder().manifest(manifest); |
| addImportPackageHeader(builder); |
| addRequireBundleHeader(builder); |
| addRequireCapabilityHeader(builder); |
| addSubsystemImportServiceHeader(builder); |
| return builder.build(); |
| } |
| |
| private SubsystemManifest computeSubsystemManifestBeforeRequirements(SubsystemManifest manifest) { |
| SubsystemManifest.Builder builder = new SubsystemManifest.Builder().manifest(manifest); |
| addSubsystemSymbolicNameHeader(builder, manifest); |
| addSubsystemVersionHeader(builder, manifest); |
| addSubsystemContentHeader(builder, manifest); |
| return builder.build(); |
| } |
| |
| private SubsystemSymbolicNameHeader computeSubsystemSymbolicNameHeader(SubsystemManifest manifest) { |
| SubsystemSymbolicNameHeader header = manifest.getSubsystemSymbolicNameHeader(); |
| if (header != null) |
| return header; |
| String symbolicName = location.getSymbolicName(); |
| if (symbolicName == null) |
| symbolicName = "org.apache.aries.subsystem." + id; |
| return new SubsystemSymbolicNameHeader(symbolicName); |
| } |
| |
| private SubsystemVersionHeader computeSubsystemVersionHeader(SubsystemManifest manifest) { |
| SubsystemVersionHeader header = manifest.getSubsystemVersionHeader(); |
| if (header.getVersion().equals(Version.emptyVersion) && location.getVersion() != null) |
| header = new SubsystemVersionHeader(location.getVersion()); |
| return header; |
| } |
| |
| private DeploymentManifest initializeDeploymentManifest(IDirectory idir) |
| throws IOException { |
| Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(idir, |
| "OSGI-INF/DEPLOYMENT.MF"); |
| if (manifest != null) |
| return new DeploymentManifest(manifest); |
| else |
| return new DeploymentManifest.Builder() |
| .manifest(getSubsystemManifest()) |
| .location(BasicSubsystem.ROOT_LOCATION).autostart(true).id(0) |
| .lastId(SubsystemIdentifier.getLastId()) |
| .state(State.INSTALLING) |
| .build(); |
| } |
| |
| private SubsystemManifest initializeSubsystemManifest(IDirectory idir) |
| throws IOException { |
| Manifest manifest = ManifestProcessor.obtainManifestFromAppDir(idir, |
| "OSGI-INF/SUBSYSTEM.MF"); |
| if (manifest != null) |
| return new SubsystemManifest(manifest); |
| else |
| return new SubsystemManifest.Builder() |
| .symbolicName(BasicSubsystem.ROOT_SYMBOLIC_NAME) |
| .version(BasicSubsystem.ROOT_VERSION) |
| .type(SubsystemTypeHeader.TYPE_APPLICATION |
| + ';' |
| + SubsystemTypeHeader.DIRECTIVE_PROVISION_POLICY |
| + ":=" |
| + SubsystemTypeHeader.PROVISION_POLICY_ACCEPT_DEPENDENCIES) |
| .build(); |
| } |
| |
| private boolean isComposite(SubsystemManifest manifest) { |
| return SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(manifest.getSubsystemTypeHeader().getType()); |
| } |
| } |