Java tutorial
/* ========================================================================== * Copyright 2003-2007 Mevenide Team * * 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.codehaus.mojo.nbm; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; import java.net.URL; import java.text.BreakIterator; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.regex.Pattern; 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.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.codehaus.mojo.nbm.model.Dependency; import org.codehaus.mojo.nbm.model.NetBeansModule; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.dependency.analyzer.DefaultClassAnalyzer; import org.apache.maven.shared.dependency.analyzer.asm.ASMDependencyAnalyzer; import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; import org.apache.maven.shared.dependency.graph.DependencyNode; import org.apache.tools.ant.taskdefs.Manifest; import org.apache.tools.ant.taskdefs.ManifestException; import org.codehaus.mojo.nbm.utils.ExamineManifest; import org.codehaus.plexus.util.IOUtil; /** * Goal for generating NetBeans module system specific manifest entries, part of <code>nbm</code> lifecycle/packaging. * * In order to have the generated manifest picked up by the maven-jar-plugin, * one shall add the following configuration snippet to maven-jar-plugin. * * <pre> * {@code <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> <configuration> <archive> <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> </archive> </configuration> </plugin> * } * </pre> * * @author <a href="mailto:mkleint@codehaus.org">Milos Kleint</a> */ @Mojo(name = "manifest", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresProject = true, threadSafe = true, requiresDependencyResolution = ResolutionScope.RUNTIME) public class NetBeansManifestUpdateMojo extends AbstractNbmMojo { /** * NetBeans module assembly build directory. * directory where the the NetBeans jar and nbm file get constructed. */ @Parameter(defaultValue = "${project.build.directory}/nbm", property = "maven.nbm.buildDir") protected File nbmBuildDir; /** * a NetBeans module descriptor containing dependency information and more * @deprecated all content from the module descriptor can be defined as plugin configuration now, will be removed in 4.0 entirely */ @Parameter(defaultValue = "${basedir}/src/main/nbm/module.xml") protected File descriptor; /** * maven project */ @Parameter(required = true, readonly = true, property = "project") private MavenProject project; /** * The location of JavaHelp sources for the project. The documentation * itself is expected to be in the directory structure based on codenamebase of the module. * eg. if your codenamebase is "org.netbeans.modules.apisupport", then the actual docs * files shall go to ${basedir}/src/main/javahelp/org/netbeans/modules/apisupport/docs. * Obsolete as of NetBeans 7.0 with @HelpSetRegistration. * @since 2.7 */ @Parameter(defaultValue = "${basedir}/src/main/javahelp") protected File nbmJavahelpSource; /** * Path to manifest file that will be used as base and enhanced with generated content. Any entry specified in the original file * will not be overwritten * @since 3.0 */ @Parameter(required = true, defaultValue = "${basedir}/src/main/nbm/manifest.mf") private File sourceManifestFile; /** * Path to the generated MANIFEST file to use. It will be used by jar:jar plugin. * @since 3.0 */ @Parameter(required = true, readonly = true, defaultValue = "${project.build.outputDirectory}/META-INF/MANIFEST.MF") private File targetManifestFile; /** * Verify the runtime NetBeans module dependencies and Class-Path items * generated from Maven dependencies. The check is done by matching classes used * in current project. Allowed values for the parameter are <code>fail</code>, <code>warn</code> and <code>skip</code>. * The default is <code>fail</code> in which case the validation failure results in a failed build, * in the vast majority of cases the module would fail at runtime anyway. * * @since 3.0 */ @Parameter(property = "maven.nbm.verify", defaultValue = "fail") private String verifyRuntime; private static final String FAIL = "fail"; private static final String WARN = "warn"; private static final String SKIP = "skip"; /** * A list of module's public packages. If not defined, no packages are exported as public. * Allowed values are single package names * or package names ending with .* which represent the package and all subpackages. * <p/> * Eg. "org.kleint.milos.api" designates just the one package, while "org.kleint.milos.spi.*" * denotes the spi package an all it's subpackages. * @since 3.0 */ @Parameter private List<String> publicPackages; /** * When encountering an OSGi bundle among dependencies, the plugin will generate a direct dependency * on the bundle and will not include the bundle's jar into the nbm. Will only work with NetBeans 6.9+ runtime. * Therefore it is off by default. * WARNING: Additionally existing applications/modules need to check modules wrapping * external libraries for library jars that are also OSGi bundles. Such modules will no longer include the OSGi bundles * as part of the module but will include a modular dependency on the bundle. Modules depending on these old wrappers * shall depend directly on the bundle, eventually rendering the old library wrapper module obsolete. * * @since 3.2 */ @Parameter(defaultValue = "false") private boolean useOSGiDependencies; /** * codename base of the module, uniquely identifying the module within the NetBeans runtime. usually the package name equivalent. * Can include the major release version. * See <a href="http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#how-manifest"> NetBeans Module system docs</a> * @since 3.8 */ @Parameter(defaultValue = "${project.groupId}.${project.artifactId}") private String codeNameBase; /** * List of explicit module dependency declarations overriding the default specification dependency. Useful when depending on a range of major versions, * depending on implementation version etc. * <p>The format is: * <pre> * <dependency> * <id>groupId:artifactId</id> * <type>spec|impl|loose</type> * <explicitValue>the entire dependency token</explicitValue> * </dependency> * </pre> * </p> * <p> * where <code>id</code> is composed of grouId and artifactId of a dependency defined in effective pom, separated by double colon. This is mandatory.</p> * <p> * Then there are 2 exclusively optional fields <code>type</code> and <code>explicitValue</code>, if both are defined <code>explicitValue</code> gets applied. * </p> * <p><code>type</code> values: <code>spec</code> means specification dependency.That's the default. * <code>impl</code> means implementation dependency, only the exact version match will satisfy the constraint. * <code>loose</code> means loose dependency, no requirement on version, the module just has to be present. Not very common option. * * @since 3.8 */ @Parameter private Dependency[] moduleDependencies; /** * Deployment type of the module, allowed values are <code>normal</code>,<code>eager</code>,<code>autoload</code>, * <code>disabled</code>. * <p> * <code>autoload</code> - Such a module is * automatically enabled when some other module requires it and * automatically disabled otherwise.</p> * <p><code>eager</code> - This module type gets * automatically enabled when all it's dependencies are * satisfied. Disabled otherwise.</p> * <p><code>normal</code> - This is the default * value. This kind of module is enabled/disabled manually by * the user. It installs enabled.</p> * <p><code>disabled</code> - This kind of module is enabled/disabled manually by * the user. It installs disabled. Since 3.11</p> * * For details, see <a href="http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#enablement">Netbeans Module system docs</a> * * Since 3.14, for autoload and eager modules, we automatically set AutoUpdate-Show-In-Client manifest entry to false, if not defined already otherwise in the manifest. * See issue <a href="http://jira.codehaus.org/browse/MNBMODULE-194">MNBMODULE-194</a> * * @since 3.8 (3.14 in manifest goal) */ @Parameter(defaultValue = "normal") protected String moduleType; // <editor-fold defaultstate="collapsed" desc="Component parameters"> /** * The artifact repository to use. */ @Parameter(required = true, readonly = true, defaultValue = "${localRepository}") private ArtifactRepository localRepository; /** * The artifact factory to use. */ @Component private ArtifactFactory artifactFactory; /** * The artifact metadata source to use. */ @Component private ArtifactMetadataSource artifactMetadataSource; /** * The artifact collector to use. */ @Component private ArtifactCollector artifactCollector; /** * The dependency tree builder to use. */ @Component(hint = "default") private DependencyGraphBuilder dependencyGraphBuilder; // end of component params custom code folding // </editor-fold> /** * execute plugin * @throws MojoExecutionException if an unexpected problem occurs * @throws MojoFailureException if an expected problem occurs */ public void execute() throws MojoExecutionException, MojoFailureException { //need to do this to chekc for javahelp on CP. super.registerNbmAntTasks(); NetBeansModule module; if (descriptor != null && descriptor.exists()) { module = readModuleDescriptor(descriptor); getLog().warn("descriptor parameter is deprecated, use equivalent mojo parameters instead."); } else { module = createDefaultDescriptor(project, false); } String mtype = moduleType; //same moduleType related code in CreateNetBeansFileStructure.java if ("normal".equals(mtype) && module.getModuleType() != null) { mtype = module.getModuleType(); getLog().warn("moduleType in module descriptor is deprecated, use the plugin's parameter moduleType"); } if (!"normal".equals(mtype) && !"autoload".equals(mtype) && !"eager".equals(mtype) && !"disabled".equals(mtype)) { getLog().error("Only 'normal,autoload,eager,disabled' are allowed values in the moduleType parameter"); } boolean autoload = "autoload".equals(mtype); boolean eager = "eager".equals(mtype); String moduleName = codeNameBase; if (module.getCodeNameBase() != null) { moduleName = module.getCodeNameBase(); getLog().warn( "codeNameBase in module descriptor is deprecated, use the plugin's parameter codeNameBase"); } moduleName = moduleName.replaceAll("-", "."); //<!-- if a NetBeans specific manifest is defined, examine this one, otherwise the already included one. // ignoring the case when some of the NetBeans attributes are already defined in the jar and more is included. File specialManifest = sourceManifestFile; File nbmManifest = (module.getManifest() != null ? new File(project.getBasedir(), module.getManifest()) : null); if (nbmManifest != null && nbmManifest.exists()) { //deprecated, but if actually defined, will use it. specialManifest = nbmManifest; } ExamineManifest examinator = new ExamineManifest(getLog()); if (specialManifest != null && specialManifest.exists()) { examinator.setManifestFile(specialManifest); examinator.checkFile(); } else { // examinator.setJarFile( jarFile ); } getLog().info("NBM Plugin generates manifest"); Manifest manifest = null; if (specialManifest != null && specialManifest.exists()) { Reader reader = null; try { reader = new InputStreamReader(new FileInputStream(specialManifest)); manifest = new Manifest(reader); } catch (IOException exc) { manifest = new Manifest(); getLog().warn("Error reading manifest at " + specialManifest, exc); } catch (ManifestException ex) { getLog().warn("Error reading manifest at " + specialManifest, ex); manifest = new Manifest(); } finally { IOUtil.close(reader); } } else { manifest = new Manifest(); } Date date = new Date(); String specVersion = AdaptNbVersion.adaptVersion(project.getVersion(), AdaptNbVersion.TYPE_SPECIFICATION, date); String implVersion = AdaptNbVersion.adaptVersion(project.getVersion(), AdaptNbVersion.TYPE_IMPLEMENTATION, date); Manifest.Section mainSection = manifest.getMainSection(); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Specification-Version", specVersion); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Implementation-Version", implVersion); if (autoload || eager) { //MNBMODULE-194 conditionallyAddAttribute(mainSection, "AutoUpdate-Show-In-Client", "false"); } final String timestamp = createTimestamp(date); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Build-Version", timestamp); String projectCNB = conditionallyAddAttribute(mainSection, "OpenIDE-Module", moduleName); String packagesValue; if (publicPackages != null && publicPackages.size() > 0) { StringBuilder sb = new StringBuilder(); for (String pub : publicPackages) { if (pub == null) { //#MNBMODULE-237 continue; } if (pub.endsWith(".**")) { // well, just sort of wrong value but accept sb.append(pub); } else if (pub.endsWith(".*")) { //multipackage value sb.append(pub).append("*"); } else { sb.append(pub).append(".*"); } sb.append(", "); } if (sb.length() > 1) { //if only item is null, we have empty builder sb.setLength(sb.length() - 2); //cut the last 2 ", " characters packagesValue = sb.toString(); } else { // no packages available packagesValue = "-"; } } else { // no packages available packagesValue = "-"; } conditionallyAddAttribute(mainSection, "OpenIDE-Module-Public-Packages", packagesValue); //See http://www.netbeans.org/download/dev/javadoc/org-openide-modules/apichanges.html#split-of-openide-jar conditionallyAddAttribute(mainSection, "OpenIDE-Module-Requires", "org.openide.modules.ModuleFormat1"); // conditionallyAddAttribute(mainSection, "OpenIDE-Module-IDE-Dependencies", "IDE/1 > 3.40"); // localization items if (!examinator.isLocalized()) { conditionallyAddAttribute(mainSection, "OpenIDE-Module-Display-Category", project.getGroupId()); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Name", project.getName()); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Short-Description", shorten(project.getDescription())); conditionallyAddAttribute(mainSection, "OpenIDE-Module-Long-Description", project.getDescription()); } getLog().debug("module =" + module); DependencyNode treeroot = createDependencyTree(project, dependencyGraphBuilder, "compile"); Map<Artifact, ExamineManifest> examinerCache = new HashMap<Artifact, ExamineManifest>(); @SuppressWarnings("unchecked") List<Artifact> libArtifacts = getLibraryArtifacts(treeroot, module, project.getRuntimeArtifacts(), examinerCache, getLog(), useOSGiDependencies); List<ModuleWrapper> moduleArtifacts = getModuleDependencyArtifacts(treeroot, module, moduleDependencies, project, examinerCache, libArtifacts, getLog(), useOSGiDependencies); StringBuilder classPath = new StringBuilder(); StringBuilder mavenClassPath = new StringBuilder(); String dependencies = ""; String depSeparator = " "; for (Artifact a : libArtifacts) { if (classPath.length() > 0) { classPath.append(' '); } classPath.append(artifactToClassPathEntry(a, codeNameBase)); if (mavenClassPath.length() > 0) { mavenClassPath.append(' '); } mavenClassPath.append(a.getGroupId()).append(':').append(a.getArtifactId()).append(':') .append(a.getBaseVersion()); if (a.getClassifier() != null) { mavenClassPath.append(":").append(a.getClassifier()); } } for (ModuleWrapper wr : moduleArtifacts) { if (wr.transitive) { continue; } Dependency dep = wr.dependency; Artifact artifact = wr.artifact; ExamineManifest depExaminator = examinerCache.get(artifact); String type = dep.getType(); String depToken = dep.getExplicitValue(); if (depToken == null) { if ("loose".equals(type)) { depToken = depExaminator.getModuleWithRelease(); } else if ("spec".equals(type)) { depToken = depExaminator.getModuleWithRelease() + " > " + (depExaminator.isNetBeansModule() ? depExaminator.getSpecVersion() : AdaptNbVersion.adaptVersion(depExaminator.getSpecVersion(), AdaptNbVersion.TYPE_SPECIFICATION, date)); } else if ("impl".equals(type)) { depToken = depExaminator.getModuleWithRelease() + " = " + (depExaminator.isNetBeansModule() ? depExaminator.getImplVersion() : AdaptNbVersion.adaptVersion(depExaminator.getImplVersion(), AdaptNbVersion.TYPE_IMPLEMENTATION, date)); } else { throw new MojoExecutionException("Wrong type of NetBeans dependency: " + type + " Allowed values are: loose, spec, impl."); } } if (depToken == null) { //TODO report getLog().error("Cannot properly resolve the NetBeans dependency for " + dep.getId()); } else { dependencies = dependencies + depSeparator + depToken; depSeparator = ", "; } } if (!verifyRuntime.equalsIgnoreCase(SKIP)) { try { checkModuleClassPath(treeroot, libArtifacts, examinerCache, moduleArtifacts, projectCNB); } catch (IOException ex) { throw new MojoExecutionException("Error while checking runtime dependencies", ex); } } if (nbmJavahelpSource.exists()) { String moduleJarName = stripVersionFromCodebaseName(moduleName).replace(".", "-"); classPath.append(" docs/").append(moduleJarName).append(".jar"); } if (classPath.length() > 0) { conditionallyAddAttribute(mainSection, "X-Class-Path", classPath.toString().trim()); } if (mavenClassPath.length() > 0) { conditionallyAddAttribute(mainSection, "Maven-Class-Path", mavenClassPath.toString()); } if (dependencies.length() > 0) { conditionallyAddAttribute(mainSection, "OpenIDE-Module-Module-Dependencies", dependencies); } // if ( librList.size() > 0 ) // { // String list = ""; // for ( int i = 0; i < librList.size(); i++ ) // { // list = list + " " + librList.get( i ); // } // getLog().warn( // "Some libraries could not be found in the dependency chain: " + list ); // } PrintWriter writer = null; try { if (!targetManifestFile.exists()) { targetManifestFile.getParentFile().mkdirs(); targetManifestFile.createNewFile(); } writer = new PrintWriter(targetManifestFile, "UTF-8"); //TODO really UTF-8?? manifest.write(writer); } catch (IOException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } finally { IOUtil.close(writer); } } //MNBMODULE-137 static String artifactToClassPathEntry(Artifact a, String codenamebase) { return "ext/" + codenamebase + "/" + a.getGroupId().replace('.', '-') + "/" + a.getArtifactId() + (a.getClassifier() != null ? "-" + a.getClassifier() : "") + "." + a.getArtifactHandler().getExtension(); } /** * Create a timestamp for <code>OpenIDE-Module-Build-Version</code> manifest * entry. * * It's created from the current time and formatted using a UTC timezone * explicitly which makes created timestamp timezone-independent. * * @return timestamp represented as <code>201012292045</code> */ private static String createTimestamp(Date date) { final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmm"); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); final String timestamp = dateFormat.format(date); return timestamp; } static String stripVersionFromCodebaseName(String cnb) { // it can happen the moduleName is in format org.milos/1 String base = cnb; int index = base.indexOf('/'); if (index > -1) { base = base.substring(0, index).trim(); } return base; } String conditionallyAddAttribute(Manifest.Section section, String key, String value) { Manifest.Attribute attr = section.getAttribute(key); if (attr == null) { attr = new Manifest.Attribute(); attr.setName(key); attr.setValue(value != null ? value.replaceAll("\\s+", " ").trim() : "<undefined>"); try { section.addConfiguredAttribute(attr); } catch (ManifestException ex) { getLog().error("Cannot update manifest (key=" + key + ")"); ex.printStackTrace(); } } return attr.getValue(); } /** * Pick out the first sentence of a paragraph. * @param paragraph some text (may be null) * @return the first sentence (may be null) */ static String shorten(String paragraph) { if (paragraph == null || paragraph.length() == 0) { return null; } BreakIterator breaker = BreakIterator.getSentenceInstance(); breaker.setText(paragraph); return paragraph.substring(0, breaker.following(0)).trim(); } //---------------------------------------------------------------------------------- // classpat checking related. //---------------------------------------------------------------------------------- private void checkModuleClassPath(DependencyNode treeroot, List<Artifact> libArtifacts, Map<Artifact, ExamineManifest> examinerCache, List<ModuleWrapper> moduleArtifacts, String projectCodeNameBase) throws IOException, MojoExecutionException, MojoFailureException { Set<String> deps = buildProjectDependencyClasses(project, libArtifacts); deps.retainAll(allProjectClasses(project)); Set<String> own = projectModuleOwnClasses(project, libArtifacts); deps.removeAll(own); CollectModuleLibrariesNodeVisitor visitor = new CollectModuleLibrariesNodeVisitor( project.getRuntimeArtifacts(), examinerCache, getLog(), treeroot, useOSGiDependencies); treeroot.accept(visitor); Map<String, List<Artifact>> modules = visitor.getDeclaredArtifacts(); Map<Artifact, Set<String>> moduleAllClasses = new HashMap<Artifact, Set<String>>(); for (ModuleWrapper wr : moduleArtifacts) { if (modules.containsKey(wr.artifact.getDependencyConflictId())) { ExamineManifest man = examinerCache.get(wr.artifact); List<Artifact> arts = modules.get(wr.artifact.getDependencyConflictId()); Set<String>[] classes = visibleModuleClasses(arts, man, wr.dependency, projectCodeNameBase, false); deps.removeAll(classes[0]); moduleAllClasses.put(wr.artifact, classes[1]); } } //now we have the classes that are not in public packages of declared modules, //but are being used if (!deps.isEmpty()) { Map<String, List<Artifact>> transmodules = visitor.getTransitiveArtifacts(); for (ModuleWrapper wr : moduleArtifacts) { if (transmodules.containsKey(wr.artifact.getDependencyConflictId())) { ExamineManifest man = examinerCache.get(wr.artifact); List<Artifact> arts = transmodules.get(wr.artifact.getDependencyConflictId()); Set<String>[] classes = visibleModuleClasses(arts, man, wr.dependency, projectCodeNameBase, true); classes[0].retainAll(deps); if (classes[0].size() > 0) { String module = wr.osgi ? "OSGi bundle" : "module"; getLog().error("Project uses classes from transitive " + module + " " + wr.artifact.getId() + " which will not be accessible at runtime."); getLog().info( " To fix the problem, add this module as direct dependency. For OSGi bundles that are supposed to be wrapped in NetBeans modules, use the useOSGiDependencies=false parameter"); deps.removeAll(classes[0]); } classes[1].retainAll(deps); if (classes[1].size() > 0) { getLog().info("Private classes referenced in transitive module: " + Arrays.toString(classes[1].toArray())); getLog().error("Project depends on packages not accessible at runtime in transitive module " + wr.artifact.getId() + " which will not be accessible at runtime."); deps.removeAll(classes[1]); } } } for (Map.Entry<Artifact, Set<String>> e : moduleAllClasses.entrySet()) { List<String> strs = new ArrayList<String>(deps); if (deps.removeAll(e.getValue())) { strs.retainAll(e.getValue()); getLog().info("Private classes referenced in module: " + Arrays.toString(strs.toArray())); getLog().error("Project depends on packages not accessible at runtime in module " + e.getKey().getId()); } } if (verifyRuntime.equalsIgnoreCase(FAIL)) { if (!deps.isEmpty()) { throw new MojoFailureException( "Uncategorized problems with NetBeans dependency verification (maybe MNBMODULE-102 or wrong maven dependency metadata). Supposedly external classes are used in the project's binaries but the classes are not found on classpath. Class usages: " + deps); } else { throw new MojoFailureException( "See above for failures in runtime NetBeans dependencies verification."); } } } } /** * The current projects's dependencies, includes classes used in teh module itself * and the classpath libraries as well. * @param project * @param libraries * @return * @throws java.io.IOException */ private Set<String> buildProjectDependencyClasses(MavenProject project, List<Artifact> libraries) throws IOException { Set<String> dependencyClasses = new HashSet<String>(); String outputDirectory = project.getBuild().getOutputDirectory(); dependencyClasses.addAll(buildDependencyClasses(outputDirectory)); for (Artifact lib : libraries) { dependencyClasses.addAll(buildDependencyClasses(lib.getFile().getAbsolutePath())); } return dependencyClasses; } @SuppressWarnings("unchecked") private Set<String> projectModuleOwnClasses(MavenProject project, List<Artifact> libraries) throws IOException { Set<String> projectClasses = new HashSet<String>(); DefaultClassAnalyzer analyzer = new DefaultClassAnalyzer(); String outputDirectory = project.getBuild().getOutputDirectory(); URL fl = new File(outputDirectory).toURI().toURL(); projectClasses.addAll(analyzer.analyze(fl)); for (Artifact lib : libraries) { URL url = lib.getFile().toURI().toURL(); projectClasses.addAll(analyzer.analyze(url)); } return projectClasses; } /** * complete list of classes on project runtime classpath (excluding * jdk bit) * @param project * @return * @throws java.io.IOException */ @SuppressWarnings("unchecked") private Set<String> allProjectClasses(MavenProject project) throws IOException { Set<String> projectClasses = new HashSet<String>(); DefaultClassAnalyzer analyzer = new DefaultClassAnalyzer(); String outputDirectory = project.getBuild().getOutputDirectory(); URL fl = new File(outputDirectory).toURI().toURL(); projectClasses.addAll(analyzer.analyze(fl)); List<Artifact> libs = project.getRuntimeArtifacts(); for (Artifact lib : libs) { URL url = lib.getFile().toURI().toURL(); projectClasses.addAll(analyzer.analyze(url)); } return projectClasses; } private Set<String>[] visibleModuleClasses(List<Artifact> moduleLibraries, ExamineManifest manifest, Dependency dep, String projectCodeNameBase, boolean transitive) throws IOException, MojoFailureException { Set<String> moduleClasses = new HashSet<String>(); Set<String> visibleModuleClasses = new HashSet<String>(); DefaultClassAnalyzer analyzer = new DefaultClassAnalyzer(); String type = dep.getType(); if (dep.getExplicitValue() != null) { if (dep.getExplicitValue().contains("=")) { type = "impl"; } } if (type == null || "loose".equals(type)) { type = "spec"; } for (Artifact lib : moduleLibraries) { URL url = lib.getFile().toURI().toURL(); moduleClasses.addAll(analyzer.analyze(url)); } if ("spec".equals(type)) { String cnb = stripVersionFromCodebaseName(projectCodeNameBase); if (!transitive && manifest.hasFriendPackages() && !manifest.getFriends().contains(cnb)) { String message = "Module has friend dependency on " + manifest.getModule() + " but is not listed as a friend."; if (verifyRuntime.equalsIgnoreCase(FAIL)) { throw new MojoFailureException(message); } else { getLog().warn(message); } } List<Pattern> compiled = createCompiledPatternList(manifest.getPackages()); if (useOSGiDependencies && manifest.isOsgiBundle()) { // TODO how to extract the public packages in osgi bundles easily.. compiled = Collections.singletonList(Pattern.compile("(.+)")); } for (String clazz : moduleClasses) { for (Pattern patt : compiled) { if (patt.matcher(clazz).matches()) { visibleModuleClasses.add(clazz); break; } } } } else if ("impl".equals(type)) { visibleModuleClasses.addAll(moduleClasses); } else { //HUH? throw new MojoFailureException("Wrong type of module dependency " + type); } return new Set[] { visibleModuleClasses, moduleClasses }; } static List<Pattern> createCompiledPatternList(List<String> packages) { List<Pattern> toRet = new ArrayList<Pattern>(); for (String token : packages) { if (token.endsWith(".**")) { String patt = "^" + Pattern.quote(token.substring(0, token.length() - 2)) + "(.+)"; toRet.add(0, Pattern.compile(patt)); } else { String patt = "^" + Pattern.quote(token.substring(0, token.length() - 1)) + "([^\\.]+)"; toRet.add(Pattern.compile(patt)); } } return toRet; } @SuppressWarnings("unchecked") private Set<String> buildDependencyClasses(String path) throws IOException { URL url = new File(path).toURI().toURL(); ASMDependencyAnalyzer dependencyAnalyzer = new ASMDependencyAnalyzer(); return dependencyAnalyzer.analyze(url); } }