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.hsc.novelSpider.bundleplugin; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeSet; import java.util.jar.Attributes; import java.util.jar.Manifest; import org.apache.maven.archiver.ManifestSection; import org.apache.maven.archiver.MavenArchiveConfiguration; import org.apache.maven.archiver.MavenArchiver; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.License; import org.apache.maven.model.Model; import org.apache.maven.model.Resource; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.codehaus.plexus.archiver.UnArchiver; import org.codehaus.plexus.archiver.manager.ArchiverManager; import org.codehaus.plexus.util.DirectoryScanner; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import org.hsc.maven.shared.osgi.DefaultMaven2OsgiConverter; import org.hsc.maven.shared.osgi.Maven2OsgiConverter; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.Analyzer; import aQute.bnd.osgi.Builder; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.EmbeddedResource; import aQute.bnd.osgi.FileResource; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Processor; import aQute.lib.spring.SpringXMLType; /** * Create an OSGi bundle from Maven project * * @goal bundle * @phase package * @requiresDependencyResolution test * @description build an OSGi bundle jar * @threadSafe */ public class BundlePlugin extends AbstractMojo { /** * Directory where the manifest will be written * @parameter expression="${manifestLocation}" default-value="${project.build.outputDirectory}/META-INF" */ protected File manifestLocation; /** * File where the BND instructions will be dumped * @parameter expression="${dumpInstructions}" */ protected File dumpInstructions; /** * File where the BND class-path will be dumped * @parameter expression="${dumpClasspath}" */ protected File dumpClasspath; /** * When true, unpack the bundle contents to the outputDirectory * @parameter expression="${unpackBundle}" */ protected boolean unpackBundle; /** * Comma separated list of artifactIds to exclude from the dependency classpath passed to BND (use "true" to exclude everything) * @parameter expression="${excludeDependencies}" */ protected String excludeDependencies; /** * Classifier type of the bundle to be installed. For example, "jdk14". * Defaults to none which means this is the project's main bundle. * * @parameter */ protected String classifier; /** * @component */ private MavenProjectHelper m_projectHelper; /** * @component */ private ArchiverManager m_archiverManager; /** * @component */ private ArtifactHandlerManager m_artifactHandlerManager; /** * Project types which this plugin supports. * @parameter */ protected List<String> supportedProjectTypes = Arrays.asList(new String[] { "jar", "bundle" }); /** * The directory for the generated bundles. * @parameter expression="${project.build.outputDirectory}" * @required */ private File outputDirectory; /** * The directory for the generated JAR. * @parameter expression="${project.build.directory}" * @required */ private String buildDirectory; /** * The Maven project. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * The BND instructions for the bundle. * * @parameter */ private Map<String, String> instructions = new LinkedHashMap<String, String>(); /** * Use locally patched version for now. */ private Maven2OsgiConverter m_maven2OsgiConverter = new DefaultMaven2OsgiConverter(); /** * The archive configuration to use. * * @parameter */ private MavenArchiveConfiguration archive; // accessed indirectly in JarPluginConfiguration /** * @parameter default-value="${session}" * @required * @readonly */ private MavenSession m_mavenSession; private static final String MAVEN_SYMBOLICNAME = "maven-symbolicname"; private static final String MAVEN_RESOURCES = "{maven-resources}"; private static final String LOCAL_PACKAGES = "{local-packages}"; private static final String MAVEN_SOURCES = "{maven-sources}"; private static final String[] EMPTY_STRING_ARRAY = {}; private static final String[] DEFAULT_INCLUDES = { "**/**" }; private static final String NL = System.getProperty("line.separator"); protected Maven2OsgiConverter getMaven2OsgiConverter() { return m_maven2OsgiConverter; } protected void setMaven2OsgiConverter(Maven2OsgiConverter maven2OsgiConverter) { m_maven2OsgiConverter = maven2OsgiConverter; } protected MavenProject getProject() { return project; } /** * @see org.apache.maven.plugin.AbstractMojo#execute() */ public void execute() throws MojoExecutionException { getLog().warn(" execute1"); Properties properties = new Properties(); String projectType = getProject().getArtifact().getType(); // ignore unsupported project types, useful when bundleplugin is configured in parent pom if (!supportedProjectTypes.contains(projectType)) { getLog().warn( "Ignoring project type " + projectType + " - supportedProjectTypes = " + supportedProjectTypes); return; } MavenProject currentProject = getProject(); try { execute(currentProject, instructions, properties, getClasspath(currentProject)); } catch (IOException e) { throw new MojoExecutionException("Error calculating classpath for project " + currentProject, e); } } /* transform directives from their XML form to the expected BND syntax (eg. _include becomes -include) */ protected static Map<String, String> transformDirectives(Map<String, String> originalInstructions) { Map<String, String> transformedInstructions = new LinkedHashMap<String, String>(); for (Iterator<Map.Entry<String, String>> i = originalInstructions.entrySet().iterator(); i.hasNext();) { Map.Entry<String, String> e = i.next(); String key = (String) e.getKey(); if (key.startsWith("_")) { key = "-" + key.substring(1); } String value = e.getValue(); if (null == value) { value = ""; } else { value = value.replaceAll("\\p{Blank}*[\r\n]\\p{Blank}*", ""); } if (Analyzer.WAB.equals(key) && value.length() == 0) { // provide useful default value = "src/main/webapp/"; } transformedInstructions.put(key, value); } return transformedInstructions; } protected boolean reportErrors(String prefix, Analyzer analyzer) { List<String> errors = analyzer.getErrors(); List<String> warnings = analyzer.getWarnings(); for (Iterator<String> w = warnings.iterator(); w.hasNext();) { String msg = w.next(); getLog().warn("?" + prefix + " : " + msg); } boolean hasErrors = false; String fileNotFound = "Input file does not exist: "; for (Iterator<String> e = errors.iterator(); e.hasNext();) { String msg = e.next(); if (msg.startsWith(fileNotFound) && msg.endsWith("~")) { // treat as warning; this error happens when you have duplicate entries in Include-Resource String duplicate = Processor.removeDuplicateMarker(msg.substring(fileNotFound.length())); getLog().warn("?1" + prefix + " : Duplicate path '" + duplicate + "' in Include-Resource"); } else { getLog().error("?2" + prefix + " : " + msg); hasErrors = true; } } return hasErrors; } protected void execute(MavenProject currentProject, Map<String, String> originalInstructions, Properties properties, Jar[] classpath) throws MojoExecutionException { getLog().warn("2 execute "); try { File jarFile = new File(getBuildDirectory(), getBundleName(currentProject)); getLog().warn(" jarFile:" + jarFile.getAbsolutePath()); Builder builder = buildOSGiBundle(currentProject, originalInstructions, properties, classpath); boolean hasErrors = reportErrors("Bundle " + currentProject.getArtifact(), builder); if (hasErrors) { String failok = builder.getProperty("-failok"); if (null == failok || "false".equalsIgnoreCase(failok)) { jarFile.delete(); throw new MojoFailureException("Error(s) found in bundle configuration"); } } // attach bundle to maven project jarFile.getParentFile().mkdirs(); builder.getJar().write(jarFile); Artifact mainArtifact = currentProject.getArtifact(); if ("bundle".equals(mainArtifact.getType())) { // workaround for MNG-1682: force maven to install artifact using the "jar" handler mainArtifact.setArtifactHandler(m_artifactHandlerManager.getArtifactHandler("jar")); } if (null == classifier || classifier.trim().length() == 0) { mainArtifact.setFile(jarFile); } else { m_projectHelper.attachArtifact(currentProject, jarFile, classifier); } if (unpackBundle) { unpackBundle(jarFile); } if (manifestLocation != null) { File outputFile = new File(manifestLocation, "MANIFEST.MF"); try { Manifest manifest = builder.getJar().getManifest(); ManifestPlugin.writeManifest(manifest, outputFile); } catch (IOException e) { getLog().error("Error trying to write Manifest to file " + outputFile, e); } } // cleanup... builder.close(); } catch (MojoFailureException e) { getLog().error(e.getLocalizedMessage(), e); throw new MojoExecutionException("Error(s) found in bundle configuration", e); } catch (Exception e) { getLog().error("An internal error occurred", e); throw new MojoExecutionException("OSGI", e); } } protected Builder getOSGiBuilder(MavenProject currentProject, Map<String, String> originalInstructions, Properties properties, Jar[] classpath) throws Exception { properties.putAll(getDefaultProperties(currentProject)); properties.putAll(transformDirectives(originalInstructions)); Builder builder = new Builder(); builder.setBase(getBase(currentProject)); builder.setProperties(sanitize(properties)); if (classpath != null) { builder.setClasspath(classpath); } List<String> errors = builder.getErrors(); if (null != errors && errors.size() > 0) { for (String str : errors) { getLog().info("" + str); } } return builder; } protected static Properties sanitize(Properties properties) { // convert any non-String keys/values to Strings Properties sanitizedEntries = new Properties(); for (Iterator itr = properties.entrySet().iterator(); itr.hasNext();) { Map.Entry entry = (Map.Entry) itr.next(); if (entry.getKey() instanceof String == false) { String key = sanitize(entry.getKey()); if (!properties.containsKey(key)) { sanitizedEntries.setProperty(key, sanitize(entry.getValue())); } itr.remove(); } else if (entry.getValue() instanceof String == false) { entry.setValue(sanitize(entry.getValue())); } } properties.putAll(sanitizedEntries); return properties; } protected static String sanitize(Object value) { if (value instanceof String) { return (String) value; } else if (value instanceof Iterable) { String delim = ""; StringBuilder buf = new StringBuilder(); for (Object i : (Iterable<?>) value) { buf.append(delim).append(i); delim = ", "; } return buf.toString(); } else if (value.getClass().isArray()) { String delim = ""; StringBuilder buf = new StringBuilder(); for (int i = 0, len = Array.getLength(value); i < len; i++) { buf.append(delim).append(Array.get(value, i)); delim = ", "; } return buf.toString(); } else { return String.valueOf(value); } } protected void addMavenInstructions(MavenProject currentProject, Builder builder) throws Exception { if (currentProject.getBasedir() != null) { // update BND instructions to add included Maven resources includeMavenResources(currentProject, builder, getLog()); // calculate default export/private settings based on sources addLocalPackages(outputDirectory, builder); // tell BND where the current project source resides addMavenSourcePath(currentProject, builder, getLog()); } // update BND instructions to embed selected Maven dependencies Collection<Artifact> embeddableArtifacts = getEmbeddableArtifacts(currentProject, builder); new DependencyEmbedder(getLog(), embeddableArtifacts).processHeaders(builder); if (dumpInstructions != null) { StringBuilder buf = new StringBuilder(); getLog().debug("BND Instructions:" + NL + dumpInstructions(builder.getProperties(), buf)); if (dumpInstructions != null) { getLog().info("Writing BND instructions to " + dumpInstructions); dumpInstructions.getParentFile().mkdirs(); FileUtils.fileWrite(dumpInstructions, "# BND instructions" + NL + buf); } } if (dumpClasspath != null) { StringBuilder buf = new StringBuilder(); getLog().debug("BND Classpath:" + NL + dumpClasspath(builder.getClasspath(), buf)); if (dumpClasspath != null) { getLog().info("Writing BND classpath to " + dumpClasspath); dumpClasspath.getParentFile().mkdirs(); FileUtils.fileWrite(dumpClasspath, "# BND classpath" + NL + buf); } } List<String> errors = builder.getErrors(); if (null != errors && errors.size() > 0) { for (String str : errors) { getLog().info("" + str); } } List<String> warns = builder.getWarnings(); if (null != warns && warns.size() > 0) { for (String str : warns) { getLog().info("" + str); } } } protected Builder buildOSGiBundle(MavenProject currentProject, Map<String, String> originalInstructions, Properties properties, Jar[] classpath) throws Exception { Builder builder = getOSGiBuilder(currentProject, originalInstructions, properties, classpath); getLog().warn("buildOSGiBundle"); addMavenInstructions(currentProject, builder); if (builder.getErrors().size() > 0 || builder.getWarnings().size() > 0) { getLog().warn("?1"); } builder.build(); if (builder.getErrors().size() > 0 || builder.getWarnings().size() > 0) { getLog().warn("?2"); } mergeMavenManifest(currentProject, builder); return builder; } protected static StringBuilder dumpInstructions(Properties properties, StringBuilder buf) { try { buf.append("#-----------------------------------------------------------------------" + NL); Properties stringProperties = new Properties(); for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) { // we can only store String properties String key = (String) e.nextElement(); String value = properties.getProperty(key); if (value != null) { stringProperties.setProperty(key, value); } } ByteArrayOutputStream out = new ByteArrayOutputStream(); stringProperties.store(out, null); // properties encoding is 8859_1 buf.append(out.toString("8859_1")); buf.append("#-----------------------------------------------------------------------" + NL); } catch (Throwable e) { // ignore... } return buf; } protected StringBuilder dumpClasspath(List<Jar> classpath, StringBuilder buf) { try { buf.append("#-----------------------------------------------------------------------" + NL); buf.append("-classpath:\\" + NL); for (Iterator<Jar> i = classpath.iterator(); i.hasNext();) { File path = i.next().getSource(); if (path != null) { buf.append(' ' + path.toString() + (i.hasNext() ? ",\\" : "") + NL); } } buf.append("#-----------------------------------------------------------------------" + NL); } catch (Throwable e) { this.getLog().error(e); } return buf; } protected StringBuilder dumpManifest(Manifest manifest, StringBuilder buf) { try { buf.append("#-----------------------------------------------------------------------" + NL); ByteArrayOutputStream out = new ByteArrayOutputStream(); Jar.writeManifest(manifest, out); // manifest encoding is UTF8 buf.append(out.toString("UTF8")); buf.append("#-----------------------------------------------------------------------" + NL); } catch (Throwable e) { this.getLog().error(e); } return buf; } protected static void includeMavenResources(MavenProject currentProject, Analyzer analyzer, Log log) { // pass maven resource paths onto BND analyzer final String mavenResourcePaths = getMavenResourcePaths(currentProject); final String includeResource = (String) analyzer.getProperty(Analyzer.INCLUDE_RESOURCE); if (includeResource != null) { if (includeResource.indexOf(MAVEN_RESOURCES) >= 0) { // if there is no maven resource path, we do a special treatment and replace // every occurance of MAVEN_RESOURCES and a following comma with an empty string if (mavenResourcePaths.length() == 0) { String cleanedResource = removeTagFromInstruction(includeResource, MAVEN_RESOURCES); if (cleanedResource.length() > 0) { analyzer.setProperty(Analyzer.INCLUDE_RESOURCE, cleanedResource); } else { analyzer.unsetProperty(Analyzer.INCLUDE_RESOURCE); } } else { String combinedResource = StringUtils.replace(includeResource, MAVEN_RESOURCES, mavenResourcePaths); analyzer.setProperty(Analyzer.INCLUDE_RESOURCE, combinedResource); } } else if (mavenResourcePaths.length() > 0) { log.warn(Analyzer.INCLUDE_RESOURCE + ": overriding " + mavenResourcePaths + " with " + includeResource + " (add " + MAVEN_RESOURCES + " if you want to include the maven resources)"); } } else if (mavenResourcePaths.length() > 0) { analyzer.setProperty(Analyzer.INCLUDE_RESOURCE, mavenResourcePaths); } } protected void mergeMavenManifest(MavenProject currentProject, Builder builder) throws Exception { Jar jar = builder.getJar(); if (getLog().isDebugEnabled()) { getLog().debug("BND Manifest:" + NL + dumpManifest(jar.getManifest(), new StringBuilder())); } boolean addMavenDescriptor = currentProject.getBasedir() != null; try { /* * Grab customized manifest entries from the maven-jar-plugin configuration */ MavenArchiveConfiguration archiveConfig = JarPluginConfiguration .getArchiveConfiguration(currentProject); String mavenManifestText = new MavenArchiver().getManifest(currentProject, archiveConfig).toString(); addMavenDescriptor = addMavenDescriptor && archiveConfig.isAddMavenDescriptor(); Manifest mavenManifest = new Manifest(); // First grab the external manifest file (if specified and different to target location) File externalManifestFile = archiveConfig.getManifestFile(); if (null != externalManifestFile && externalManifestFile.exists() && !externalManifestFile.equals(new File(manifestLocation, "MANIFEST.MF"))) { InputStream mis = new FileInputStream(externalManifestFile); mavenManifest.read(mis); mis.close(); } // Then apply customized entries from the jar plugin; note: manifest encoding is UTF8 mavenManifest.read(new ByteArrayInputStream(mavenManifestText.getBytes("UTF8"))); if (!archiveConfig.isManifestSectionsEmpty()) { /* * Add customized manifest sections (for some reason MavenArchiver doesn't do this for us) */ List<ManifestSection> sections = archiveConfig.getManifestSections(); for (Iterator<ManifestSection> i = sections.iterator(); i.hasNext();) { ManifestSection section = (ManifestSection) i.next(); Attributes attributes = new Attributes(); if (!section.isManifestEntriesEmpty()) { Map<String, String> entries = section.getManifestEntries(); for (Iterator<Map.Entry<String, String>> j = entries.entrySet().iterator(); j.hasNext();) { Map.Entry<String, String> entry = j.next(); attributes.putValue(entry.getKey(), entry.getValue()); } } mavenManifest.getEntries().put(section.getName(), attributes); } } Attributes mainMavenAttributes = mavenManifest.getMainAttributes(); mainMavenAttributes.putValue("Created-By", "novelSpide Maven osgi Plugin"); String[] removeHeaders = builder.getProperty(Constants.REMOVEHEADERS, "").split(","); // apply -removeheaders to the custom manifest for (int i = 0; i < removeHeaders.length; i++) { for (Iterator j = mainMavenAttributes.keySet().iterator(); j.hasNext();) { if (j.next().toString().matches(removeHeaders[i].trim())) { j.remove(); } } } /* * Overlay generated bundle manifest with customized entries */ Manifest bundleManifest = jar.getManifest(); bundleManifest.getMainAttributes().putAll(mainMavenAttributes); bundleManifest.getEntries().putAll(mavenManifest.getEntries()); jar.setManifest(bundleManifest); if (null != instructions.get("Import-Package")) { bundleManifest.getMainAttributes().putValue("Import-Package", instructions.get("Import-Package")); } if (null != instructions.get("Export-Package")) { bundleManifest.getMainAttributes().putValue("Export-Package", instructions.get("Export-Package")); } getLog().info( "hsc info Import-Package:" + bundleManifest.getMainAttributes().getValue("Import-Package")); getLog().info( "hsc info Export-Package:" + bundleManifest.getMainAttributes().getValue("Export-Package")); // adjust the import package attributes so that optional dependencies use // optional resolution. // String importPackages = bundleManifest.getMainAttributes().getValue( "Import-Package" ); // if ( importPackages != null ){ // Set<String> optionalPackages = getOptionalPackages( currentProject ); // //// Map<String, Map<String, String>> values = new Analyzer().parseHeader( importPackages ); // // Parameters params= new Analyzer().parseHeader( importPackages ); // // for ( String pkg: params.keySet() ) // { //// String pkg = entry.getKey(); // Map<String, String> options = params.get(pkg); // if ( !options.containsKey( "resolution:" ) && optionalPackages.contains( pkg ) ) // { // options.put( "resolution:", "optional" ); // } // } // String result = Processor.printClauses( params ); // getLog().info("hsc info replace Import-Package:"+bundleManifest.getMainAttributes().getValue( "Import-Package" )); // // bundleManifest.getMainAttributes().putValue( "Import-Package", result ); // } // jar.setManifest( bundleManifest ); } catch (Exception e) { getLog().warn("Unable to merge Maven manifest: " + e.getLocalizedMessage()); } if (addMavenDescriptor) { doMavenMetadata(currentProject, jar); } getLog().debug("Final Manifest:" + NL + dumpManifest(jar.getManifest(), new StringBuilder())); builder.setJar(jar); } protected Set<String> getOptionalPackages(MavenProject currentProject) throws IOException, MojoExecutionException { ArrayList<Artifact> inscope = new ArrayList<Artifact>(); final Collection<Artifact> artifacts = getSelectedDependencies(currentProject.getArtifacts()); for (Iterator<Artifact> it = artifacts.iterator(); it.hasNext();) { Artifact artifact = it.next(); if (artifact.getArtifactHandler().isAddedToClasspath()) { if (!Artifact.SCOPE_TEST.equals(artifact.getScope())) { inscope.add(artifact); } } } HashSet<String> optionalArtifactIds = new HashSet<String>(); for (Iterator<Artifact> it = inscope.iterator(); it.hasNext();) { Artifact artifact = (Artifact) it.next(); if (artifact.isOptional()) { String id = artifact.toString(); if (artifact.getScope() != null) { // strip the scope... id = id.replaceFirst(":[^:]*$", ""); } optionalArtifactIds.add(id); } } HashSet<String> required = new HashSet<String>(); HashSet<String> optional = new HashSet<String>(); for (Iterator<Artifact> it = inscope.iterator(); it.hasNext();) { Artifact artifact = it.next(); File file = getFile(artifact); if (file == null) { continue; } Jar jar = new Jar(artifact.getArtifactId(), file); if (isTransitivelyOptional(optionalArtifactIds, artifact)) { optional.addAll(jar.getPackages()); } else { required.addAll(jar.getPackages()); } jar.close(); } optional.removeAll(required); return optional; } /** * Check to see if any dependency along the dependency trail of * the artifact is optional. * * @param artifact */ protected boolean isTransitivelyOptional(HashSet<String> optionalArtifactIds, Artifact artifact) { List<String> trail = artifact.getDependencyTrail(); for (Iterator<String> iterator = trail.iterator(); iterator.hasNext();) { String next = iterator.next(); if (optionalArtifactIds.contains(next)) { return true; } } return false; } private void unpackBundle(File jarFile) { File outputDir = getOutputDirectory(); if (null == outputDir) { outputDir = new File(getBuildDirectory(), "classes"); } try { /* * this directory must exist before unpacking, otherwise the plexus * unarchiver decides to use the current working directory instead! */ if (!outputDir.exists()) { outputDir.mkdirs(); } UnArchiver unArchiver = m_archiverManager.getUnArchiver("jar"); unArchiver.setDestDirectory(outputDir); unArchiver.setSourceFile(jarFile); unArchiver.extract(); } catch (Exception e) { getLog().error("Problem unpacking " + jarFile + " to " + outputDir, e); } } protected static String removeTagFromInstruction(String instruction, String tag) { StringBuffer buf = new StringBuffer(); String[] clauses = instruction.split(","); for (int i = 0; i < clauses.length; i++) { String clause = clauses[i].trim(); if (!tag.equals(clause)) { if (buf.length() > 0) { buf.append(','); } buf.append(clause); } } return buf.toString(); } private Map<String, Object> getProperties(Model projectModel, String prefix) { Map<String, Object> properties = new LinkedHashMap<String, Object>(); Method methods[] = Model.class.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { String name = methods[i].getName(); if (name.startsWith("get")) { try { Object v = methods[i].invoke(projectModel, null); if (v != null) { name = prefix + Character.toLowerCase(name.charAt(3)) + name.substring(4); if (v.getClass().isArray()) properties.put(name, Arrays.asList((Object[]) v).toString()); else properties.put(name, v); } } catch (Exception e) { this.getLog().error(e); } } } return properties; } private static StringBuffer printLicenses(List<License> licenses) { if (licenses == null || licenses.size() == 0) return null; StringBuffer sb = new StringBuffer(); String del = ""; for (Iterator<License> i = licenses.iterator(); i.hasNext();) { License l = i.next(); String url = l.getUrl(); if (url == null) continue; sb.append(del); sb.append(url); del = ", "; } if (sb.length() == 0) return null; return sb; } /** * @param jar * @throws IOException */ private void doMavenMetadata(MavenProject currentProject, Jar jar) throws IOException { String path = "META-INF/maven/" + currentProject.getGroupId() + "/" + currentProject.getArtifactId(); File pomFile = new File(currentProject.getBasedir(), "pom.xml"); jar.putResource(path + "/pom.xml", new FileResource(pomFile)); Properties p = new Properties(); p.put("version", currentProject.getVersion()); p.put("groupId", currentProject.getGroupId()); p.put("artifactId", currentProject.getArtifactId()); ByteArrayOutputStream out = new ByteArrayOutputStream(); p.store(out, "Generated by org.hsc.novelSpider.bundleplugin"); jar.putResource(path + "/pom.properties", new EmbeddedResource(out.toByteArray(), System.currentTimeMillis())); } protected Jar[] getClasspath(MavenProject currentProject) throws IOException, MojoExecutionException { List<Jar> list = new ArrayList<Jar>(); if (getOutputDirectory() != null && getOutputDirectory().exists()) { list.add(new Jar(".", getOutputDirectory())); } final Collection<Artifact> artifacts = getSelectedDependencies(currentProject.getArtifacts()); for (Iterator<Artifact> it = artifacts.iterator(); it.hasNext();) { Artifact artifact = (Artifact) it.next(); if (artifact.getArtifactHandler().isAddedToClasspath()) { if (!Artifact.SCOPE_TEST.equals(artifact.getScope())) { File file = getFile(artifact); if (file == null) { getLog().warn("File is not available for artifact " + artifact + " in project " + currentProject.getArtifact()); continue; } Jar jar = new Jar(artifact.getArtifactId(), file); list.add(jar); } } } Jar[] cp = new Jar[list.size()]; list.toArray(cp); return cp; } private Collection<Artifact> getSelectedDependencies(Collection<Artifact> artifacts) throws MojoExecutionException { if (null == excludeDependencies || excludeDependencies.length() == 0) { return artifacts; } else if ("true".equalsIgnoreCase(excludeDependencies)) { return Collections.EMPTY_LIST; } Collection<Artifact> selectedDependencies = new LinkedHashSet<Artifact>(artifacts); DependencyExcluder excluder = new DependencyExcluder(artifacts); excluder.processHeaders(excludeDependencies); selectedDependencies.removeAll(excluder.getExcludedArtifacts()); return selectedDependencies; } /** * Get the file for an Artifact * * @param artifact */ protected File getFile(Artifact artifact) { return artifact.getFile(); } private static void header(Properties properties, String key, Object value) { if (value == null) return; if (value instanceof Collection && ((Collection<?>) value).isEmpty()) return; properties.put(key, value.toString().replaceAll("[\r\n]", "")); } /** * Convert a Maven version into an OSGi compliant version * * @param version Maven version * @return the OSGi version */ protected String convertVersionToOsgi(String version) { return getMaven2OsgiConverter().getVersion(version); } /** * TODO this should return getMaven2Osgi().getBundleFileName( project.getArtifact() ) */ protected String getBundleName(MavenProject currentProject) { String extension; try { extension = currentProject.getArtifact().getArtifactHandler().getExtension(); } catch (Throwable e) { extension = currentProject.getArtifact().getType(); } if (StringUtils.isEmpty(extension) || "bundle".equals(extension) || "pom".equals(extension)) { extension = "jar"; // just in case maven gets confused } String finalName = currentProject.getBuild().getFinalName(); if (null != classifier && classifier.trim().length() > 0) { return finalName + '-' + classifier + '.' + extension; } return finalName + '.' + extension; } protected String getBuildDirectory() { return buildDirectory; } protected void setBuildDirectory(String _buildirectory) { buildDirectory = _buildirectory; } protected Properties getDefaultProperties(MavenProject currentProject) { Properties properties = new Properties(); String bsn; try { bsn = getMaven2OsgiConverter().getBundleSymbolicName(currentProject.getArtifact()); } catch (Exception e) { bsn = currentProject.getGroupId() + "." + currentProject.getArtifactId(); } // Setup defaults properties.put(MAVEN_SYMBOLICNAME, bsn); properties.put(Analyzer.BUNDLE_SYMBOLICNAME, bsn); properties.put(Analyzer.IMPORT_PACKAGE, "*"); properties.put(Analyzer.BUNDLE_VERSION, getMaven2OsgiConverter().getVersion(currentProject.getVersion())); // remove the extraneous Include-Resource and Private-Package entries from generated manifest properties.put(Constants.REMOVEHEADERS, Analyzer.INCLUDE_RESOURCE + ',' + Analyzer.PRIVATE_PACKAGE); header(properties, Analyzer.BUNDLE_DESCRIPTION, currentProject.getDescription()); StringBuffer licenseText = printLicenses(currentProject.getLicenses()); if (licenseText != null) { header(properties, Analyzer.BUNDLE_LICENSE, licenseText); } header(properties, Analyzer.BUNDLE_NAME, currentProject.getName()); if (currentProject.getOrganization() != null) { if (currentProject.getOrganization().getName() != null) { String organizationName = currentProject.getOrganization().getName(); header(properties, Analyzer.BUNDLE_VENDOR, organizationName); properties.put("project.organization.name", organizationName); properties.put("pom.organization.name", organizationName); } if (currentProject.getOrganization().getUrl() != null) { String organizationUrl = currentProject.getOrganization().getUrl(); header(properties, Analyzer.BUNDLE_DOCURL, organizationUrl); properties.put("project.organization.url", organizationUrl); properties.put("pom.organization.url", organizationUrl); } } properties.putAll(currentProject.getProperties()); properties.putAll(currentProject.getModel().getProperties()); if (m_mavenSession != null) { try { // don't pass upper-case session settings to bnd as they end up in the manifest Properties sessionProperties = m_mavenSession.getExecutionProperties(); for (Enumeration e = sessionProperties.propertyNames(); e.hasMoreElements();) { String key = (String) e.nextElement(); if (key.length() > 0 && !Character.isUpperCase(key.charAt(0))) { properties.put(key, sessionProperties.getProperty(key)); } } } catch (Exception e) { getLog().warn("Problem with Maven session properties: " + e.getLocalizedMessage()); } } properties.putAll(getProperties(currentProject.getModel(), "project.build.")); properties.putAll(getProperties(currentProject.getModel(), "pom.")); properties.putAll(getProperties(currentProject.getModel(), "project.")); properties.put("project.baseDir", getBase(currentProject)); properties.put("project.build.directory", getBuildDirectory()); properties.put("project.build.outputdirectory", getOutputDirectory()); properties.put("classifier", classifier == null ? "" : classifier); getLog().info("BlueprintPlugin"); // Add default plugins header(properties, Analyzer.PLUGIN, SpringXMLType.class.getName()); //header( properties, Analyzer.PLUGIN, BlueprintPlugin.class.getName() + "," + SpringXMLType.class.getName() ); return properties; } protected static File getBase(MavenProject currentProject) { return currentProject.getBasedir() != null ? currentProject.getBasedir() : new File(""); } protected File getOutputDirectory() { return outputDirectory; } protected void setOutputDirectory(File _outputDirectory) { outputDirectory = _outputDirectory; } private void addLocalPackages(File outputDirectory, Analyzer analyzer) { Collection<String> packages = new TreeSet<String>(); if (outputDirectory != null && outputDirectory.isDirectory()) { // scan classes directory for potential packages DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(outputDirectory); scanner.setIncludes(new String[] { "**/*.class" }); scanner.addDefaultExcludes(); scanner.scan(); String[] paths = scanner.getIncludedFiles(); for (int i = 0; i < paths.length; i++) { packages.add(getPackageName(paths[i])); } } StringBuffer exportedPkgs = new StringBuffer(); StringBuffer privatePkgs = new StringBuffer(); boolean noprivatePackages = "!*".equals(analyzer.getProperty(Analyzer.PRIVATE_PACKAGE)); for (Iterator<String> i = packages.iterator(); i.hasNext();) { String pkg = i.next(); // mark all source packages as private by default (can be overridden by export list) if (privatePkgs.length() > 0) { privatePkgs.append(';'); } privatePkgs.append(pkg); // we can't export the default package (".") and we shouldn't export internal packages if (noprivatePackages || !(".".equals(pkg) || pkg.contains(".internal") || pkg.contains(".impl"))) { if (exportedPkgs.length() > 0) { exportedPkgs.append(';'); } exportedPkgs.append(pkg); } } getLog().warn(".EXPORT_PACKAGE:" + analyzer.getProperty(Analyzer.EXPORT_PACKAGE)); getLog().warn(".EXPORT_CONTENTS:" + analyzer.getProperty(Analyzer.EXPORT_CONTENTS)); if (analyzer.getProperty(Analyzer.EXPORT_PACKAGE) == null) { if (analyzer.getProperty(Analyzer.EXPORT_CONTENTS) == null) { // no -exportcontents overriding the exports, so use our computed list analyzer.setProperty(Analyzer.EXPORT_PACKAGE, exportedPkgs + ";-split-package:=merge-first"); } else { // leave Export-Package empty (but non-null) as we have -exportcontents analyzer.setProperty(Analyzer.EXPORT_PACKAGE, ""); } } // else{ // String exported = analyzer.getProperty( Analyzer.EXPORT_PACKAGE ); // if ( exported.indexOf( LOCAL_PACKAGES ) >= 0 ){ // String newExported = StringUtils.replace( exported, LOCAL_PACKAGES, exportedPkgs.toString() ); // analyzer.setProperty( Analyzer.EXPORT_PACKAGE, newExported ); // // } // } String internal = analyzer.getProperty(Analyzer.PRIVATE_PACKAGE); if (internal == null) { if (privatePkgs.length() > 0) { analyzer.setProperty(Analyzer.PRIVATE_PACKAGE, privatePkgs + ";-split-package:=merge-first"); } else { // if there are really no private packages then use "!*" as this will keep the Bnd Tool happy analyzer.setProperty(Analyzer.PRIVATE_PACKAGE, "!*"); } } else if (internal.indexOf(LOCAL_PACKAGES) >= 0) { String newInternal = StringUtils.replace(internal, LOCAL_PACKAGES, privatePkgs.toString()); analyzer.setProperty(Analyzer.PRIVATE_PACKAGE, newInternal); } } private static String getPackageName(String filename) { int n = filename.lastIndexOf(File.separatorChar); return n < 0 ? "." : filename.substring(0, n).replace(File.separatorChar, '.'); } private static List<Resource> getMavenResources(MavenProject currentProject) { List<Resource> resources = new ArrayList<Resource>(currentProject.getResources()); if (currentProject.getCompileSourceRoots() != null) { // also scan for any "packageinfo" files lurking in the source folders List<String> packageInfoIncludes = Collections.singletonList("**/packageinfo"); for (Iterator<String> i = currentProject.getCompileSourceRoots().iterator(); i.hasNext();) { String sourceRoot = i.next(); Resource packageInfoResource = new Resource(); packageInfoResource.setDirectory(sourceRoot); packageInfoResource.setIncludes(packageInfoIncludes); resources.add(packageInfoResource); } } return resources; } protected static String getMavenResourcePaths(MavenProject currentProject) { final String basePath = currentProject.getBasedir().getAbsolutePath(); Set<String> pathSet = new LinkedHashSet<String>(); for (Iterator<Resource> i = getMavenResources(currentProject).iterator(); i.hasNext();) { Resource resource = i.next(); final String sourcePath = resource.getDirectory(); final String targetPath = resource.getTargetPath(); // ignore empty or non-local resources if (new File(sourcePath).exists() && ((targetPath == null) || (targetPath.indexOf("..") < 0))) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(sourcePath); if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) { scanner.setIncludes((String[]) resource.getIncludes().toArray(EMPTY_STRING_ARRAY)); } else { scanner.setIncludes(DEFAULT_INCLUDES); } if (resource.getExcludes() != null && !resource.getExcludes().isEmpty()) { scanner.setExcludes((String[]) resource.getExcludes().toArray(EMPTY_STRING_ARRAY)); } scanner.addDefaultExcludes(); scanner.scan(); List<String> includedFiles = Arrays.asList(scanner.getIncludedFiles()); for (Iterator<String> j = includedFiles.iterator(); j.hasNext();) { String name = j.next(); String path = sourcePath + '/' + name; // make relative to project if (path.startsWith(basePath)) { if (path.length() == basePath.length()) { path = "."; } else { path = path.substring(basePath.length() + 1); } } // replace windows backslash with a slash // this is a workaround for a problem with bnd 0.0.189 if (File.separatorChar != '/') { name = name.replace(File.separatorChar, '/'); path = path.replace(File.separatorChar, '/'); } // copy to correct place path = name + '=' + path; if (targetPath != null) { path = targetPath + '/' + path; } // use Bnd filtering? if (resource.isFiltering()) { path = '{' + path + '}'; } pathSet.add(path); } } } StringBuffer resourcePaths = new StringBuffer(); for (Iterator<String> i = pathSet.iterator(); i.hasNext();) { resourcePaths.append(i.next()); if (i.hasNext()) { resourcePaths.append(','); } } return resourcePaths.toString(); } protected Collection<Artifact> getEmbeddableArtifacts(MavenProject currentProject, Analyzer analyzer) throws MojoExecutionException { final Collection<Artifact> artifacts; String embedTransitive = analyzer.getProperty(DependencyEmbedder.EMBED_TRANSITIVE); if (Boolean.valueOf(embedTransitive).booleanValue()) { // includes transitive dependencies artifacts = currentProject.getArtifacts(); } else { // only includes direct dependencies artifacts = currentProject.getDependencyArtifacts(); } return getSelectedDependencies(artifacts); } protected static void addMavenSourcePath(MavenProject currentProject, Analyzer analyzer, Log log) { // pass maven source paths onto BND analyzer StringBuilder mavenSourcePaths = new StringBuilder(); if (currentProject.getCompileSourceRoots() != null) { for (Iterator<String> i = currentProject.getCompileSourceRoots().iterator(); i.hasNext();) { if (mavenSourcePaths.length() > 0) { mavenSourcePaths.append(','); } mavenSourcePaths.append(i.next()); } } final String sourcePath = (String) analyzer.getProperty(Analyzer.SOURCEPATH); if (sourcePath != null) { if (sourcePath.indexOf(MAVEN_SOURCES) >= 0) { // if there is no maven source path, we do a special treatment and replace // every occurance of MAVEN_SOURCES and a following comma with an empty string if (mavenSourcePaths.length() == 0) { String cleanedSource = removeTagFromInstruction(sourcePath, MAVEN_SOURCES); if (cleanedSource.length() > 0) { analyzer.setProperty(Analyzer.SOURCEPATH, cleanedSource); } else { analyzer.unsetProperty(Analyzer.SOURCEPATH); } } else { String combinedSource = StringUtils.replace(sourcePath, MAVEN_SOURCES, mavenSourcePaths.toString()); analyzer.setProperty(Analyzer.SOURCEPATH, combinedSource); } } else if (mavenSourcePaths.length() > 0) { log.warn(Analyzer.SOURCEPATH + ": overriding " + mavenSourcePaths + " with " + sourcePath + " (add " + MAVEN_SOURCES + " if you want to include the maven sources)"); } } else if (mavenSourcePaths.length() > 0) { analyzer.setProperty(Analyzer.SOURCEPATH, mavenSourcePaths.toString()); } } }