/*
* 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.ivy.osgi.core;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.ivy.Ivy;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.id.ArtifactId;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.osgi.util.Version;
import org.apache.ivy.osgi.util.VersionRange;
import org.apache.ivy.plugins.matcher.ExactOrRegexpPatternMatcher;
import org.apache.ivy.plugins.matcher.PatternMatcher;
import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
public class BundleInfoAdapter {
public static final String CONF_NAME_DEFAULT = "default";
public static final Configuration CONF_DEFAULT = new Configuration(CONF_NAME_DEFAULT);
public static final String CONF_NAME_OPTIONAL = "optional";
public static final Configuration CONF_OPTIONAL = new Configuration(CONF_NAME_OPTIONAL,
Visibility.PUBLIC, "Optional dependencies", new String[] {CONF_NAME_DEFAULT}, true,
null);
public static final String CONF_NAME_TRANSITIVE_OPTIONAL = "transitive-optional";
public static final Configuration CONF_TRANSITIVE_OPTIONAL = new Configuration(
CONF_NAME_TRANSITIVE_OPTIONAL, Visibility.PUBLIC, "Optional dependencies",
new String[] {CONF_NAME_OPTIONAL}, true, null);
public static final String CONF_USE_PREFIX = "use_";
public static final String EXTRA_INFO_EXPORT_PREFIX = "_osgi_export_";
/**
*
* @param baseUri
* uri to help build the absolute url if the bundle info has a relative uri.
* @param bundle
* @param profileProvider
* @param parser
* @return
* @throws ProfileNotFoundException
*/
public static DefaultModuleDescriptor toModuleDescriptor(ModuleDescriptorParser parser, URI baseUri, BundleInfo bundle,
ExecutionEnvironmentProfileProvider profileProvider) throws ProfileNotFoundException {
DefaultModuleDescriptor md = new DefaultModuleDescriptor(parser, null);
md.addExtraAttributeNamespace("o", Ivy.getIvyHomeURL() + "osgi");
ModuleRevisionId mrid = asMrid(BundleInfo.BUNDLE_TYPE, bundle.getSymbolicName(), bundle.getVersion());
md.setResolvedPublicationDate(new Date());
md.setModuleRevisionId(mrid);
md.addConfiguration(CONF_DEFAULT);
md.addConfiguration(CONF_OPTIONAL);
md.addConfiguration(CONF_TRANSITIVE_OPTIONAL);
Set/* <String> */exportedPkgNames = new HashSet/* <String> */(bundle.getExports().size());
Iterator itExport = bundle.getExports().iterator();
while (itExport.hasNext()) {
ExportPackage exportPackage = (ExportPackage) itExport.next();
md.getExtraInfo().put(EXTRA_INFO_EXPORT_PREFIX + exportPackage.getName(),
exportPackage.getVersion().toString());
exportedPkgNames.add(exportPackage.getName());
String[] confDependencies = new String[exportPackage.getUses().size() + 1];
int i = 0;
Iterator itUse = exportPackage.getUses().iterator();
while (itUse.hasNext()) {
String use = (String) itUse.next();
confDependencies[i++] = CONF_USE_PREFIX + use;
}
confDependencies[i] = CONF_NAME_DEFAULT;
md.addConfiguration(new Configuration(CONF_USE_PREFIX + exportPackage.getName(),
Visibility.PUBLIC, "Exported package " + exportPackage.getName(),
confDependencies, true, null));
}
requirementAsDependency(md, bundle, exportedPkgNames);
URI uri = bundle.getUri();
if (uri != null) {
DefaultArtifact artifact = null;
if ("ivy".equals(uri.getScheme())) {
artifact = decodeIvyURI(uri);
} else {
if (!uri.isAbsolute()) {
uri = baseUri.resolve(uri);
}
try {
artifact = new DefaultArtifact(mrid, null, bundle.getSymbolicName(), "jar",
"jar", new URL(uri.toString()), null);
} catch (MalformedURLException e) {
throw new RuntimeException("Unable to make the uri into the url", e);
}
}
if (artifact != null) {
md.addArtifact(CONF_NAME_DEFAULT, artifact);
}
}
if (profileProvider != null) {
Iterator itEnv = bundle.getExecutionEnvironments().iterator();
while (itEnv.hasNext()) {
String env = (String) itEnv.next();
ExecutionEnvironmentProfile profile = profileProvider.getProfile(env);
if (profile == null) {
throw new ProfileNotFoundException("Execution environment profile " + env
+ " not found");
}
Iterator itPkg = profile.getPkgNames().iterator();
while (itPkg.hasNext()) {
String pkg = (String) itPkg.next();
ArtifactId id = new ArtifactId(ModuleId.newInstance(BundleInfo.BUNDLE_TYPE, pkg),
PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION,
PatternMatcher.ANY_EXPRESSION);
DefaultExcludeRule rule = new DefaultExcludeRule(id,
ExactOrRegexpPatternMatcher.INSTANCE, null);
String[] confs = md.getConfigurationsNames();
for (int i = 0; i < confs.length; i++) {
rule.addConfiguration(confs[i]);
}
md.addExcludeRule(rule);
}
}
}
return md;
}
public static List/*<String>*/ getConfigurations(BundleInfo bundle) {
List/*<String>*/ confs = new ArrayList();
confs.add(CONF_DEFAULT);
confs.add(CONF_OPTIONAL);
confs.add(CONF_TRANSITIVE_OPTIONAL);
Iterator itExport = bundle.getExports().iterator();
while (itExport.hasNext()) {
ExportPackage exportPackage = (ExportPackage) itExport.next();
confs.add(CONF_USE_PREFIX + exportPackage.getName());
}
return confs;
}
public static URI buildIvyURI(Artifact artifact) {
ModuleRevisionId mrid = artifact.getModuleRevisionId();
return asIvyURI(mrid.getOrganisation(), mrid.getName(), mrid.getBranch(),
mrid.getRevision(), artifact.getType(), artifact.getName(), artifact.getExt());
}
private static URI asIvyURI(String org, String name, String branch, String rev, String type,
String art, String ext) {
StringBuffer builder = new StringBuffer();
builder.append("ivy:///");
builder.append(org);
builder.append('/');
builder.append(name);
builder.append('?');
if (branch != null) {
builder.append("branch=");
builder.append(branch);
}
if (rev != null) {
builder.append("&rev=");
builder.append(rev);
}
if (type != null) {
builder.append("&type=");
builder.append(type);
}
if (art != null) {
builder.append("&art=");
builder.append(art);
}
if (ext != null) {
builder.append("&ext=");
builder.append(ext);
}
try {
return new URI(builder.toString());
} catch (URISyntaxException e) {
throw new RuntimeException("illformed ivy url", e);
}
}
private static DefaultArtifact decodeIvyURI(final URI uri) {
String org = null;
String name = null;
String branch = null;
String rev = null;
String art = null;
String type = null;
String ext = null;
String path = uri.getPath();
if (!path.startsWith("/")) {
throw new IllegalArgumentException("An ivy url should be of the form ivy:///org/module but was : " + uri);
}
int i = path.indexOf('/', 1);
if (i < 0) {
throw new IllegalArgumentException("Expecting an organisation in the ivy url: " + uri);
}
org = path.substring(1, i);
name = path.substring(i + 1);
String query = uri.getQuery();
String[] parameters = query.split("&");
for (int j = 0; j < parameters.length; j++) {
String parameter = parameters[j];
if (parameter.length() == 0) {
continue;
}
String[] nameAndValue = parameter.split("=");
if (nameAndValue.length != 2) {
throw new IllegalArgumentException("Malformed query string in the ivy url: " + uri);
} else if (nameAndValue[0].equals("branch")) {
branch = nameAndValue[1];
} else if (nameAndValue[0].equals("rev")) {
rev = nameAndValue[1];
} else if (nameAndValue[0].equals("art")) {
art = nameAndValue[1];
} else if (nameAndValue[0].equals("type")) {
type = nameAndValue[1];
} else if (nameAndValue[0].equals("ext")) {
ext = nameAndValue[1];
} else {
throw new IllegalArgumentException("Unrecognized parameter '" + nameAndValue[0]
+ " in the query string of the ivy url: " + uri);
}
}
ModuleRevisionId amrid = ModuleRevisionId.newInstance(org, name, branch, rev);
DefaultArtifact artifact = new DefaultArtifact(amrid, null, art, type, ext);
return artifact;
}
private static void requirementAsDependency(DefaultModuleDescriptor md, BundleInfo bundleInfo,
Set/* <String> */exportedPkgNames) {
Iterator itReq = bundleInfo.getRequirements().iterator();
while (itReq.hasNext()) {
BundleRequirement requirement = (BundleRequirement) itReq.next();
String type = requirement.getType();
String name = requirement.getName();
if (BundleInfo.PACKAGE_TYPE.equals(type) && exportedPkgNames.contains(name)) {
// don't declare package exported by the current bundle
continue;
}
ModuleRevisionId ddmrid = asMrid(type, name, requirement.getVersion());
DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(ddmrid, false);
String conf = CONF_NAME_DEFAULT;
if (BundleInfo.PACKAGE_TYPE.equals(type)) {
// declare the configuration for the package
conf = CONF_USE_PREFIX + name;
md.addConfiguration(new Configuration(CONF_USE_PREFIX + name, Visibility.PUBLIC,
"Exported package " + name, new String[] {CONF_NAME_DEFAULT}, true, null));
dd.addDependencyConfiguration(conf, conf);
}
if ("optional".equals(requirement.getResolution())) {
dd.addDependencyConfiguration(CONF_NAME_OPTIONAL, conf);
dd.addDependencyConfiguration(CONF_NAME_TRANSITIVE_OPTIONAL,
CONF_NAME_TRANSITIVE_OPTIONAL);
} else {
dd.addDependencyConfiguration(CONF_NAME_DEFAULT, conf);
}
md.addDependency(dd);
}
}
public static ModuleRevisionId asMrid(String type, String name, Version v) {
return ModuleRevisionId.newInstance(type, name, v == null ? null : v.toString());
}
public static ModuleRevisionId asMrid(String type, String name, VersionRange v) {
String revision;
if (v == null) {
revision = "[0,)";
} else {
revision = v.toIvyRevision();
}
return ModuleRevisionId.newInstance(type, name, revision);
}
public static class ProfileNotFoundException extends RuntimeException {
public ProfileNotFoundException(String msg) {
super(msg);
}
}
}