com.sun.enterprise.module.maven.OSGiPackager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hk2-maven Show documentation
Show all versions of hk2-maven Show documentation
Maven2 plugin for developing
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2007-2015 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.module.maven;
import com.sun.enterprise.module.common_impl.Jar;
import org.apache.maven.archiver.MavenArchiveConfiguration;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Collections;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.logging.Logger;
import static org.osgi.framework.Constants.*;
/**
* Prepares OSGi manifest entries in {@link MavenArchiveConfiguration}.
*
* @author [email protected]
*/
public class OSGiPackager {
/*
* TODO:
* 1. Does not yet calculate uses attribute in Export-Package
*/
private static final Logger logger = Logger.getAnonymousLogger();
private Properties props;
public OSGiPackager(Properties props) {
this.props = props;
}
/**
* Reads information from the POM and the artifact archive to configure
* the OSGi manifest entries. Returns a new set of entries if the archive
* does not already have manifest entries, else it uses the existing entries
* map. If any of the attribute already exists, then
* it skips its processing honoring user's request. It uses the following
* rules:
*
* Bundle-SymbolicName is assumed to be "${groupId}.${artifactId}"
* Bundle-Version is derived from "${pom.version}"
* using {@link VersionTranslator#MavenToOSGi(String)}
* Bundle-Description is assumed to be "${pom.description}".
* Bundle-Vendor is assumed to be "${pom.organization.name}".
* Require-Bundle is populated by values read from pom dependencies
* Note:
* There is no support for Export-Package yet.
* It sets Bundle-ManifestVersion as 2 which indicates OSGi r4 bundle.
*
* @param pom The Maven project object
* @param archive The archive that is being built
* @param classesDirectory output for javac
* @return Manifest entries
* @throws java.io.IOException
*/
public Map configureOSGiManifest(MavenProject pom, MavenArchiveConfiguration archive, File classesDirectory) throws IOException {
Map entries;
if(archive!=null)
entries = archive.getManifestEntries();
else
entries = new HashMap();
if(entries.get(BUNDLE_MANIFESTVERSION) == null) {
// 2 indicates compliance with r4, note: there is no value called 1
entries.put(BUNDLE_MANIFESTVERSION, "2");
}
if (entries.get(BUNDLE_NAME) == null) {
// Bundle-Name is a human readable localizable name that can contain spaces
entries.put(BUNDLE_NAME, pom.getName());
}
if (entries.get(BUNDLE_SYMBOLICNAME) == null) {
// OSGi convention is to use reverse domain name for SymbolicName, hence use .
entries.put(BUNDLE_SYMBOLICNAME, pom.getGroupId()+'.'+pom.getArtifactId());
}
if (entries.get(BUNDLE_VERSION) == null) {
entries.put(BUNDLE_VERSION, VersionTranslator.MavenToOSGi(pom.getVersion()));
}
if (entries.get(BUNDLE_DESCRIPTION) == null) {
if (pom.getDescription()!=null)
entries.put(BUNDLE_DESCRIPTION, pom.getDescription());
}
if (entries.get(BUNDLE_VENDOR) == null) {
if (pom.getOrganization()!=null && pom.getOrganization().getName() != null)
entries.put(BUNDLE_VENDOR, pom.getOrganization().getName());
}
// Handle Require-Bundle.
if (entries.get(REQUIRE_BUNDLE) == null) {
String requiredBundles = generateRequireBundleHeader(discoverRequiredBundles(pom));
if (requiredBundles.length()>0) {
entries.put(REQUIRE_BUNDLE, requiredBundles);
}
}
// Handle Export-Package
if (entries.get(EXPORT_PACKAGE) == null) {
List packages = discoverPackages(classesDirectory);
// don't use version until we resolve split package issues in GF
String exportPackages = generateExportPackageHeader(packages, null);
if (exportPackages.length()> 0) {
entries.put(EXPORT_PACKAGE, exportPackages);
}
}
return entries;
}
static class BundleDependency {
String bundleSymbolicName;
String versionRange; // version = 1.0.0.SNAPSHOT or "[1.0.0.SNAPSHOT, 1.0.0.SNAPSHOT]"
String resolution; // optional or mandatory
String visibility; // private or reexport
}
public List discoverRequiredBundles(MavenProject pom)
throws IOException {
List dependencies = new ArrayList();
for (Artifact a : (Set)pom.getDependencyArtifacts()) {
if("test".equals(a.getScope()) || "provided".equals(a.getScope()))
continue;
// http://www.nabble.com/V3-gf%3Arun-throws-NPE-tf4816802.html indicates
// that some artifacts are not resolved at this point. Not sure when that could happen
// so aborting with diagnostics if we find it. We need to better understand what this
// means and work accordingly. - KK
if(a.getFile()==null) {
throw new AssertionError(a.getId()+" is not resolved. a="+a);
}
Attributes attributes = null;
String name = null;
Manifest manifest = Jar.create(a.getFile()).getManifest();
if (manifest!=null) {
attributes = manifest.getMainAttributes();
name = attributes.getValue(BUNDLE_SYMBOLICNAME);
}
if (name != null && attributes.getValue(FRAGMENT_HOST)==null) {
// this is a OSGi host module
BundleDependency bd = new BundleDependency();
bd.bundleSymbolicName = name;
final String version = attributes.getValue(BUNDLE_VERSION);
// no need to translate, for it's already an OSGi version
bd.versionRange = version; // maps to [version, infinity)
// resolution=optional or mandatory
bd.resolution = props.getProperty("resolution", RESOLUTION_MANDATORY);
bd.visibility = props.getProperty("visibility", VISIBILITY_PRIVATE);
dependencies.add(bd);
}
}
return dependencies;
}
public String generateRequireBundleHeader(
List dependencies) {
// Require-Bundle:
// a.b.c;version=1.0.0.b58g;resolution:=mandatory;visibility:=reexport,
// p.q.r;version=1.0.0.SNAPSHOT;resolution:=optional;visibility:=private
StringBuilder requiredBundles = new StringBuilder();
for (BundleDependency bd : dependencies) {
if(requiredBundles.length()!=0) {
requiredBundles.append(",");
}
requiredBundles.append(bd.bundleSymbolicName);
if(bd.versionRange!= null) {
requiredBundles.append(';').
append(BUNDLE_VERSION_ATTRIBUTE).
append('=').
append(bd.versionRange);
}
requiredBundles.append(";").
append(RESOLUTION_DIRECTIVE).
append(":=").append(bd.resolution);
requiredBundles.append(';').
append(VISIBILITY_DIRECTIVE).
append(":=").append(bd.visibility);
}
return requiredBundles.toString();
}
static class ExportedPackage implements Comparable{
String packageName;
Collection packagesUsed; // uses directive
public ExportedPackage(
String packageName,
Collection packagesUsed) {
this.packageName = packageName;
this.packagesUsed = packagesUsed;
}
public ExportedPackage(String packageName) {
this(packageName, null);
}
@Override public int hashCode() {
return packageName.hashCode();
}
@Override public boolean equals(Object obj) {
if (obj instanceof ExportedPackage) {
return packageName.equals(ExportedPackage.class.cast(obj).packageName);
}
return false;
}
public int compareTo(ExportedPackage exportedPackage) {
return this.packageName.compareTo(exportedPackage.packageName);
}
}
public static List discoverPackages(final File classesDirectory) {
final List packages = new ArrayList();
classesDirectory.listFiles(
new FilenameFilter() {
public boolean accept(File dir, String name) {
final File file = new File(dir, name);
if (file.isDirectory()) {
file.listFiles(this);
}
final String CLASS_EXT = ".class";
if (!name.endsWith(CLASS_EXT)) {
return false;
}
final String packagePath = dir.getPath().substring(classesDirectory.getPath().length()+1);
String packageName = packagePath.replace(File.separatorChar, '.');
logger.fine("packageName = " + packageName);
final ExportedPackage ep = new ExportedPackage(packageName);
if (!packages.contains(ep)) {
packages.add(ep);
}
return true;
}
}
);
Collections.sort(packages);
return packages;
}
public static String generateExportPackageHeader(
Collection packages, String version) {
// Export-Packages=a.b.c;version=1.0.0.SNAPSHOT,p.q.r;x.y.z;version=2.0
if (packages.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for(ExportedPackage p : packages) {
if(sb.length()>0) {
sb.append(","); // exclude the first one
}
sb.append(p.packageName);
// TODO: Make use of uses directive
if (version!=null) {
sb.append(";").append(VERSION_ATTRIBUTE).append("=").append(version);
}
}
return sb.toString();
}
}