Java tutorial
/* * 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.s4i.feature; import org.apache.commons.lang.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactCollector; import org.apache.maven.model.License; import org.apache.maven.model.Organization; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.dependency.tree.DependencyNode; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; import org.apache.maven.shared.osgi.Maven2OsgiConverter; import org.codehaus.plexus.util.WriterFactory; import org.eclipse.core.runtime.CoreException; import org.eclipse.pde.internal.core.feature.*; import org.eclipse.pde.internal.core.ifeature.IFeature; import org.eclipse.pde.internal.core.ifeature.IFeatureImport; import org.eclipse.pde.internal.core.ifeature.IFeatureModel; import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin; import java.io.*; import java.util.*; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; /** * Generate an Eclipse feature file from the dependency list * * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a> * @version $Id$ * @requiresDependencyResolution runtime * @goal feature */ public class FeatureMojo extends AbstractMojo { public static final String ECLIPSE_FEATURE_TYPE = "eclipse-feature"; public static final String FEATURE_NAME_PROPERTY = "featureName"; public static final String PROVIDER_NAME_PROPERTY = "providerName"; public static final String DESCRIPTION_PROPERTY = "description"; public static final String COPYRIGHT_PROPERTY = "copyright"; public static final String LICENSE_URL_PROPERTY = "licenseURL"; public static final String LICENSE_PROPERTY = "license"; /** * The project we are executing. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * The list of resolved dependencies from the current project. Since we're not resolving the dependencies by hand * here, the build will fail if some of these dependencies do not resolve. * * @parameter default-value="${project.artifacts}" * @required * @readonly */ private Collection artifacts; /** * Directory where the generated files will be saved * * @parameter expression="${project.build.outputDirectory}" * @required */ private File outputDirectory; /** * Whether to add dependencies as imports or include them as part of the feature. If <code>true</code>, all Maven * dependencies that are not in the same groupId as this project will be added as Eclipse plugin dependencies. * Dependencies of type "eclipse-feature" are always imported. * * @parameter */ private boolean useImports = false; /** * @component */ private Maven2OsgiConverter maven2OsgiConverter; /** * @component */ private DependencyTreeBuilder dependencyTreeBuilder; /** * Local repository. * * @parameter expression="${localRepository}" * @required * @readonly */ private ArtifactRepository localRepository; /** * @component */ private ArtifactMetadataSource artifactMetadataSource; /** * @component */ private ArtifactCollector collector; /** * @component */ private ArtifactFactory factory; void setProject(MavenProject project) { this.project = project; } private MavenProject getProject() { return project; } void setArtifacts(Collection artifacts) { this.artifacts = artifacts; } private Collection getArtifacts() { return artifacts; } void setOutputDirectory(File outputDirectory) { this.outputDirectory = outputDirectory; } private File getOutputDirectory() { return outputDirectory; } public void execute() throws MojoExecutionException, MojoFailureException { Feature feature = createFeature(); writeFeature(feature); Properties properties = createProperties(); writeProperties(properties); } /** * The feature.xml version calculated based on the maven pom version */ private String getVersion() { String osgiVersion = maven2OsgiConverter.getVersion(getProject().getVersion()); // Replace "SNAPSHOT" with "qualifier" for Tycho String lastPart = StringUtils.substringAfterLast(osgiVersion, "."); if (StringUtils.equals(lastPart, "SNAPSHOT")) { return StringUtils.substringBeforeLast(osgiVersion, ".") + ".qualifier"; } return osgiVersion; } private Feature createFeature() throws MojoExecutionException, MojoFailureException { Feature feature; feature = new Feature(); try { IFeatureModel model = new WorkspaceFeatureModel(); feature.setModel(model); feature.setId(getProject().getArtifactId()); feature.setLabel('%' + FEATURE_NAME_PROPERTY); feature.setVersion(getVersion()); feature.setProviderName('%' + PROVIDER_NAME_PROPERTY); FeatureInfo description = new FeatureInfo(IFeature.INFO_DESCRIPTION); description.setModel(model); description.setDescription('%' + DESCRIPTION_PROPERTY); feature.setFeatureInfo(description, IFeature.INFO_DESCRIPTION); FeatureInfo license = new FeatureInfo(IFeature.INFO_LICENSE); license.setModel(model); license.setURL('%' + LICENSE_URL_PROPERTY); license.setDescription('%' + LICENSE_PROPERTY); feature.setFeatureInfo(license, IFeature.INFO_LICENSE); FeatureInfo copyright = new FeatureInfo(IFeature.INFO_COPYRIGHT); copyright.setModel(model); copyright.setDescription('%' + COPYRIGHT_PROPERTY); feature.setFeatureInfo(copyright, IFeature.INFO_COPYRIGHT); ArrayList plugins = new ArrayList(getArtifacts().size()); ArrayList imports = new ArrayList(getArtifacts().size()); DependencyNode dependencyTree; try { dependencyTree = dependencyTreeBuilder.buildDependencyTree(project, localRepository, factory, artifactMetadataSource, null, collector); } catch (DependencyTreeBuilderException e) { throw new MojoExecutionException("Unable to build dependency tree", e); } processNode(dependencyTree, model, plugins, imports); feature.addPlugins((IFeaturePlugin[]) plugins.toArray(new IFeaturePlugin[plugins.size()])); feature.addImports((IFeatureImport[]) imports.toArray(new IFeatureImport[imports.size()])); } catch (CoreException e) { throw new MojoExecutionException("Error creating the feature", e); } return feature; } private void processNode(DependencyNode dependencyNode, IFeatureModel model, List plugins, List imports) throws CoreException, MojoExecutionException { for (Iterator it = dependencyNode.getChildren().iterator(); it.hasNext();) { DependencyNode node = (DependencyNode) it.next(); if (node.getState() != DependencyNode.INCLUDED) { continue; } Artifact artifact = node.getArtifact(); if (Artifact.SCOPE_SYSTEM.equals(artifact.getScope())) { getLog().debug("Ignoring system scoped artifact " + artifact); continue; } /* do not add children of features, Eclipse will handle that, and features are always imported */ if (ECLIPSE_FEATURE_TYPE.equals(artifact.getType())) { imports.add(createFeatureImport(model, node)); continue; } else /* if it's not a feature we can include it or depend on it */ if (!useImports || artifact.getGroupId().equals(getProject().getGroupId())) { IFeaturePlugin plugin = createFeaturePlugin(model, node); if (plugin == null) { getLog().debug("Ignoring artifact " + artifact); } else { plugins.add(plugin); } } else { imports.add(createFeatureImport(model, node)); } /* continue with the children */ processNode(node, model, plugins, imports); } } private static String getBundleSymbolicName(Attributes attrs) { String name = attrs.getValue("Bundle-SymbolicName"); if (name == null) return null; StringTokenizer token = new StringTokenizer(name, ";"); return token.nextToken().trim(); } private static String getBundleVersion(Attributes attrs) { return attrs.getValue("Bundle-Version"); } private static boolean isFragment(Attributes attrs) { return attrs.getValue("Fragment-Host") != null; } private IFeaturePlugin createFeaturePlugin(IFeatureModel model, DependencyNode node) throws CoreException, MojoExecutionException { FeaturePlugin featurePlugin = new FeaturePlugin(); featurePlugin.setModel(model); File file = node.getArtifact().getFile(); if (file == null) { /* the file was already resolved, it's just that is not available in the dependency node */ file = new File(this.localRepository.getBasedir(), this.localRepository.pathOf(node.getArtifact())); } String type = node.getArtifact().getType(); if (!"jar".equals(type)) { getLog().error("Unsupported type \"" + type + "\" for artifact: " + node.getArtifact().getId()); return null; } try { JarFile jarFile = new JarFile(file); Manifest manifest = jarFile.getManifest(); if (manifest == null) { getLog().warn("Artifact is not an OSGi bundle and will be ignored (no manifest): " + node.getArtifact().getId()); return null; } Attributes attrs = manifest.getMainAttributes(); String bsn = getBundleSymbolicName(attrs); String ver = getBundleVersion(attrs); if (bsn == null || ver == null) { getLog().warn("Artifact is not an OSGi bundle and will be ignored (no BSN/version): " + node.getArtifact().getId()); return null; } featurePlugin.setId(bsn); featurePlugin.setVersion(ver); featurePlugin.setFragment(isFragment(attrs)); getLog().info("Adding OSGi bundle to feature: " + node.getArtifact().getId()); } catch (Exception e) { getLog().error("Artifact is not an OSGi bundle and will be ignored: " + node.getArtifact().getId(), e); return null; } long size = file.length() / 1024; featurePlugin.setInstallSize(size); featurePlugin.setDownloadSize(size); featurePlugin.setUnpack(false); return featurePlugin; } private IFeatureImport createFeatureImport(IFeatureModel model, DependencyNode node) throws CoreException { FeatureImport featureImport = new FeatureImport(); featureImport.setModel(model); featureImport.setId(maven2OsgiConverter.getBundleSymbolicName(node.getArtifact())); if (ECLIPSE_FEATURE_TYPE.equals(node.getArtifact().getType())) { featureImport.setType(IFeatureImport.FEATURE); } else { featureImport.setType(IFeatureImport.PLUGIN); } String version = node.getArtifact().getVersion(); featureImport.setVersion(maven2OsgiConverter.getVersion(version)); featureImport.setMatch(IFeatureImport.COMPATIBLE); return featureImport; } private void writeFeature(Feature feature) throws MojoExecutionException, MojoFailureException { File featureFile = new File(getOutputDirectory(), "feature.xml"); featureFile.getParentFile().mkdirs(); PrintWriter writer = null; try { writer = new PrintWriter(WriterFactory.newXmlWriter(featureFile)); writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"); feature.write("", writer); } catch (IOException e) { throw new MojoExecutionException("Unable to create feature file: " + featureFile, e); } finally { if (writer != null) { writer.close(); } } } private void put(Properties properties, String key, String value) { if (value != null) { properties.put(key, value); } } private Properties createProperties() throws MojoExecutionException, MojoFailureException { Properties properties = new Properties(); put(properties, FEATURE_NAME_PROPERTY, getProject().getName()); Organization organization = getProject().getOrganization(); if (organization != null) { put(properties, PROVIDER_NAME_PROPERTY, organization.getName()); StringBuffer sb = new StringBuffer(); sb.append("Copyright "); sb.append(organization.getName()); if (organization.getUrl() != null) { sb.append(" "); sb.append(organization.getUrl()); } put(properties, COPYRIGHT_PROPERTY, sb.toString()); } put(properties, DESCRIPTION_PROPERTY, getProject().getDescription()); List licenses = getProject().getLicenses(); if (licenses.isEmpty()) { throw new MojoFailureException("No license in pom. This feature will not work in an update site."); } else if (licenses.size() > 1) { throw new MojoFailureException( "There is more than one license in the pom, " + "features can only have one license"); } else { License license = (License) getProject().getLicenses().get(0); put(properties, LICENSE_URL_PROPERTY, license.getUrl()); put(properties, LICENSE_PROPERTY, license.getName()); } return properties; } private void writeProperties(Properties properties) throws MojoExecutionException, MojoFailureException { File propertiesFile = new File(getOutputDirectory(), "feature.properties"); propertiesFile.getParentFile().mkdirs(); OutputStream out = null; try { out = new FileOutputStream(propertiesFile); properties.store(out, ""); } catch (IOException e) { throw new MojoExecutionException("Unable to create properties file: " + propertiesFile, e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // ignore } } } } }