diff options
author | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2008-11-19 11:40:41 +0000 |
---|---|---|
committer | antelder <antelder@13f79535-47bb-0310-9956-ffa450edef68> | 2008-11-19 11:40:41 +0000 |
commit | 3c490b44be2231ce7f4d625aa6d01e40b3606510 (patch) | |
tree | 87a7f4f232b3ad28bd221e94557bb215dfdf3376 | |
parent | d417aa104869ba1fa9861e236919a407c2bfe506 (diff) |
Copy bundle plugin from sca-equinox branch to 2.0 trunk as it appears to be required to build
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@718934 13f79535-47bb-0310-9956-ffa450edef68
13 files changed, 5039 insertions, 0 deletions
diff --git a/java/sca/tools/maven/maven-bundle-plugin/pom.xml b/java/sca/tools/maven/maven-bundle-plugin/pom.xml new file mode 100644 index 0000000000..9b6b02d47a --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/pom.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-sca-maven-tools</artifactId> + <version>2.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>tuscany-maven-bundle-plugin</artifactId> + <packaging>maven-plugin</packaging> + <name>Apache Tuscany SCA Maven OSGi Bundle Plugin</name> + + <dependencies> + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-plugin-api</artifactId> + <version>2.0.8</version> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-project</artifactId> + <version>2.0.8</version> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-settings</artifactId> + <version>2.0.8</version> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-artifact</artifactId> + <version>2.0.8</version> + </dependency> + + <dependency> + <groupId>org.apache.maven</groupId> + <artifactId>maven-model</artifactId> + <version>2.0.8</version> + </dependency> + + <dependency> + <groupId>org.apache.maven.shared</groupId> + <artifactId>maven-dependency-tree</artifactId> + <version>1.1</version> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>1.4.3</version> + </dependency> + + <dependency> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.5.1</version> + </dependency> + </dependencies> +</project> diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/AbstractIdeSupportMojo.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/AbstractIdeSupportMojo.java new file mode 100644 index 0000000000..d0961ccd9c --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/AbstractIdeSupportMojo.java @@ -0,0 +1,1114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +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.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipFile; + +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.artifact.resolver.ArtifactNotFoundException; +import org.apache.maven.artifact.resolver.ArtifactResolutionException; +import org.apache.maven.artifact.resolver.ArtifactResolutionResult; +import org.apache.maven.artifact.resolver.ArtifactResolver; +import org.apache.maven.artifact.resolver.DebugResolutionListener; +import org.apache.maven.artifact.resolver.ResolutionNode; +import org.apache.maven.artifact.resolver.WarningResolutionListener; +import org.apache.maven.artifact.resolver.filter.ArtifactFilter; +import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.VersionRange; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.Exclusion; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.eclipse.Constants; +import org.apache.maven.plugin.ide.IdeDependency; +import org.apache.maven.plugin.ide.IdeUtils; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.logging.LogEnabled; +import org.codehaus.plexus.logging.Logger; +import org.codehaus.plexus.util.IOUtil; + +/** + * Abstract base plugin which takes care of the common stuff usually needed by maven IDE plugins. A plugin extending + * AbstractIdeSupportMojo should implement the <code>setup()</code> and <code>writeConfiguration()</code> methods, + * plus the getters needed to get the various configuration flags and required components. The lifecycle: + * + * <pre> + * *** calls setup() where you can configure your specific stuff and stop the mojo from execute if appropriate *** + * - manually resolve project dependencies, NOT failing if a dependency is missing + * - compute project references (reactor projects) if the getUseProjectReferences() flag is set + * - download sources/javadocs if the getDownloadSources() flag is set + * *** calls writeConfiguration(), passing the list of resolved referenced dependencies *** + * - report the list of missing sources or just tell how to turn this feature on if the flag was disabled + * </pre> + * + * @author Fabrizio Giustina + * @version $Id: AbstractIdeSupportMojo.java 628794 2008-02-18 16:09:11Z aheritier $ + */ +public abstract class AbstractIdeSupportMojo + extends AbstractMojo + implements LogEnabled +{ + /** + * Is it an PDE project? If yes, the plugin adds the necessary natures and build commands to the .project file. + * Additionally it copies all libraries to a project local directory and references them instead of referencing the + * files in the local Maven repository. It also ensured that the "Bundle-Classpath" in META-INF/MANIFEST.MF is + * synchronized. + * + * @parameter expression="${eclipse.pde}" default-value="true" + */ + protected boolean pde; + + /** + * The project whose project files to create. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + protected MavenProject project; + + // [rfeng] Change it to use the current project + /** + * The currently executed project (can be a reactor project). + * + * @parameter expression="${project}" + * @readonly + */ + protected MavenProject executedProject; + + /** + * The project packaging. + * + * @parameter expression="${project.packaging}" + */ + protected String packaging; + + /** + * Artifact factory, needed to download source jars for inclusion in classpath. + * + * @component role="org.apache.maven.artifact.factory.ArtifactFactory" + * @required + * @readonly + */ + protected ArtifactFactory artifactFactory; + + /** + * Artifact resolver, needed to download source jars for inclusion in classpath. + * + * @component role="org.apache.maven.artifact.resolver.ArtifactResolver" + * @required + * @readonly + */ + protected ArtifactResolver artifactResolver; + + /** + * Artifact collector, needed to resolve dependencies. + * + * @component role="org.apache.maven.artifact.resolver.ArtifactCollector" + * @required + * @readonly + */ + protected ArtifactCollector artifactCollector; + + /** + * @component role="org.apache.maven.artifact.metadata.ArtifactMetadataSource" hint="maven" + */ + protected ArtifactMetadataSource artifactMetadataSource; + + /** + * Remote repositories which will be searched for source attachments. + * + * @parameter expression="${project.remoteArtifactRepositories}" + * @required + * @readonly + */ + protected List remoteArtifactRepositories; + + /** + * Local maven repository. + * + * @parameter expression="${localRepository}" + * @required + * @readonly + */ + protected ArtifactRepository localRepository; + + /** + * If the executed project is a reactor project, this will contains the full list of projects in the reactor. + * + * @parameter expression="${reactorProjects}" + * @required + * @readonly + */ + protected List reactorProjects; + + /** + * Skip the operation when true. + * + * @parameter expression="${eclipse.skip}" default-value="false" + */ + private boolean skip; + + /** + * Enables/disables the downloading of source attachments. Defaults to false. When this flag is <code>true</code> + * remote repositories are checked for sources: in order to avoid repeated check for unavailable source archives, a + * status cache is mantained into the target dir of the root project. Run <code>mvn:clean</code> or delete the + * file <code>mvn-eclipse-cache.properties</code> in order to reset this cache. + * + * @parameter expression="${downloadSources}" + */ + protected boolean downloadSources; + + /** + * Enables/disables the downloading of javadoc attachments. Defaults to false. When this flag is <code>true</code> + * remote repositories are checked for javadocs: in order to avoid repeated check for unavailable javadoc archives, + * a status cache is mantained into the target dir of the root project. Run <code>mvn:clean</code> or delete the + * file <code>mvn-eclipse-cache.properties</code> in order to reset this cache. + * + * @parameter expression="${downloadJavadocs}" + */ + protected boolean downloadJavadocs; + + /** + * Plexus logger needed for debugging manual artifact resolution. + */ + protected Logger logger; + + /** + * Getter for <code>artifactMetadataSource</code>. + * + * @return Returns the artifactMetadataSource. + */ + public ArtifactMetadataSource getArtifactMetadataSource() + { + return artifactMetadataSource; + } + + /** + * Setter for <code>artifactMetadataSource</code>. + * + * @param artifactMetadataSource The artifactMetadataSource to set. + */ + public void setArtifactMetadataSource( ArtifactMetadataSource artifactMetadataSource ) + { + this.artifactMetadataSource = artifactMetadataSource; + } + + /** + * Getter for <code>project</code>. + * + * @return Returns the project. + */ + public MavenProject getProject() + { + return project; + } + + /** + * Setter for <code>project</code>. + * + * @param project The project to set. + */ + public void setProject( MavenProject project ) + { + this.project = project; + } + + /** + * Getter for <code>reactorProjects</code>. + * + * @return Returns the reactorProjects. + */ + public List getReactorProjects() + { + return reactorProjects; + } + + /** + * Setter for <code>reactorProjects</code>. + * + * @param reactorProjects The reactorProjects to set. + */ + public void setReactorProjects( List reactorProjects ) + { + this.reactorProjects = reactorProjects; + } + + /** + * Getter for <code>remoteArtifactRepositories</code>. + * + * @return Returns the remoteArtifactRepositories. + */ + public List getRemoteArtifactRepositories() + { + return remoteArtifactRepositories; + } + + /** + * Setter for <code>remoteArtifactRepositories</code>. + * + * @param remoteArtifactRepositories The remoteArtifactRepositories to set. + */ + public void setRemoteArtifactRepositories( List remoteArtifactRepositories ) + { + this.remoteArtifactRepositories = remoteArtifactRepositories; + } + + /** + * Getter for <code>artifactFactory</code>. + * + * @return Returns the artifactFactory. + */ + public ArtifactFactory getArtifactFactory() + { + return artifactFactory; + } + + /** + * Setter for <code>artifactFactory</code>. + * + * @param artifactFactory The artifactFactory to set. + */ + public void setArtifactFactory( ArtifactFactory artifactFactory ) + { + this.artifactFactory = artifactFactory; + } + + /** + * Getter for <code>artifactResolver</code>. + * + * @return Returns the artifactResolver. + */ + public ArtifactResolver getArtifactResolver() + { + return artifactResolver; + } + + /** + * Setter for <code>artifactResolver</code>. + * + * @param artifactResolver The artifactResolver to set. + */ + public void setArtifactResolver( ArtifactResolver artifactResolver ) + { + this.artifactResolver = artifactResolver; + } + + /** + * Getter for <code>executedProject</code>. + * + * @return Returns the executedProject. + */ + public MavenProject getExecutedProject() + { + return executedProject; + } + + /** + * Setter for <code>executedProject</code>. + * + * @param executedProject The executedProject to set. + */ + public void setExecutedProject( MavenProject executedProject ) + { + this.executedProject = executedProject; + } + + /** + * Getter for <code>localRepository</code>. + * + * @return Returns the localRepository. + */ + public ArtifactRepository getLocalRepository() + { + return localRepository; + } + + /** + * Setter for <code>localRepository</code>. + * + * @param localRepository The localRepository to set. + */ + public void setLocalRepository( ArtifactRepository localRepository ) + { + this.localRepository = localRepository; + } + + /** + * Getter for <code>downloadJavadocs</code>. + * + * @return Returns the downloadJavadocs. + */ + public boolean getDownloadJavadocs() + { + return downloadJavadocs; + } + + /** + * Setter for <code>downloadJavadocs</code>. + * + * @param downloadJavadocs The downloadJavadocs to set. + */ + public void setDownloadJavadocs( boolean downloadJavadoc ) + { + downloadJavadocs = downloadJavadoc; + } + + /** + * Getter for <code>downloadSources</code>. + * + * @return Returns the downloadSources. + */ + public boolean getDownloadSources() + { + return downloadSources; + } + + /** + * Setter for <code>downloadSources</code>. + * + * @param downloadSources The downloadSources to set. + */ + public void setDownloadSources( boolean downloadSources ) + { + this.downloadSources = downloadSources; + } + + protected void setResolveDependencies( boolean resolveDependencies ) + { + this.resolveDependencies = resolveDependencies; + } + + protected boolean isResolveDependencies() + { + return resolveDependencies; + } + + /** + * return <code>false</code> if projects available in a reactor build should be considered normal dependencies, + * <code>true</code> if referenced project will be linked and not need artifact resolution. + * + * @return <code>true</code> if referenced project will be linked and not need artifact resolution + */ + protected abstract boolean getUseProjectReferences(); + + /** + * Hook for preparation steps before the actual plugin execution. + * + * @return <code>true</code> if execution should continue or <code>false</code> if not. + * @throws MojoExecutionException generic mojo exception + */ + protected abstract boolean setup() + throws MojoExecutionException; + + /** + * Main plugin method where dependencies should be processed in order to generate IDE configuration files. + * + * @param deps list of <code>IdeDependency</code> objects, with artifacts, sources and javadocs already resolved + * @throws MojoExecutionException generic mojo exception + */ + protected abstract void writeConfiguration( IdeDependency[] deps ) + throws MojoExecutionException; + + /** + * Not a plugin parameter. Collect the list of dependencies with a missing source artifact for the final report. + */ + private List missingSourceDependencies = new ArrayList(); + + /** + * Not a plugin parameter. Collect the list of dependencies with a missing javadoc artifact for the final report. + */ + // TODO merge this with the missingSourceDependencies in a classifier based map? + private List missingJavadocDependencies = new ArrayList(); + + /** + * Cached array of resolved dependencies. + */ + private IdeDependency[] ideDeps; + + /** + * Flag for mojo implementations to control whether normal maven dependencies should be resolved. Default value is + * true. + */ + private boolean resolveDependencies = true; + + /** + * @see org.codehaus.plexus.logging.LogEnabled#enableLogging(org.codehaus.plexus.logging.Logger) + */ + public void enableLogging( Logger logger ) + { + this.logger = logger; + } + + /** + * @see org.apache.maven.plugin.Mojo#execute() + */ + public final void execute() + throws MojoExecutionException, MojoFailureException + { + if ( skip ) + { + return; + } + + boolean processProject = setup(); + if ( !processProject ) + { + return; + } + + // resolve artifacts + IdeDependency[] deps = doDependencyResolution(); + + resolveSourceAndJavadocArtifacts( deps ); + + writeConfiguration( deps ); + + reportMissingArtifacts(); + + } + + /** + * Resolve project dependencies. Manual resolution is needed in order to avoid resolution of multiproject artifacts + * (if projects will be linked each other an installed jar is not needed) and to avoid a failure when a jar is + * missing. + * + * @throws MojoExecutionException if dependencies can't be resolved + * @return resolved IDE dependencies, with attached jars for non-reactor dependencies + */ + protected IdeDependency[] doDependencyResolution() + throws MojoExecutionException + { + if ( ideDeps == null ) + { + if ( resolveDependencies ) + { + MavenProject project = getProject(); + ArtifactRepository localRepo = getLocalRepository(); + + List deps = getProject().getDependencies(); + + // Collect the list of resolved IdeDependencies. + List dependencies = new ArrayList(); + + if ( deps != null ) + { + Map managedVersions = + createManagedVersionMap( getArtifactFactory(), project.getId(), + project.getDependencyManagement() ); + + ArtifactResolutionResult artifactResolutionResult = null; + + try + { + + List listeners = new ArrayList(); + + if ( logger.isDebugEnabled() ) + { + listeners.add( new DebugResolutionListener( logger ) ); + } + + listeners.add( new WarningResolutionListener( logger ) ); + + artifactResolutionResult = + artifactCollector.collect( getProjectArtifacts(), project.getArtifact(), managedVersions, + localRepo, project.getRemoteArtifactRepositories(), + getArtifactMetadataSource(), null, listeners ); + } + catch ( ArtifactResolutionException e ) + { + getLog().debug( e.getMessage(), e ); + getLog().error( + Messages.getString( "artifactresolution", new Object[] { //$NON-NLS-1$ + e.getGroupId(), e.getArtifactId(), e.getVersion(), + e.getMessage() } ) ); + + // if we are here artifactResolutionResult is null, create a project without dependencies but + // don't fail + // (this could be a reactor projects, we don't want to fail everything) + // Causes MECLIPSE-185. Not sure if it should be handled this way?? + return new IdeDependency[0]; + } + + // keep track of added reactor projects in order to avoid duplicates + Set emittedReactorProjectId = new HashSet(); + + for ( Iterator i = artifactResolutionResult.getArtifactResolutionNodes().iterator(); i.hasNext(); ) + { + + ResolutionNode node = (ResolutionNode) i.next(); + int dependencyDepth = node.getDepth(); + Artifact art = node.getArtifact(); + // don't resolve jars for reactor projects + if ( hasToResolveJar( art ) ) + { + try + { + artifactResolver.resolve( art, node.getRemoteRepositories(), localRepository ); + } + catch ( ArtifactNotFoundException e ) + { + getLog().debug( e.getMessage(), e ); + getLog().warn( + Messages.getString( "artifactdownload", new Object[] { //$NON-NLS-1$ + e.getGroupId(), e.getArtifactId(), e.getVersion(), + e.getMessage() } ) ); + } + catch ( ArtifactResolutionException e ) + { + getLog().debug( e.getMessage(), e ); + getLog().warn( + Messages.getString( "artifactresolution", new Object[] { //$NON-NLS-1$ + e.getGroupId(), e.getArtifactId(), e.getVersion(), + e.getMessage() } ) ); + } + } + + boolean includeArtifact = true; + if ( getExcludes() != null ) + { + String artifactFullId = art.getGroupId() + ":" + art.getArtifactId(); + if ( getExcludes().contains( artifactFullId ) ) + { + getLog().info( "excluded: " + artifactFullId ); + includeArtifact = false; + } + } + + if ( includeArtifact && + ( !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) ) || emittedReactorProjectId.add( art.getGroupId() + + '-' + art.getArtifactId() ) ) ) + { + + // the following doesn't work: art.getArtifactHandler().getPackaging() always returns "jar" + // also + // if the packaging specified in pom.xml is different. + + // osgi-bundle packaging is provided by the felix osgi plugin + // eclipse-plugin packaging is provided by this eclipse plugin + // String packaging = art.getArtifactHandler().getPackaging(); + // boolean isOsgiBundle = "osgi-bundle".equals( packaging ) || "eclipse-plugin".equals( + // packaging ); + + // we need to check the manifest, if "Bundle-SymbolicName" is there the artifact can be + // considered + // an osgi bundle + boolean isOsgiBundle = false; + String osgiSymbolicName = null; + if ( art.getFile() != null ) + { + JarFile jarFile = null; + try + { + jarFile = new JarFile( art.getFile(), false, ZipFile.OPEN_READ ); + + Manifest manifest = jarFile.getManifest(); + if ( manifest != null ) + { + osgiSymbolicName = + manifest.getMainAttributes().getValue( + new Attributes.Name( + "Bundle-SymbolicName" ) ); + } + } + catch ( IOException e ) + { + getLog().info( "Unable to read jar manifest from " + art.getFile() ); + } + finally + { + if ( jarFile != null ) + { + try + { + jarFile.close(); + } + catch ( IOException e ) + { + // ignore + } + } + } + } + + isOsgiBundle = osgiSymbolicName != null; + + IdeDependency dep = + new IdeDependency( art.getGroupId(), art.getArtifactId(), art.getVersion(), + art.getClassifier(), useProjectReference( art ), + Artifact.SCOPE_TEST.equals( art.getScope() ), + Artifact.SCOPE_SYSTEM.equals( art.getScope() ), + Artifact.SCOPE_PROVIDED.equals( art.getScope() ), + art.getArtifactHandler().isAddedToClasspath(), art.getFile(), + art.getType(), isOsgiBundle, osgiSymbolicName, dependencyDepth, + getProjectNameForArifact( art ) ); + // no duplicate entries allowed. System paths can cause this problem. + if ( !dependencies.contains( dep ) ) + { + // [rfeng] Do not add compile/provided dependencies + if (!(pde && (Artifact.SCOPE_COMPILE.equals(art.getScope()) || Artifact.SCOPE_PROVIDED + .equals(art.getScope())))) { + dependencies.add( dep ); + } + } + } + + } + + // @todo a final report with the list of + // missingArtifacts? + + } + + ideDeps = (IdeDependency[]) dependencies.toArray( new IdeDependency[dependencies.size()] ); + } + else + { + ideDeps = new IdeDependency[0]; + } + } + + return ideDeps; + } + + /** + * Find the name of the project as used in eclipse. + * + * @param artifact The artifact to find the eclipse name for. + * @return The name os the eclipse project. + */ + abstract public String getProjectNameForArifact( Artifact artifact ); + + /** + * Returns the list of project artifacts. Also artifacts generated from referenced projects will be added, but with + * the <code>resolved</code> property set to true. + * + * @return list of projects artifacts + * @throws MojoExecutionException if unable to parse dependency versions + */ + private Set getProjectArtifacts() + throws MojoExecutionException + { + // keep it sorted, this should avoid random classpath order in tests + Set artifacts = new TreeSet(); + + for ( Iterator dependencies = getProject().getDependencies().iterator(); dependencies.hasNext(); ) + { + Dependency dependency = (Dependency) dependencies.next(); + + String groupId = dependency.getGroupId(); + String artifactId = dependency.getArtifactId(); + VersionRange versionRange; + try + { + versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new MojoExecutionException( + Messages.getString( + "unabletoparseversion", new Object[] { //$NON-NLS-1$ + dependency.getArtifactId(), + dependency.getVersion(), + dependency.getManagementKey(), e.getMessage() } ), + e ); + } + + String type = dependency.getType(); + if ( type == null ) + { + type = Constants.PROJECT_PACKAGING_JAR; + } + String classifier = dependency.getClassifier(); + boolean optional = dependency.isOptional(); + String scope = dependency.getScope(); + if ( scope == null ) + { + scope = Artifact.SCOPE_COMPILE; + } + + Artifact art = + getArtifactFactory().createDependencyArtifact( groupId, artifactId, versionRange, type, classifier, + scope, optional ); + + if ( scope.equalsIgnoreCase( Artifact.SCOPE_SYSTEM ) ) + { + art.setFile( new File( dependency.getSystemPath() ) ); + } + + List exclusions = new ArrayList(); + for ( Iterator j = dependency.getExclusions().iterator(); j.hasNext(); ) + { + Exclusion e = (Exclusion) j.next(); + exclusions.add( e.getGroupId() + ":" + e.getArtifactId() ); //$NON-NLS-1$ + } + + ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions ); + + art.setDependencyFilter( newFilter ); + + artifacts.add( art ); + } + + return artifacts; + } + + /** + * Utility method that locates a project producing the given artifact. + * + * @param artifact the artifact a project should produce. + * @return <code>true</code> if the artifact is produced by a reactor projectart. + */ + protected boolean isAvailableAsAReactorProject( Artifact artifact ) + { + if ( reactorProjects != null ) + { + for ( Iterator iter = reactorProjects.iterator(); iter.hasNext(); ) + { + MavenProject reactorProject = (MavenProject) iter.next(); + + if ( reactorProject.getGroupId().equals( artifact.getGroupId() ) && + reactorProject.getArtifactId().equals( artifact.getArtifactId() ) ) + { + if ( reactorProject.getVersion().equals( artifact.getVersion() ) ) + { + return true; + } + else + { + getLog().info( + "Artifact " + + artifact.getId() + + " already available as a reactor project, but with different version. Expected: " + + artifact.getVersion() + ", found: " + reactorProject.getVersion() ); + } + } + } + } + return false; + } + + /** + * @return an array with all dependencies avalaible in the workspace, to be implemented by the subclasses. + */ + protected IdeDependency[] getWorkspaceArtefacts() + { + return new IdeDependency[0]; + } + + private Map createManagedVersionMap( ArtifactFactory artifactFactory, String projectId, + DependencyManagement dependencyManagement ) + throws MojoExecutionException + { + Map map; + if ( dependencyManagement != null && dependencyManagement.getDependencies() != null ) + { + map = new HashMap(); + for ( Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext(); ) + { + Dependency d = (Dependency) i.next(); + + try + { + VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() ); + Artifact artifact = + artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(), versionRange, + d.getType(), d.getClassifier(), d.getScope(), + d.isOptional() ); + map.put( d.getManagementKey(), artifact ); + } + catch ( InvalidVersionSpecificationException e ) + { + throw new MojoExecutionException( Messages.getString( "unabletoparseversion", new Object[] { //$NON-NLS-1$ + projectId, d.getVersion(), + d.getManagementKey(), e.getMessage() } ), + e ); + } + } + } + else + { + map = Collections.EMPTY_MAP; + } + return map; + } + + /** + * Find the reactor target dir. executedProject doesn't have the multiproject root dir set, and the only way to + * extract it is iterating on parent projects. + * + * @param prj current project + * @return the parent target dir. + */ + private File getReactorTargetDir( MavenProject prj ) + { + if ( prj.getParent() != null ) + { + if ( prj.getParent().getBasedir() != null && prj.getParent().getBasedir().exists() ) + { + return getReactorTargetDir( prj.getParent() ); + } + } + return new File( prj.getBuild().getDirectory() ); + } + + /** + * Resolve source artifacts and download them if <code>downloadSources</code> is <code>true</code>. Source and + * javadocs artifacts will be attached to the <code>IdeDependency</code> Resolve source and javadoc artifacts. The + * resolved artifacts will be downloaded based on the <code>downloadSources</code> and + * <code>downloadJavadocs</code> attributes. Source and + * + * @param deps resolved dependencies + */ + private void resolveSourceAndJavadocArtifacts( IdeDependency[] deps ) + { + + File reactorTargetDir = getReactorTargetDir( project ); + File unavailableArtifactsTmpFile = new File( reactorTargetDir, "mvn-eclipse-cache.properties" ); + + getLog().info( "Using source status cache: " + unavailableArtifactsTmpFile.getAbsolutePath() ); + + // create target dir if missing + if ( !unavailableArtifactsTmpFile.getParentFile().exists() ) + { + unavailableArtifactsTmpFile.getParentFile().mkdirs(); + } + + Properties unavailableArtifactsCache = new Properties(); + if ( unavailableArtifactsTmpFile.exists() ) + { + InputStream is = null; + try + { + is = new FileInputStream( unavailableArtifactsTmpFile ); + unavailableArtifactsCache.load( is ); + } + catch ( IOException e ) + { + getLog().warn( "Unable to read source status for reactor projects" ); + } + finally + { + IOUtil.close( is ); + } + + } + + final List missingSources = + resolveDependenciesWithClassifier( deps, "sources", getDownloadSources(), unavailableArtifactsCache ); + missingSourceDependencies.addAll( missingSources ); + + final List missingJavadocs = + resolveDependenciesWithClassifier( deps, "javadoc", getDownloadJavadocs(), unavailableArtifactsCache ); + missingJavadocDependencies.addAll( missingJavadocs ); + + FileOutputStream fos = null; + try + { + fos = new FileOutputStream( unavailableArtifactsTmpFile ); + unavailableArtifactsCache.store( fos, "Temporary index for unavailable sources and javadocs" ); + } + catch ( IOException e ) + { + getLog().warn( "Unable to cache source status for reactor projects" ); + } + finally + { + IOUtil.close( fos ); + } + + } + + /** + * Resolve the required artifacts for each of the dependency. <code>sources</code> or <code>javadoc</code> + * artifacts (depending on the <code>classifier</code>) are attached to the dependency. + * + * @param deps resolved dependencies + * @param inClassifier the classifier we are looking for (either <code>sources</code> or <code>javadoc</code>) + * @param includeRemoteRepositories flag whether we should search remote repositories for the artifacts or not + * @param unavailableArtifactsCache cache of unavailable artifacts + * @return the list of dependencies for which the required artifact was not found + */ + private List resolveDependenciesWithClassifier( IdeDependency[] deps, String inClassifier, + boolean includeRemoteRepositories, + Properties unavailableArtifactsCache ) + { + List missingClassifierDependencies = new ArrayList(); + + // if downloadSources is off, just check + // local repository for reporting missing source jars + List remoteRepos = includeRemoteRepositories ? getRemoteArtifactRepositories() : Collections.EMPTY_LIST; + + for ( int j = 0; j < deps.length; j++ ) + { + IdeDependency dependency = deps[j]; + + if ( dependency.isReferencedProject() || dependency.isSystemScoped() ) + { + // artifact not needed + continue; + } + + if ( getLog().isDebugEnabled() ) + { + getLog().debug( + "Searching for sources for " + dependency.getId() + ":" + dependency.getClassifier() + + " at " + dependency.getId() + ":" + inClassifier ); + } + + String key = + dependency.getClassifier() == null ? dependency.getId() + ":" + inClassifier : dependency.getId() + + ":" + inClassifier + ":" + dependency.getClassifier(); + + if ( !unavailableArtifactsCache.containsKey( key ) ) + { + Artifact artifact = + IdeUtils.resolveArtifactWithClassifier( dependency.getGroupId(), dependency.getArtifactId(), + dependency.getVersion(), dependency.getClassifier(), + inClassifier, localRepository, artifactResolver, + artifactFactory, remoteRepos, getLog() ); + if ( artifact.isResolved() ) + { + if ( "sources".equals( inClassifier ) ) + { + dependency.setSourceAttachment( artifact.getFile() ); + } + else if ( "javadoc".equals( inClassifier ) ) + { + dependency.setJavadocAttachment( artifact.getFile() ); + } + } + else + { + unavailableArtifactsCache.put( key, Boolean.TRUE.toString() ); + // add the dependencies to the list + // of those lacking the required + // artifact + missingClassifierDependencies.add( dependency ); + } + } + } + + // return the list of dependencies missing the + // required artifact + return missingClassifierDependencies; + + } + + /** + * Output a message with the list of missing dependencies and info on how turn download on if it was disabled. + */ + private void reportMissingArtifacts() + { + StringBuffer msg = new StringBuffer(); + + if ( !missingSourceDependencies.isEmpty() ) + { + if ( getDownloadSources() ) + { + msg.append( Messages.getString( "sourcesnotavailable" ) ); //$NON-NLS-1$ + } + else + { + msg.append( Messages.getString( "sourcesnotdownloaded" ) ); //$NON-NLS-1$ + } + + for ( Iterator it = missingSourceDependencies.iterator(); it.hasNext(); ) + { + IdeDependency art = (IdeDependency) it.next(); + msg.append( Messages.getString( "sourcesmissingitem", art.getId() ) ); //$NON-NLS-1$ + } + msg.append( "\n" ); //$NON-NLS-1$ + } + + if ( !missingJavadocDependencies.isEmpty() ) + { + if ( getDownloadJavadocs() ) + { + msg.append( Messages.getString( "javadocnotavailable" ) ); //$NON-NLS-1$ + } + else + { + msg.append( Messages.getString( "javadocnotdownloaded" ) ); //$NON-NLS-1$ + } + + for ( Iterator it = missingJavadocDependencies.iterator(); it.hasNext(); ) + { + IdeDependency art = (IdeDependency) it.next(); + msg.append( Messages.getString( "javadocmissingitem", art.getId() ) ); //$NON-NLS-1$ + } + msg.append( "\n" ); //$NON-NLS-1$ + } + getLog().info( msg ); + } + + /** + * @return List of dependencies to exclude from eclipse classpath. + * @since 2.5 + */ + public abstract List getExcludes(); + + /** + * Checks if jar has to be resolved for the given artifact + * + * @param art the artifact to check + * @return true if resolution should happen + */ + protected boolean hasToResolveJar( Artifact art ) + { + return !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) ); + } + + /** + * Checks if a projects reference has to be used for the given artifact + * + * @param art the artifact to check + * @return true if a project reference has to be used. + */ + protected boolean useProjectReference( Artifact art ) + { + return getUseProjectReferences() && isAvailableAsAReactorProject( art ); + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseClasspathWriter.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseClasspathWriter.java new file mode 100644 index 0000000000..2813eb5388 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseClasspathWriter.java @@ -0,0 +1,531 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.eclipse.BuildCommand; +import org.apache.maven.plugin.eclipse.Constants; +import org.apache.maven.plugin.eclipse.EclipseSourceDir; +import org.apache.maven.plugin.eclipse.writers.AbstractEclipseWriter; +import org.apache.maven.plugin.eclipse.writers.EclipseAntExternalLaunchConfigurationWriter; +import org.apache.maven.plugin.eclipse.writers.EclipseLaunchConfigurationWriter; +import org.apache.maven.plugin.ide.IdeDependency; +import org.apache.maven.plugin.ide.IdeUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; +import org.codehaus.plexus.util.xml.XMLWriter; + +/** + * Writes eclipse .classpath file. + * + * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> + * @author <a href="mailto:kenney@neonics.com">Kenney Westerhof</a> + * @author <a href="mailto:fgiust@apache.org">Fabrizio Giustina</a> + * @version $Id: EclipseClasspathWriter.java 636955 2008-03-14 02:10:42Z aheritier $ + */ +public class EclipseClasspathWriter + extends AbstractEclipseWriter +{ + + /** + * Eclipse build path variable M2_REPO + */ + private static final String M2_REPO = "M2_REPO"; //$NON-NLS-1$ + + /** + * Attribute for sourcepath. + */ + private static final String ATTR_SOURCEPATH = "sourcepath"; //$NON-NLS-1$ + + /** + * Attribute for output. + */ + private static final String ATTR_OUTPUT = "output"; //$NON-NLS-1$ + + /** + * Attribute for path. + */ + private static final String ATTR_PATH = "path"; //$NON-NLS-1$ + + /** + * Attribute for kind - Container (con), Variable (var)..etc. + */ + private static final String ATTR_KIND = "kind"; //$NON-NLS-1$ + + /** + * Attribute value for kind: var + */ + private static final String ATTR_VAR = "var"; //$NON-NLS-1$ + + /** + * Attribute value for kind: lib + */ + private static final String ATTR_LIB = "lib"; //$NON-NLS-1$ + + /** + * Attribute value for kind: src + */ + private static final String ATTR_SRC = "src"; //$NON-NLS-1$ + + /** + * Attribute name for source file includes in a path. + */ + private static final String ATTR_INCLUDING = "including"; + + /** + * Attribute name for source file excludes in a path. + */ + private static final String ATTR_EXCLUDING = "excluding"; + + /** + * Element for classpathentry. + */ + private static final String ELT_CLASSPATHENTRY = "classpathentry"; //$NON-NLS-1$ + + /** + * Element for classpath. + */ + private static final String ELT_CLASSPATH = "classpath"; //$NON-NLS-1$ + + /** + * File name that stores project classpath settings. + */ + private static final String FILE_DOT_CLASSPATH = ".classpath"; //$NON-NLS-1$ + + /** + * @see org.apache.tuscany.sca.maven.plugin.eclipse.writers.EclipseWriter#write() + */ + public void write() + throws MojoExecutionException + { + + Writer w; + + try + { + w = + new OutputStreamWriter( new FileOutputStream( new File( config.getEclipseProjectDirectory(), + FILE_DOT_CLASSPATH ) ), "UTF-8" ); + } + catch ( IOException ex ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.erroropeningfile" ), ex ); //$NON-NLS-1$ + } + + XMLWriter writer = new PrettyPrintXMLWriter( w ); + + writer.startElement( ELT_CLASSPATH ); + + String defaultOutput = + IdeUtils.toRelativeAndFixSeparator( config.getProjectBaseDir(), config.getBuildOutputDirectory(), false ); + + // ---------------------------------------------------------------------- + // Source roots and resources + // ---------------------------------------------------------------------- + + // List<EclipseSourceDir> + List specialSources = new ArrayList(); + + // Map<String,List<EclipseSourceDir>> + Map byOutputDir = new HashMap(); + + for ( int j = 0; j < config.getSourceDirs().length; j++ ) + { + EclipseSourceDir dir = config.getSourceDirs()[j]; + + // List<EclipseSourceDir> + List byOutputDirs = (List) byOutputDir.get( dir.getOutput() ); + if ( byOutputDirs == null ) + { + // ArrayList<EclipseSourceDir> + byOutputDir.put( dir.getOutput() == null ? defaultOutput : dir.getOutput(), byOutputDirs = + new ArrayList() ); + } + byOutputDirs.add( dir ); + } + + for ( int j = 0; j < config.getSourceDirs().length; j++ ) + { + EclipseSourceDir dir = config.getSourceDirs()[j]; + + log.debug( "Processing " + ( dir.isResource() ? "re" : "" ) + "source " + dir.getPath() + ": output=" + + dir.getOutput() + "; default output=" + defaultOutput ); + + boolean isSpecial = false; + + // handle resource with nested output folders + if ( dir.isResource() ) + { + // Check if the output is a subdirectory of the default output, + // and if the default output has any sources that copy there. + + if ( dir.getOutput() != null // resource output dir is set + && + !dir.getOutput().equals( defaultOutput ) // output dir is not default target/classes + && dir.getOutput().startsWith( defaultOutput ) // ... but is nested + && byOutputDir.get( defaultOutput ) != null // ??? + && !( (List) byOutputDir.get( defaultOutput ) ).isEmpty() // ??? + ) + { + // do not specify as source since the output will be nested. Instead, mark + // it as a todo, and handle it with a custom build.xml file later. + + log.debug( "Marking as special to prevent output folder nesting: " + dir.getPath() + " (output=" + + dir.getOutput() + ")" ); + + isSpecial = true; + specialSources.add( dir ); + } + } + + writer.startElement( ELT_CLASSPATHENTRY ); + + writer.addAttribute( ATTR_KIND, "src" ); //$NON-NLS-1$ + writer.addAttribute( ATTR_PATH, dir.getPath() ); + + if ( !isSpecial && dir.getOutput() != null && !defaultOutput.equals( dir.getOutput() ) ) + { + writer.addAttribute( ATTR_OUTPUT, dir.getOutput() ); + } + + if ( StringUtils.isNotEmpty( dir.getInclude() ) ) + { + writer.addAttribute( ATTR_INCLUDING, dir.getInclude() ); + } + + String excludes = dir.getExclude(); + + if ( dir.isResource() ) + { + // automatically exclude java files: eclipse doesn't have the concept of resource directory so it will + // try to compile any java file found in maven resource dirs + excludes = StringUtils.isEmpty( excludes ) ? "**/*.java" : excludes + "|**/*.java"; + } + + if ( StringUtils.isNotEmpty( excludes ) ) + { + writer.addAttribute( ATTR_EXCLUDING, excludes ); + } + + writer.endElement(); + + } + + // handle the special sources. + if ( !specialSources.isEmpty() ) + { + log.info( "Creating maven-eclipse.xml Ant file to handle resources" ); + + try + { + Writer buildXmlWriter = + new OutputStreamWriter( new FileOutputStream( new File( config.getEclipseProjectDirectory(), + "maven-eclipse.xml" ) ), "UTF-8" ); + PrettyPrintXMLWriter buildXmlPrinter = new PrettyPrintXMLWriter( buildXmlWriter ); + + buildXmlPrinter.startElement( "project" ); + buildXmlPrinter.addAttribute( "default", "copy-resources" ); + + buildXmlPrinter.startElement( "target" ); + buildXmlPrinter.addAttribute( "name", "init" ); + // initialize filtering tokens here + buildXmlPrinter.endElement(); + + buildXmlPrinter.startElement( "target" ); + buildXmlPrinter.addAttribute( "name", "copy-resources" ); + buildXmlPrinter.addAttribute( "depends", "init" ); + + for ( Iterator it = specialSources.iterator(); it.hasNext(); ) + { + // TODO: merge source dirs on output path+filtering to reduce + // <copy> tags for speed. + EclipseSourceDir dir = (EclipseSourceDir) it.next(); + buildXmlPrinter.startElement( "copy" ); + buildXmlPrinter.addAttribute( "todir", dir.getOutput() ); + buildXmlPrinter.addAttribute( "filtering", "" + dir.isFiltering() ); + + buildXmlPrinter.startElement( "fileset" ); + buildXmlPrinter.addAttribute( "dir", dir.getPath() ); + if ( dir.getInclude() != null ) + { + buildXmlPrinter.addAttribute( "includes", dir.getInclude() ); + } + if ( dir.getExclude() != null ) + { + buildXmlPrinter.addAttribute( "excludes", dir.getExclude() ); + } + buildXmlPrinter.endElement(); + + buildXmlPrinter.endElement(); + } + + buildXmlPrinter.endElement(); + + buildXmlPrinter.endElement(); + + IOUtil.close( buildXmlWriter ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Cannot create " + config.getEclipseProjectDirectory() + + "/maven-eclipse.xml", e ); + } + + log.info( "Creating external launcher file" ); + // now create the launcher + new EclipseAntExternalLaunchConfigurationWriter().init( log, config, "Maven_Ant_Builder.launch", + "maven-eclipse.xml" ).write(); + + // finally add it to the project writer. + + config.getBuildCommands().add( + new BuildCommand( + "org.eclipse.ui.externaltools.ExternalToolBuilder", + "LaunchConfigHandle", + "<project>/" + + EclipseLaunchConfigurationWriter.FILE_DOT_EXTERNAL_TOOL_BUILDERS + + "Maven_Ant_Builder.launch" ) ); + } + + // ---------------------------------------------------------------------- + // The default output + // ---------------------------------------------------------------------- + + writer.startElement( ELT_CLASSPATHENTRY ); + writer.addAttribute( ATTR_KIND, ATTR_OUTPUT ); + writer.addAttribute( ATTR_PATH, defaultOutput ); + writer.endElement(); + + // ---------------------------------------------------------------------- + // Container classpath entries + // ---------------------------------------------------------------------- + + for ( Iterator it = config.getClasspathContainers().iterator(); it.hasNext(); ) + { + writer.startElement( ELT_CLASSPATHENTRY ); + writer.addAttribute( ATTR_KIND, "con" ); //$NON-NLS-1$ + writer.addAttribute( ATTR_PATH, (String) it.next() ); + writer.endElement(); // name + } + + // ---------------------------------------------------------------------- + // The dependencies + // ---------------------------------------------------------------------- + Set addedDependencies = new HashSet(); + // TODO if (..magic property equals orderDependencies..) + IdeDependency[] depsToWrite = config.getDepsOrdered(); + for ( int j = 0; j < depsToWrite.length; j++ ) + { + IdeDependency dep = depsToWrite[j]; + + if ( dep.isAddedToClasspath() ) + { + String depId = + dep.getGroupId() + ":" + dep.getArtifactId() + ":" + dep.getClassifier() + ":" + dep.getVersion(); + /* avoid duplicates in the classpath for artifacts with different types (like ejbs) */ + if ( !addedDependencies.contains( depId ) ) + { + addDependency( writer, dep ); + addedDependencies.add( depId ); + } + } + } + + writer.endElement(); + + IOUtil.close( w ); + + } + + protected void addDependency( XMLWriter writer, IdeDependency dep ) + throws MojoExecutionException + { + + String path; + String kind; + String sourcepath = null; + String javadocpath = null; + // [rfeng] Force to non-PDE mode + boolean pdeMode = false; + + if ( dep.isReferencedProject() && !pdeMode ) + { + path = "/" + dep.getEclipseProjectName(); //$NON-NLS-1$ + kind = ATTR_SRC; + } + else if ( dep.isReferencedProject() && pdeMode ) + { + // don't do anything, referenced projects are automatically handled by eclipse in PDE builds + return; + } + else + { + File artifactPath = dep.getFile(); + + if ( artifactPath == null ) + { + log.error( Messages.getString( "EclipsePlugin.artifactpathisnull", dep.getId() ) ); //$NON-NLS-1$ + return; + } + + if ( dep.isSystemScoped() ) + { + path = IdeUtils.toRelativeAndFixSeparator( config.getEclipseProjectDirectory(), artifactPath, false ); + + if ( log.isDebugEnabled() ) + { + log.debug( Messages.getString( "EclipsePlugin.artifactissystemscoped", //$NON-NLS-1$ + new Object[] { dep.getArtifactId(), path } ) ); + } + + kind = ATTR_LIB; + } + else + { + File localRepositoryFile = new File( config.getLocalRepository().getBasedir() ); + + // if the dependency is not provided and the plugin runs in "pde mode", the dependency is + // added to the Bundle-Classpath: + if ( pdeMode && ( dep.isProvided() || dep.isOsgiBundle() ) ) + { + return; + } + else if ( pdeMode && !dep.isProvided() && !dep.isTestDependency() ) + { + // path for link created in .project, not to the actual file + path = dep.getFile().getName(); + + kind = ATTR_LIB; + } + // running in PDE mode and the dependency is provided means, that it is provided by + // the target platform. This case is covered by adding the plugin container + else + { + String fullPath = artifactPath.getPath(); + String relativePath = + IdeUtils.toRelativeAndFixSeparator( localRepositoryFile, new File( fullPath ), false ); + + if ( !new File( relativePath ).isAbsolute() ) + { + path = M2_REPO + "/" //$NON-NLS-1$ + + relativePath; + kind = ATTR_VAR; //$NON-NLS-1$ + } + else + { + path = relativePath; + kind = ATTR_LIB; + } + } + + if ( dep.getSourceAttachment() != null ) + { + if ( ATTR_VAR.equals( kind ) ) + { + sourcepath = + M2_REPO + + "/" //$NON-NLS-1$ + + + IdeUtils.toRelativeAndFixSeparator( localRepositoryFile, dep.getSourceAttachment(), + false ); + } + else + { + // source archive must be referenced with the full path, we can't mix a lib with a variable + sourcepath = IdeUtils.getCanonicalPath( dep.getSourceAttachment() ); + } + } + + if ( dep.getJavadocAttachment() != null ) + { + // NB eclipse (3.1) doesn't support variables in javadoc paths, so we need to add the + // full path for the maven repo + javadocpath = + StringUtils.replace( IdeUtils.getCanonicalPath( dep.getJavadocAttachment() ), "\\", "/" ); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } + + } + + writer.startElement( ELT_CLASSPATHENTRY ); + writer.addAttribute( ATTR_KIND, kind ); + writer.addAttribute( ATTR_PATH, path ); + + if ( sourcepath != null ) + { + writer.addAttribute( ATTR_SOURCEPATH, sourcepath ); + } + + boolean attributeElemOpen = false; + + if ( javadocpath != null ) + { + if ( !attributeElemOpen ) + { + writer.startElement( "attributes" ); //$NON-NLS-1$ + attributeElemOpen = true; + } + + writer.startElement( "attribute" ); //$NON-NLS-1$ + writer.addAttribute( "value", "jar:" + new File( javadocpath ).toURI() + "!/" ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + writer.addAttribute( "name", "javadoc_location" ); //$NON-NLS-1$ //$NON-NLS-2$ + writer.endElement(); + + } + + if ( Constants.PROJECT_PACKAGING_WAR.equals( this.config.getPackaging() ) && config.getWtpapplicationxml() && + kind.equals( ATTR_VAR ) && !dep.isTestDependency() && !dep.isProvided() && + !dep.isSystemScopedOutsideProject( this.config.getProject() ) ) + { + if ( !attributeElemOpen ) + { + writer.startElement( "attributes" ); //$NON-NLS-1$ + attributeElemOpen = true; + } + + writer.startElement( "attribute" ); //$NON-NLS-1$ + writer.addAttribute( "value", "/WEB-INF/lib" ); //$NON-NLS-1$ //$NON-NLS-2$ + writer.addAttribute( "name", "org.eclipse.jst.component.dependency" ); //$NON-NLS-1$ //$NON-NLS-2$ + writer.endElement(); + + } + + if ( attributeElemOpen ) + { + writer.endElement(); + } + writer.endElement(); + + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseCleanMojo.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseCleanMojo.java new file mode 100644 index 0000000000..ac770c743c --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseCleanMojo.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.io.File; +import java.io.IOException; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.eclipse.Constants; +import org.apache.maven.plugin.eclipse.EclipseConfigFile; +import org.codehaus.plexus.util.FileUtils; + +/** + * Deletes the .project, .classpath, .wtpmodules files and .settings folder used by Eclipse. + * + * @goal clean + */ +public class EclipseCleanMojo + extends AbstractMojo +{ + + /** + * Definition file for Eclipse Web Tools project. + */ + private static final String FILE_DOT_WTPMODULES = ".wtpmodules"; //$NON-NLS-1$ + + /** + * Classpath definition file for an Eclipse Java project. + */ + private static final String FILE_DOT_CLASSPATH = ".classpath"; //$NON-NLS-1$ + + /** + * Project definition file for an Eclipse Project. + */ + private static final String FILE_DOT_PROJECT = ".project"; //$NON-NLS-1$ + + /** + * Web Project definition file for Eclipse Web Tools Project (Release 1.0x). + */ + private static final String DIR_DOT_SETTINGS = ".settings"; //$NON-NLS-1$ + + /** + * File name where the WTP component settings will be stored - WTP 1.0 name. + */ + private static final String FILE_DOT_COMPONENT = ".settings/.component"; //$NON-NLS-1$ + + /** + * File name where the WTP component settings will be stored - WTP 1.5 name. + */ + private static final String FILE_DOT_COMPONENT_15 = ".settings/org.eclipse.wst.common.component"; //$NON-NLS-1$ + + /** + * File name where Eclipse Project's Facet configuration will be stored. + */ + private static final String FILE_FACET_CORE_XML = ".settings/org.eclipse.wst.common.project.facet.core.xml"; //$NON-NLS-1$ + + /** + * General project preferences. + */ + private static final String FILE_ECLIPSE_JDT_CORE_PREFS = ".settings/org.eclipse.jdt.core.prefs"; //$NON-NLS-1$ + + /** + * Packaging for the current project. + * + * @parameter expression="${project.packaging}" + */ + private String packaging; + + /** + * The root directory of the project + * + * @parameter expression="${basedir}" + */ + private File basedir; + + /** + * Skip the operation when true. + * + * @parameter expression="${eclipse.skip}" default-value="false" + */ + private boolean skip; + + /** + * additional generic configuration files for eclipse + * + * @parameter + */ + private EclipseConfigFile[] additionalConfig; + + /** + * @see org.apache.maven.plugin.AbstractMojo#execute() + */ + public void execute() + throws MojoExecutionException + { + if ( skip ) + { + return; + } + + if ( Constants.PROJECT_PACKAGING_POM.equals( this.packaging ) ) + { + return; + } + + delete( new File( basedir, FILE_DOT_PROJECT ) ); + delete( new File( basedir, FILE_DOT_CLASSPATH ) ); + delete( new File( basedir, FILE_DOT_WTPMODULES ) ); + + delete( new File( basedir, FILE_DOT_COMPONENT ) ); + delete( new File( basedir, FILE_DOT_COMPONENT_15 ) ); + delete( new File( basedir, FILE_FACET_CORE_XML ) ); + delete( new File( basedir, FILE_ECLIPSE_JDT_CORE_PREFS ) ); + + File settingsDir = new File( basedir, DIR_DOT_SETTINGS ); + if ( settingsDir.exists() && settingsDir.isDirectory() && settingsDir.list().length == 0 ) + { + delete( settingsDir ); + } + + if ( additionalConfig != null ) + { + for ( int i = 0; i < additionalConfig.length; i++ ) + { + delete( new File( basedir, additionalConfig[i].getName() ) ); + } + } + + cleanExtras(); + } + + protected void cleanExtras() + throws MojoExecutionException + { + // extension point. + } + + /** + * Delete a file, handling log messages and exceptions + * + * @param f File to be deleted + * @throws MojoExecutionException only if a file exists and can't be deleted + */ + protected void delete( File f ) + throws MojoExecutionException + { + if ( f.isDirectory() ) + { + getLog().info( Messages.getString( "EclipseCleanMojo.deletingDirectory", f.getName() ) ); //$NON-NLS-1$ + } + else + { + getLog().info( Messages.getString( "EclipseCleanMojo.deletingFile", f.getName() ) ); //$NON-NLS-1$ + } + + if ( f.exists() ) + { + if ( !f.delete() ) + { + try + { + FileUtils.forceDelete( f ); + } + catch ( IOException e ) + { + throw new MojoExecutionException( Messages.getString( "EclipseCleanMojo.failedtodelete", //$NON-NLS-1$ + new Object[] { f.getName(), + f.getAbsolutePath() } ) ); + } + } + } + else + { + getLog().debug( Messages.getString( "EclipseCleanMojo.nofilefound", f.getName() ) ); //$NON-NLS-1$ + } + } + + /** + * Getter for <code>basedir</code>. + * + * @return Returns the basedir. + */ + public File getBasedir() + { + return this.basedir; + } + + /** + * Setter for <code>basedir</code>. + * + * @param basedir The basedir to set. + */ + public void setBasedir( File basedir ) + { + this.basedir = basedir; + } + + /** + * @return the packaging + */ + public String getPackaging() + { + return this.packaging; + } + + /** + * @param packaging the packaging to set + */ + public void setPackaging( String packaging ) + { + this.packaging = packaging; + } + +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipsePlugin.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipsePlugin.java new file mode 100644 index 0000000000..f2049c2cde --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipsePlugin.java @@ -0,0 +1,1550 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.handler.ArtifactHandler; +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.eclipse.BuildCommand; +import org.apache.maven.plugin.eclipse.Constants; +import org.apache.maven.plugin.eclipse.EclipseConfigFile; +import org.apache.maven.plugin.eclipse.EclipseSourceDir; +import org.apache.maven.plugin.eclipse.WorkspaceConfiguration; +import org.apache.maven.plugin.eclipse.reader.ReadWorkspaceLocations; +import org.apache.maven.plugin.eclipse.writers.EclipseManifestWriter; +import org.apache.maven.plugin.eclipse.writers.EclipseSettingsWriter; +import org.apache.maven.plugin.eclipse.writers.EclipseWriterConfig; +import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpApplicationXMLWriter; +import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpComponent15Writer; +import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpComponentWriter; +import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpFacetsWriter; +import org.apache.maven.plugin.eclipse.writers.wtp.EclipseWtpmodulesWriter; +import org.apache.maven.plugin.ide.IdeDependency; +import org.apache.maven.plugin.ide.IdeUtils; +import org.apache.maven.plugin.ide.JeeUtils; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.resource.ResourceManager; +import org.codehaus.plexus.resource.loader.FileResourceLoader; +import org.codehaus.plexus.resource.loader.ResourceNotFoundException; +import org.codehaus.plexus.util.FileUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; +import org.codehaus.plexus.util.xml.Xpp3Dom; + +/** + * Generates the following eclipse configuration files: + * <ul> + * <li><code>.project</code> and <code>.classpath</code> files</li> + * <li><code>.setting/org.eclipse.jdt.core.prefs</code> with project specific compiler settings</li> + * <li>various configuration files for WTP (Web Tools Project), if the parameter <code>wtpversion</code> is set to a + * valid version (WTP configuration is not generated by default)</li> + * </ul> + * If this goal is run on a multiproject root, dependencies between modules will be configured as direct project + * dependencies in Eclipse (unless <code>useProjectReferences</code> is set to <code>false</code>). + * + * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> + * @author <a href="mailto:fgiust@apache.org">Fabrizio Giustina</a> + * @version $Id: EclipsePlugin.java 641616 2008-03-26 22:42:42Z aheritier $ + * @goal eclipse + * @phase generate-resources + */ +public class EclipsePlugin + extends AbstractIdeSupportMojo +{ + + private static final String NATURE_WST_FACET_CORE_NATURE = "org.eclipse.wst.common.project.facet.core.nature"; //$NON-NLS-1$ + + private static final String BUILDER_WST_COMPONENT_STRUCTURAL_DEPENDENCY_RESOLVER = + "org.eclipse.wst.common.modulecore.ComponentStructuralBuilderDependencyResolver"; //$NON-NLS-1$ + + protected static final String BUILDER_WST_VALIDATION = "org.eclipse.wst.validation.validationbuilder"; //$NON-NLS-1$ + + private static final String BUILDER_JDT_CORE_JAVA = "org.eclipse.jdt.core.javabuilder"; //$NON-NLS-1$ + + private static final String BUILDER_WST_COMPONENT_STRUCTURAL = + "org.eclipse.wst.common.modulecore.ComponentStructuralBuilder"; //$NON-NLS-1$ + + private static final String BUILDER_WST_FACET = "org.eclipse.wst.common.project.facet.core.builder"; //$NON-NLS-1$ + + private static final String BUILDER_PDE_MANIFEST = "org.eclipse.pde.ManifestBuilder"; //$NON-NLS-1$ + + private static final String BUILDER_PDE_SCHEMA = "org.eclipse.pde.SchemaBuilder"; //$NON-NLS-1$ + + private static final String NATURE_WST_MODULE_CORE_NATURE = "org.eclipse.wst.common.modulecore.ModuleCoreNature"; //$NON-NLS-1$ + + private static final String NATURE_JDT_CORE_JAVA = "org.eclipse.jdt.core.javanature"; //$NON-NLS-1$ + + private static final String NATURE_JEM_WORKBENCH_JAVA_EMF = "org.eclipse.jem.workbench.JavaEMFNature"; //$NON-NLS-1$ + + private static final String NATURE_PDE_PLUGIN = "org.eclipse.pde.PluginNature"; //$NON-NLS-1$ + + protected static final String COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER = "org.eclipse.jdt.launching.JRE_CONTAINER"; //$NON-NLS-1$ + + protected static final String REQUIRED_PLUGINS_CONTAINER = "org.eclipse.pde.core.requiredPlugins"; //$NON-NLS-1$ + + // warning, order is important for binary search + public static final String[] WTP_SUPPORTED_VERSIONS = new String[] { "1.0", "1.5", "2.0", "R7", "none" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + /** + * Constant for 'artifactId' element in POM.xml. + */ + private static final String POM_ELT_ARTIFACT_ID = "artifactId"; //$NON-NLS-1$ + + /** + * Constant for 'groupId' element in POM.xml. + */ + private static final String POM_ELT_GROUP_ID = "groupId"; //$NON-NLS-1$ + + /** + * List of eclipse project natures. By default the <code>org.eclipse.jdt.core.javanature</code> nature plus the + * needed WTP natures are added. Natures added using this property <strong>replace</strong> the default list. + * + * <pre> + * <projectnatures> + * <projectnature>org.eclipse.jdt.core.javanature</projectnature> + * <projectnature>org.eclipse.wst.common.modulecore.ModuleCoreNature</projectnature> + * </projectnatures> + * </pre> + * + * @parameter + */ + private List projectnatures; + + /** + * List of artifact to exclude from eclipse classpath, beeing provided by some eclipse classPathContainer + * [MECLIPSE-79] + * + * @since 2.5 + * @parameter + */ + private List excludes; + + /** + * List of eclipse project natures to be added to the default ones. + * + * <pre> + * <additionalProjectnatures> + * <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature> + * </additionalProjectnatures> + * </pre> + * + * @parameter + */ + private List additionalProjectnatures; + + /** + * List of eclipse project facets to be added to the default ones. + * + * <pre> + * <additionalProjectFacets> + * <jst.jsf>1.1<jst.jsf/> + * </additionalProjectFacets> + * </pre> + * + * @parameter + */ + private Map additionalProjectFacets; + + /** + * List of eclipse build commands. By default the <code>org.eclipse.jdt.core.javabuilder</code> builder plus the + * needed WTP builders are added. If you specify any configuration for this parameter, only those buildcommands + * specified will be used; the defaults won't be added. Use the <code>additionalBuildCommands</code> parameter for + * that. Configuration example: Old style: + * + * <pre> + * <buildcommands> + * <buildcommand>org.eclipse.wst.common.modulecore.ComponentStructuralBuilder</buildcommand> + * <buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand> + * <buildcommand>org.eclipse.wst.common.modulecore.ComponentStructuralBuilderDependencyResolver</buildcommand> + * </buildcommands> + * </pre> + * + * For new style, see <code>additionalBuildCommands</code>. + * + * @parameter + */ + private List buildcommands; + + /** + * List of eclipse build commands to be added to the default ones. Old style: + * + * <pre> + * <additionalBuildcommands> + * <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand> + * </additionalBuildcommands> + * </pre> + * + * New style: + * + * <pre> + * <additionalBuildcommands> + * <buildCommand> + * <name>org.ui.externaltools.ExternalToolBuilder</name> + * <triggers>auto,full,incremental,</triggers> + * <arguments> + * <LaunchConfigHandle>&lt;project&gt;./externalToolBuilders/MavenBuilder.launch</LaunchConfighandle> + * </arguments> + * </buildCommand> + * </additionalBuildcommands> + * </pre> + * + * Note the difference between <code>build<strong>c</strong>ommand</code> and + * <code>build<strong>C</strong>ommand</code>. You can mix and match old and new-style configuration entries. + * + * @parameter + */ + private List additionalBuildcommands; + + /** + * List of container classpath entries. By default the <code>org.eclipse.jdt.launching.JRE_CONTAINER</code> + * classpath container is added. Configuration example: + * + * <pre> + * <classpathContainers> + * <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer> + * <classpathContainer>org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v5.5</classpathContainer> + * <classpathContainer>org.eclipse.jst.j2ee.internal.web.container/artifact</classpathContainer> + * </classpathContainers> + * </pre> + * + * @parameter + */ + private List classpathContainers; + + /** + * Enables/disables the downloading of source attachments. Defaults to false. DEPRECATED - use downloadSources + * + * @parameter expression="${eclipse.downloadSources}" + * @deprecated use downloadSources + */ + private boolean eclipseDownloadSources; + + /** + * Eclipse workspace directory. + * + * @parameter expression="${eclipse.projectDir}" alias="outputDir" + */ + private File eclipseProjectDir; + + /** + * When set to false, the plugin will not create sub-projects and instead reference those sub-projects using the + * installed package in the local repository + * + * @parameter expression="${eclipse.useProjectReferences}" default-value="true" + * @required + */ + private boolean useProjectReferences; + + /** + * The default output directory + * + * @parameter expression="${outputDirectory}" alias="outputDirectory" + * default-value="${project.build.outputDirectory}" + * @required + */ + private File buildOutputDirectory; + + /** + * The version of WTP for which configuration files will be generated. The default value is "none" (don't generate + * WTP configuration), supported versions are "R7", "1.0", and "1.5" + * + * @parameter expression="${wtpversion}" default-value="none" + */ + private String wtpversion; + + /** + * JEE context name of the WTP module. ( ex. WEB context name ). + * + * @parameter expression="${wtpContextName}" + */ + private String wtpContextName; + + /** + * The relative path of the manifest file + * + * @parameter expression="${eclipse.manifest}" default-value="${basedir}/META-INF/MANIFEST.MF" + */ + private File manifest; + + /** + * Allow to configure additional generic configuration files for eclipse that will be written out to disk when + * running eclipse:eclipse. FOr each file you can specify the name and the text content. + * + * <pre> + * <plugin> + * <groupId>org.apache.maven.plugins</groupId> + * <artifactId>maven-eclipse-plugin</artifactId> + * <configuration> + * <additionalConfig> + * <file> + * <name>.checkstyle</name> + * <content> + * <![CDATA[<fileset-config file-format-version="1.2.0" simple-config="true"> + * <fileset name="all" enabled="true" check-config-name="acme corporate style" local="false"> + * <file-match-pattern match-pattern="." include-pattern="true"/> + * </fileset> + * <filter name="NonSrcDirs" enabled="true"/> + * </fileset-config>]]> + * </content> + * </file> + * </additionalConfig> + * </configuration> + * </plugin> + * </pre> + * + * Instead of the content you can also define (from version 2.5) an url to download the file : + * + * <pre> + * <plugin> + * <groupId>org.apache.maven.plugins</groupId> + * <artifactId>maven-eclipse-plugin</artifactId> + * <configuration> + * <additionalConfig> + * <file> + * <name>.checkstyle</name> + * <url>http://some.place.org/path/to/file</url> + * </file> + * </additionalConfig> + * </configuration> + * </pre> + * + * or a location : + * + * <pre> + * <plugin> + * <groupId>org.apache.maven.plugins</groupId> + * <artifactId>maven-eclipse-plugin</artifactId> + * <configuration> + * <additionalConfig> + * <file> + * <name>.checkstyle</name> + * <location>/checkstyle-config.xml</location> + * </file> + * </additionalConfig> + * </configuration> + * <dependencies> + * <!-- The file defined in the location is stored in this dependency --> + * <dependency> + * <groupId>eclipsetest</groupId> + * <artifactId>checkstyle-config</artifactId> + * <version>1.0</version> + * </dependency> + * </dependencies> + * </plugin> + * </pre> + * + * @parameter + */ + private EclipseConfigFile[] additionalConfig; + + /** + * If set to <code>true</code>, the version number of the artifact is appended to the name of the generated + * Eclipse project. See projectNameTemplate for other options. + * + * @parameter expression="${eclipse.addVersionToProjectName}" default-value="false" + */ + private boolean addVersionToProjectName; + + /** + * If set to <code>true</code>, the groupId of the artifact is appended to the name of the generated Eclipse + * project. See projectNameTemplate for other options. + * + * @parameter expression="${eclipse.addGroupIdToProjectName}" default-value="false" + */ + private boolean addGroupIdToProjectName; + + /** + * Allows configuring the name of the eclipse projects. This property if set wins over addVersionToProjectName and + * addGroupIdToProjectName You can use <code>[groupId]</code>, <code>[artifactId]</code> and + * <code>[version]</code> variables. eg. <code>[groupId].[artifactId]-[version]</code> + * + * @parameter expression="${eclipse.projectNameTemplate}" + */ + private String projectNameTemplate; + + /** + * Parsed wtp version. + */ + private float wtpVersionFloat; + + /** + * Not a plugin parameter. Is this a java project? + */ + private boolean isJavaProject; + + /** + * Must the manifest files be written for java projects so that that the jee classpath for wtp is correct. + * + * @parameter expression="${eclipse.wtpmanifest}" default-value="false" + */ + private boolean wtpmanifest; + + /** + * Must the application files be written for ear projects in a separate directory. + * + * @parameter expression="${eclipse.wtpapplicationxml}" default-value="false" + */ + private boolean wtpapplicationxml; + + /** + * What WTP defined server to use for deployment informations. + * + * @parameter expression="${eclipse.wtpdefaultserver}" + */ + private String wtpdefaultserver; + + private WorkspaceConfiguration workspaceConfiguration; + + /** + * ResourceManager for getting additonalConfig files from resources + * + * @component + * @required + * @readonly + */ + private ResourceManager locator; + + /** + * This eclipse workspace is read and all artifacts detected there will be connected as eclipse projects and will + * not be linked to the jars in the local repository. Requirement is that it was created with the similar wtp + * settings as the reactor projects, but the project name template my differ. The pom's in the workspace projects + * may not contain variables in the artefactId, groupId and version tags. + * + * @since 2.5 + * @parameter expression="${eclipse.workspace}" + */ + protected String workspace; + + /** + * Limit the use of project references to the current workspace. No project references will be created to projects + * in the reactor when they are not available in the workspace. + * + * @parameter expression="${eclipse.limitProjectReferencesToWorkspace}" default-value="false" + */ + protected boolean limitProjectReferencesToWorkspace; + + protected boolean isJavaProject() + { + return isJavaProject; + } + + protected boolean isPdeProject() + { + return pde; + } + + /** + * Getter for <code>buildcommands</code>. + * + * @return Returns the buildcommands. + */ + public List getBuildcommands() + { + return buildcommands; + } + + /** + * Setter for <code>buildcommands</code>. + * + * @param buildcommands The buildcommands to set. + */ + public void setBuildcommands( List buildcommands ) + { + this.buildcommands = buildcommands; + } + + /** + * Getter for <code>buildOutputDirectory</code>. + * + * @return Returns the buildOutputDirectory. + */ + public File getBuildOutputDirectory() + { + return buildOutputDirectory; + } + + /** + * Setter for <code>buildOutputDirectory</code>. + * + * @param buildOutputDirectory The buildOutputDirectory to set. + */ + public void setBuildOutputDirectory( File buildOutputDirectory ) + { + this.buildOutputDirectory = buildOutputDirectory; + } + + /** + * Getter for <code>classpathContainers</code>. + * + * @return Returns the classpathContainers. + */ + public List getClasspathContainers() + { + return classpathContainers; + } + + /** + * Setter for <code>classpathContainers</code>. + * + * @param classpathContainers The classpathContainers to set. + */ + public void setClasspathContainers( List classpathContainers ) + { + this.classpathContainers = classpathContainers; + } + + /** + * Getter for <code>eclipseProjectDir</code>. + * + * @return Returns the eclipseProjectDir. + */ + public File getEclipseProjectDir() + { + return eclipseProjectDir; + } + + /** + * Setter for <code>eclipseProjectDir</code>. + * + * @param eclipseProjectDir The eclipseProjectDir to set. + */ + public void setEclipseProjectDir( File eclipseProjectDir ) + { + this.eclipseProjectDir = eclipseProjectDir; + } + + /** + * Getter for <code>projectnatures</code>. + * + * @return Returns the projectnatures. + */ + public List getProjectnatures() + { + return projectnatures; + } + + /** + * Setter for <code>projectnatures</code>. + * + * @param projectnatures The projectnatures to set. + */ + public void setProjectnatures( List projectnatures ) + { + this.projectnatures = projectnatures; + } + + /** + * Getter for <code>useProjectReferences</code>. + * + * @return Returns the useProjectReferences. + */ + public boolean getUseProjectReferences() + { + return useProjectReferences; + } + + /** + * Setter for <code>useProjectReferences</code>. + * + * @param useProjectReferences The useProjectReferences to set. + */ + public void setUseProjectReferences( boolean useProjectReferences ) + { + this.useProjectReferences = useProjectReferences; + } + + /** + * Getter for <code>wtpversion</code>. + * + * @return Returns the wtpversion. + */ + public String getWtpversion() + { + return wtpversion; + } + + /** + * Setter for <code>wtpversion</code>. + * + * @param wtpversion The wtpversion to set. + */ + public void setWtpversion( String wtpversion ) + { + this.wtpversion = wtpversion; + } + + /** + * Getter for <code>additionalBuildcommands</code>. + * + * @return Returns the additionalBuildcommands. + */ + public List getAdditionalBuildcommands() + { + return additionalBuildcommands; + } + + /** + * Setter for <code>additionalBuildcommands</code>. + * + * @param additionalBuildcommands The additionalBuildcommands to set. + */ + public void setAdditionalBuildcommands( List additionalBuildcommands ) + { + this.additionalBuildcommands = additionalBuildcommands; + } + + /** + * Getter for <code>additionalProjectnatures</code>. + * + * @return Returns the additionalProjectnatures. + */ + public List getAdditionalProjectnatures() + { + return additionalProjectnatures; + } + + /** + * Setter for <code>additionalProjectnatures</code>. + * + * @param additionalProjectnatures The additionalProjectnatures to set. + */ + public void setAdditionalProjectnatures( List additionalProjectnatures ) + { + this.additionalProjectnatures = additionalProjectnatures; + } + + /** + * Getter for <code>addVersionToProjectName</code>. + */ + public boolean isAddVersionToProjectName() + { + return addVersionToProjectName; + } + + /** + * Setter for <code>addVersionToProjectName</code>. + */ + public void setAddVersionToProjectName( boolean addVersionToProjectName ) + { + this.addVersionToProjectName = addVersionToProjectName; + } + + /** + * Getter for <code>addGroupIdToProjectName</code>. + */ + public boolean isAddGroupIdToProjectName() + { + return addGroupIdToProjectName; + } + + /** + * Setter for <code>addGroupIdToProjectName</code>. + */ + public void setAddGroupIdToProjectName( boolean addGroupIdToProjectName ) + { + this.addGroupIdToProjectName = addGroupIdToProjectName; + } + + public String getProjectNameTemplate() + { + return projectNameTemplate; + } + + public void setProjectNameTemplate( String projectNameTemplate ) + { + this.projectNameTemplate = projectNameTemplate; + } + + /** + * @see org.apache.maven.plugin.Mojo#execute() + */ + public boolean setup() + throws MojoExecutionException + { + boolean ready = true; + + checkDeprecations(); + + ready = validate(); + + // TODO: Why are we using project in some places, and executedProject in others?? + ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler(); + + // ear projects don't contain java sources + // pde projects are always java projects + isJavaProject = + pde || + ( Constants.LANGUAGE_JAVA.equals( artifactHandler.getLanguage() ) && !Constants.PROJECT_PACKAGING_EAR.equals( packaging ) ); + + setupExtras(); + + parseConfigurationOptions(); + + // defaults + if ( projectnatures == null ) + { + fillDefaultNatures( packaging ); + } + + if ( additionalProjectnatures != null ) + { + projectnatures.addAll( additionalProjectnatures ); + } + + if ( buildcommands == null ) + { + fillDefaultBuilders( packaging ); + } + else + { + convertBuildCommandList( buildcommands ); + } + + if ( additionalBuildcommands != null ) + { + convertBuildCommandList( additionalBuildcommands ); + buildcommands.addAll( additionalBuildcommands ); + } + + if ( classpathContainers == null ) + { + fillDefaultClasspathContainers( packaging ); + } + else + { + verifyClasspathContainerListIsComplete(); + } + locator.addSearchPath( FileResourceLoader.ID, project.getFile().getParentFile().getAbsolutePath() ); + locator.setOutputDirectory( new File( project.getBuild().getDirectory() ) ); + + // ready to start + return ready; + } + + protected void convertBuildCommandList( List commands ) + { + if ( commands != null ) + { + for ( ListIterator i = commands.listIterator(); i.hasNext(); ) + { + Object command = i.next(); + + if ( command instanceof String ) + { + command = new BuildCommand( (String) command ); + i.set( command ); + } + } + } + } + + private void parseConfigurationOptions() + { + if ( "R7".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$ + { + wtpVersionFloat = 0.7f; + } + else if ( "1.0".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$ + { + wtpVersionFloat = 1.0f; + } + else if ( "1.5".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$ + { + wtpVersionFloat = 1.5f; + } + else if ( "2.0".equalsIgnoreCase( wtpversion ) ) //$NON-NLS-1$ + { + wtpVersionFloat = 2.0f; + } + if ( !"none".equalsIgnoreCase( wtpversion ) ) + { + getLog().info( Messages.getString( "EclipsePlugin.wtpversion", wtpversion ) ); + } + } + + protected void setupExtras() + throws MojoExecutionException + { + // extension point. + } + + protected void verifyClasspathContainerListIsComplete() + { + boolean containsJREContainer = false; + // Check if classpathContainer contains a JRE (default, alternate or + // Execution Environment) + for ( Iterator iter = classpathContainers.iterator(); iter.hasNext(); ) + { + Object classPathContainer = iter.next(); + if ( classPathContainer != null && + classPathContainer.toString().startsWith( COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER ) ) + { + containsJREContainer = true; + break; + } + } + if ( !containsJREContainer ) + { + getLog().warn( Messages.getString( "EclipsePlugin.missingjrecontainer" ) ); //$NON-NLS-1$ + classpathContainers.add( 0, COMMON_PATH_JDT_LAUNCHING_JRE_CONTAINER ); + } + } + + private boolean validate() + throws MojoExecutionException + { + // validate sanity of the current m2 project + if ( Arrays.binarySearch( WTP_SUPPORTED_VERSIONS, wtpversion ) < 0 ) + { + throw new MojoExecutionException( + Messages.getString( "EclipsePlugin.unsupportedwtp", new Object[] { //$NON-NLS-1$ + wtpversion, + StringUtils.join( WTP_SUPPORTED_VERSIONS, " " ) } ) ); //$NON-NLS-1$ + } + + assertNotEmpty( executedProject.getGroupId(), POM_ELT_GROUP_ID ); + assertNotEmpty( executedProject.getArtifactId(), POM_ELT_ARTIFACT_ID ); + + if ( executedProject.getFile() == null || !executedProject.getFile().exists() ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.missingpom" ) ); //$NON-NLS-1$ + } + + if ( "pom".equals( packaging ) && eclipseProjectDir == null ) //$NON-NLS-1$ + { + getLog().info( Messages.getString( "EclipsePlugin.pompackaging" ) ); //$NON-NLS-1$ + return false; + } + + if ( "eclipse-plugin".equals( packaging ) ) + { + pde = true; + } + + if ( eclipseProjectDir == null ) + { + eclipseProjectDir = executedProject.getFile().getParentFile(); + } + + if ( !eclipseProjectDir.exists() && !eclipseProjectDir.mkdirs() ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantcreatedir", eclipseProjectDir ) ); //$NON-NLS-1$ + } + + if ( !eclipseProjectDir.equals( executedProject.getFile().getParentFile() ) ) + { + if ( !eclipseProjectDir.isDirectory() ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.notadir", eclipseProjectDir ) ); //$NON-NLS-1$ + } + eclipseProjectDir = new File( eclipseProjectDir, executedProject.getArtifactId() ); + if ( !eclipseProjectDir.isDirectory() && !eclipseProjectDir.mkdirs() ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantcreatedir", eclipseProjectDir ) ); //$NON-NLS-1$ + } + } + + validateExtras(); + + return true; + } + + protected void validateExtras() + { + // provided for extension. + } + + private void checkDeprecations() + { + if ( eclipseDownloadSources ) + { + // deprecated warning + getLog().warn( Messages.getString( "EclipsePlugin.deprecatedpar", new Object[] { //$NON-NLS-1$ + "eclipse.downloadSources", //$NON-NLS-1$ + "downloadSources" } ) ); //$NON-NLS-1$ + downloadSources = true; + } + + checkExtraDeprecations(); + } + + protected void checkExtraDeprecations() + { + // provided for extension. + } + + public void writeConfiguration( IdeDependency[] deps ) + throws MojoExecutionException + { + EclipseWriterConfig config = createEclipseWriterConfig( deps ); + + if ( wtpmanifest && isJavaProject() ) + { + EclipseManifestWriter.addManifestResource( getLog(), config ); + } + // NOTE: This could change the config! + writeExtraConfiguration( config ); + + if ( wtpVersionFloat == 0.7f ) + { + new EclipseWtpmodulesWriter().init( getLog(), config ).write(); + } + + if ( wtpVersionFloat >= 1.0f ) + { + new EclipseWtpFacetsWriter().init( getLog(), config ).write(); + } + if ( wtpVersionFloat == 1.0f ) + { + new EclipseWtpComponentWriter().init( getLog(), config ).write(); + } + if ( wtpVersionFloat >= 1.5 ) + { + new EclipseWtpComponent15Writer().init( getLog(), config ).write(); + } + + new EclipseSettingsWriter().init( getLog(), config ).write(); + + if ( isJavaProject ) + { + new EclipseClasspathWriter().init( getLog(), config ).write(); + } + + if ( wtpapplicationxml ) + { + new EclipseWtpApplicationXMLWriter().init( getLog(), config ).write(); + } + + // [rfeng] + /* + if ( pde ) + { + this.getLog().info( "The Maven Eclipse plugin runs in 'pde'-mode." ); + new EclipseOSGiManifestWriter().init( getLog(), config ).write(); + } + */ + // [rfeng] + + // NOTE: This one MUST be after EclipseClasspathwriter, and possibly others, + // since currently EclipseClasspathWriter does some magic to detect nested + // output folders and modifies the configuration by adding new (Ant) builders. + // So the .project file must be written AFTER those have run! + new EclipseProjectWriter().init( getLog(), config ).write(); + + writeAdditionalConfig(); + + getLog().info( Messages.getString( "EclipsePlugin.wrote", new Object[] { //$NON-NLS-1$ + config.getEclipseProjectName(), eclipseProjectDir.getAbsolutePath() } ) ); + } + + protected void writeAdditionalConfig() + throws MojoExecutionException + { + if ( additionalConfig != null ) + { + for ( int j = 0; j < additionalConfig.length; j++ ) + { + EclipseConfigFile file = additionalConfig[j]; + File projectRelativeFile = new File( eclipseProjectDir, file.getName() ); + if ( projectRelativeFile.isDirectory() ) + { + // just ignore? + getLog().warn( Messages.getString( "EclipsePlugin.foundadir", //$NON-NLS-1$ + projectRelativeFile.getAbsolutePath() ) ); + } + + try + { + projectRelativeFile.getParentFile().mkdirs(); + if ( file.getContent() == null ) + { + InputStream inStream; + if ( file.getLocation() != null ) + { + inStream = locator.getResourceAsInputStream( file.getLocation() ); + } + else + { + inStream = file.getURL().openConnection().getInputStream(); + } + OutputStream outStream = new FileOutputStream( projectRelativeFile ); + try + { + IOUtil.copy( inStream, outStream ); + } + finally + { + inStream.close(); + outStream.close(); + } + } + else + { + FileUtils.fileWrite( projectRelativeFile.getAbsolutePath(), file.getContent() ); + } + } + catch ( IOException e ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantwritetofile", //$NON-NLS-1$ + projectRelativeFile.getAbsolutePath() ) ); + } + catch ( ResourceNotFoundException e ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.cantfindresource", //$NON-NLS-1$ + file.getLocation() ) ); + } + + } + } + } + + protected EclipseWriterConfig createEclipseWriterConfig( IdeDependency[] deps ) + throws MojoExecutionException + { + File projectBaseDir = executedProject.getFile().getParentFile(); + + // build a list of UNIQUE source dirs (both src and resources) to be + // used in classpath and wtpmodules + EclipseSourceDir[] sourceDirs = buildDirectoryList( executedProject, eclipseProjectDir, buildOutputDirectory ); + + EclipseWriterConfig config = new EclipseWriterConfig(); + + config.setWorkspaceConfiguration( getWorkspaceConfiguration() ); + + config.setProjectNameTemplate( calculateProjectNameTemplate() ); + + String projectName = IdeUtils.getProjectName( config.getProjectNameTemplate(), project ); + + config.setEclipseProjectName( projectName ); + + config.setWtpapplicationxml( wtpapplicationxml ); + + config.setWtpVersion( wtpVersionFloat ); + + Set convertedBuildCommands = new LinkedHashSet(); + + if ( buildcommands != null ) + { + for ( Iterator it = buildcommands.iterator(); it.hasNext(); ) + { + Object cmd = it.next(); + + if ( cmd instanceof BuildCommand ) + { + convertedBuildCommands.add( cmd ); + } + else + { + convertedBuildCommands.add( new BuildCommand( (String) cmd ) ); + } + } + } + + config.setBuildCommands( new LinkedList( convertedBuildCommands ) ); + + config.setBuildOutputDirectory( buildOutputDirectory ); + config.setClasspathContainers( classpathContainers ); + config.setDeps( deps ); + config.setEclipseProjectDirectory( eclipseProjectDir ); + config.setLocalRepository( localRepository ); + config.setOSGIManifestFile( manifest ); + config.setPde( pde ); + config.setProject( project ); + config.setProjectBaseDir( projectBaseDir ); + config.setProjectnatures( projectnatures ); + config.setProjectFacets( additionalProjectFacets ); + config.setSourceDirs( sourceDirs ); + config.setAddVersionToProjectName( isAddVersionToProjectName() ); + config.setPackaging( packaging ); + + collectWarContextRootsFromReactorEarConfiguration( config ); + + return config; + } + + /** + * If this is a war module peek into the reactor an search for an ear module that defines the context root of this + * module. + * + * @param config config to save the context root. + */ + private void collectWarContextRootsFromReactorEarConfiguration( EclipseWriterConfig config ) + { + if ( reactorProjects != null && wtpContextName == null && + Constants.PROJECT_PACKAGING_WAR.equals( project.getPackaging() ) ) + { + for ( Iterator iter = reactorProjects.iterator(); iter.hasNext(); ) + { + MavenProject reactorProject = (MavenProject) iter.next(); + + if ( Constants.PROJECT_PACKAGING_EAR.equals( reactorProject.getPackaging() ) ) + { + Xpp3Dom[] warDefinitions = + IdeUtils.getPluginConfigurationDom( reactorProject, JeeUtils.ARTIFACT_MAVEN_EAR_PLUGIN, + new String[] { "modules", "webModule" } ); + for ( int index = 0; index < warDefinitions.length; index++ ) + { + Xpp3Dom groupId = warDefinitions[index].getChild( "groupId" ); + Xpp3Dom artifactId = warDefinitions[index].getChild( "artifactId" ); + Xpp3Dom contextRoot = warDefinitions[index].getChild( "contextRoot" ); + if ( groupId != null && artifactId != null && contextRoot != null && + groupId.getValue() != null && artifactId.getValue() != null && + contextRoot.getValue() != null ) + { + getLog().info( + "Found context root definition for " + groupId.getValue() + ":" + + artifactId.getValue() + " " + contextRoot.getValue() ); + if ( project.getArtifactId().equals( artifactId.getValue() ) && + project.getGroupId().equals( groupId.getValue() ) ) + { + config.setContextName( contextRoot.getValue() ); + } + } + else + { + getLog().info( + "Found incomplete ear configuration in " + reactorProject.getGroupId() + + ":" + reactorProject.getGroupId() + " found " + + warDefinitions[index].toString() ); + } + } + } + } + } + if ( config.getContextName() == null && Constants.PROJECT_PACKAGING_WAR.equals( project.getPackaging() ) ) + { + if ( wtpContextName == null ) + { + config.setContextName( project.getArtifactId() ); + } + else + { + config.setContextName( wtpContextName ); + } + } + } + + /** + * Write any extra configuration information for the Eclipse project. This is an extension point, called before the + * main configurations are written. <br/> <b> NOTE: This could change the config! </b> + * + * @param config + * @throws MojoExecutionException + */ + protected void writeExtraConfiguration( EclipseWriterConfig config ) + throws MojoExecutionException + { + // extension point. + } + + private void assertNotEmpty( String string, String elementName ) + throws MojoExecutionException + { + if ( string == null ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.missingelement", elementName ) ); //$NON-NLS-1$ + } + } + + protected void fillDefaultNatures( String packaging ) + { + projectnatures = new ArrayList(); + + if ( wtpVersionFloat >= 1.0f ) + { + projectnatures.add( NATURE_WST_FACET_CORE_NATURE ); // WTP 1.0 nature + } + + if ( isJavaProject ) + { + projectnatures.add( NATURE_JDT_CORE_JAVA ); + } + + if ( wtpVersionFloat >= 0.7f ) + { + projectnatures.add( NATURE_WST_MODULE_CORE_NATURE ); // WTP 0.7/1.0 nature + + if ( isJavaProject ) + { + projectnatures.add( NATURE_JEM_WORKBENCH_JAVA_EMF ); // WTP 0.7/1.0 nature + } + } + + if ( pde ) + { + projectnatures.add( NATURE_PDE_PLUGIN ); + } + + } + + protected void fillDefaultClasspathContainers( String packaging ) + { + classpathContainers = new ArrayList(); + + if ( getWorkspaceConfiguration().getDefaultClasspathContainer() != null ) + { + getLog().info( + "Adding default classpath contaigner: " + + getWorkspaceConfiguration().getDefaultClasspathContainer() ); + classpathContainers.add( getWorkspaceConfiguration().getDefaultClasspathContainer() ); + } + if ( pde ) + { + classpathContainers.add( REQUIRED_PLUGINS_CONTAINER ); + } + } + + protected void fillDefaultBuilders( String packaging ) + { + buildcommands = new ArrayList(); + + if ( wtpVersionFloat == 0.7f ) + { + buildcommands.add( new BuildCommand( BUILDER_WST_COMPONENT_STRUCTURAL ) ); // WTP 0.7 builder + } + + if ( isJavaProject ) + { + buildcommands.add( new BuildCommand( BUILDER_JDT_CORE_JAVA ) ); + } + + if ( wtpVersionFloat >= 1.5f ) + { + buildcommands.add( new BuildCommand( BUILDER_WST_FACET ) ); // WTP 1.5 builder + } + + if ( wtpVersionFloat >= 0.7f ) + { + buildcommands.add( new BuildCommand( BUILDER_WST_VALIDATION ) ); // WTP 0.7/1.0 builder + } + + if ( wtpVersionFloat == 0.7f ) + { + // WTP 0.7 builder + buildcommands.add( new BuildCommand( BUILDER_WST_COMPONENT_STRUCTURAL_DEPENDENCY_RESOLVER ) ); + } + + if ( pde ) + { + buildcommands.add( new BuildCommand( BUILDER_PDE_MANIFEST ) ); + buildcommands.add( new BuildCommand( BUILDER_PDE_SCHEMA ) ); + } + } + + public EclipseSourceDir[] buildDirectoryList( MavenProject project, File basedir, File buildOutputDirectory ) + throws MojoExecutionException + { + File projectBaseDir = project.getFile().getParentFile(); + + // avoid duplicated entries + Set directories = new TreeSet(); + + extractSourceDirs( directories, project.getCompileSourceRoots(), basedir, projectBaseDir, false, null ); + + String relativeOutput = IdeUtils.toRelativeAndFixSeparator( projectBaseDir, buildOutputDirectory, false ); + + extractResourceDirs( directories, project.getBuild().getResources(), project, basedir, projectBaseDir, false, + relativeOutput ); + + // If using the standard output location, don't mix the test output into it. + String testOutput = null; + boolean useStandardOutputDir = + buildOutputDirectory.equals( new File( project.getBuild().getOutputDirectory() ) ); + if ( useStandardOutputDir ) + { + getLog().debug( + "testOutput toRelativeAndFixSeparator " + projectBaseDir + " , " + + project.getBuild().getTestOutputDirectory() ); + testOutput = + IdeUtils.toRelativeAndFixSeparator( projectBaseDir, + new File( project.getBuild().getTestOutputDirectory() ), false ); + getLog().debug( "testOutput after toRelative : " + testOutput ); + } + + extractSourceDirs( directories, project.getTestCompileSourceRoots(), basedir, projectBaseDir, true, testOutput ); + + extractResourceDirs( directories, project.getBuild().getTestResources(), project, basedir, projectBaseDir, + true, testOutput ); + + return (EclipseSourceDir[]) directories.toArray( new EclipseSourceDir[directories.size()] ); + } + + private void extractSourceDirs( Set directories, List sourceRoots, File basedir, File projectBaseDir, boolean test, + String output ) + throws MojoExecutionException + { + for ( Iterator it = sourceRoots.iterator(); it.hasNext(); ) + { + + File sourceRootFile = new File( (String) it.next() ); + + if ( sourceRootFile.isDirectory() ) + { + String sourceRoot = + IdeUtils.toRelativeAndFixSeparator( projectBaseDir, sourceRootFile, + !projectBaseDir.equals( basedir ) ); + + directories.add( new EclipseSourceDir( sourceRoot, output, false, test, null, null, false ) ); + } + } + } + + void extractResourceDirs( Set directories, List resources, MavenProject project, File basedir, + File workspaceProjectBaseDir, boolean test, final String output ) + throws MojoExecutionException + { + for ( Iterator it = resources.iterator(); it.hasNext(); ) + { + Resource resource = (Resource) it.next(); + + getLog().debug( "Processing resource dir: " + resource.getDirectory() ); + + String includePattern = null; + String excludePattern = null; + + if ( resource.getIncludes().size() != 0 ) + { + includePattern = StringUtils.join( resource.getIncludes().iterator(), "|" ); + } + + if ( resource.getExcludes().size() != 0 ) + { + excludePattern = StringUtils.join( resource.getExcludes().iterator(), "|" ); + } + + // TODO: figure out how to merge if the same dir is specified twice + // with different in/exclude patterns. + + File resourceDirectory = new File( /* basedir, */resource.getDirectory() ); + + if ( !resourceDirectory.exists() || !resourceDirectory.isDirectory() ) + { + getLog().debug( "Resource dir: " + resourceDirectory + " either missing or not a directory." ); + continue; + } + + String resourceDir = + IdeUtils.toRelativeAndFixSeparator( workspaceProjectBaseDir, resourceDirectory, + !workspaceProjectBaseDir.equals( basedir ) ); + String thisOutput = output; + if ( thisOutput != null ) + { + // sometimes thisOutput is already an absolute path + File outputFile = new File( thisOutput ); + if ( !outputFile.isAbsolute() ) + { + outputFile = new File( workspaceProjectBaseDir, thisOutput ); + } + // create output dir if it doesn't exist + outputFile.mkdirs(); + + if ( !StringUtils.isEmpty( resource.getTargetPath() ) ) + { + outputFile = new File( outputFile, resource.getTargetPath() ); + // create output dir if it doesn't exist + outputFile.mkdirs(); + } + + getLog().debug( + "Making relative and fixing separator: { " + workspaceProjectBaseDir + ", " + + outputFile + ", false }." ); + thisOutput = IdeUtils.toRelativeAndFixSeparator( workspaceProjectBaseDir, outputFile, false ); + } + + getLog().debug( + "Adding eclipse source dir: { " + resourceDir + ", " + thisOutput + ", true, " + test + + ", " + includePattern + ", " + excludePattern + " }." ); + + directories.add( new EclipseSourceDir( resourceDir, thisOutput, true, test, includePattern, excludePattern, + resource.isFiltering() ) ); + } + } + + /** + * Calculate the project name template from the fields {@link #projectNameTemplate}, + * {@link #addVersionToProjectName} and {@link #addGroupIdToProjectName} + * + * @return the project name template that should be used after considering the plugin configuration + */ + private String calculateProjectNameTemplate() + { + if ( getProjectNameTemplate() != null ) + { + if ( isAddVersionToProjectName() || isAddGroupIdToProjectName() ) + { + getLog().warn( + "projectNameTemplate definition overrides " + + "addVersionToProjectName or addGroupIdToProjectName" ); + } + return getProjectNameTemplate(); + } + else if ( isAddVersionToProjectName() && isAddGroupIdToProjectName() ) + { + return IdeUtils.PROJECT_NAME_WITH_GROUP_AND_VERSION_TEMPLATE; + } + else if ( isAddVersionToProjectName() ) + { + return IdeUtils.PROJECT_NAME_WITH_VERSION_TEMPLATE; + } + else if ( isAddGroupIdToProjectName() ) + { + return IdeUtils.PROJECT_NAME_WITH_GROUP_TEMPLATE; + } + return IdeUtils.PROJECT_NAME_DEFAULT_TEMPLATE; + } + + /** + * {@inheritDoc} + */ + public String getProjectNameForArifact( Artifact artifact ) + { + IdeDependency[] workspaceArtefacts = getWorkspaceArtefacts(); + for ( int index = 0; workspaceArtefacts != null && index < workspaceArtefacts.length; index++ ) + { + IdeDependency workspaceArtefact = workspaceArtefacts[index]; + if ( workspaceArtefact.isAddedToClasspath() && + workspaceArtefact.getGroupId().equals( artifact.getGroupId() ) && + workspaceArtefact.getArtifactId().equals( artifact.getArtifactId() ) ) + { + if ( workspaceArtefact.getVersion().equals( artifact.getVersion() ) ) + { + return workspaceArtefact.getEclipseProjectName(); + } + } + } + return IdeUtils.getProjectName( calculateProjectNameTemplate(), artifact ); + } + + /** + * {@inheritDoc} + */ + protected IdeDependency[] getWorkspaceArtefacts() + { + return getWorkspaceConfiguration().getWorkspaceArtefacts(); + } + + public WorkspaceConfiguration getWorkspaceConfiguration() + { + if ( workspaceConfiguration == null ) + { + workspaceConfiguration = new WorkspaceConfiguration(); + if ( workspace != null ) + { + workspaceConfiguration.setWorkspaceDirectory( new File( workspace ) ); + } + new ReadWorkspaceLocations().init( getLog(), workspaceConfiguration, project, wtpdefaultserver ); + } + return workspaceConfiguration; + } + + public List getExcludes() + { + return excludes; + } + + /** + * Utility method that locates a project in the workspace for the given artifact. + * + * @param artifact the artifact a project should produce. + * @return <code>true</code> if the artifact is produced by a reactor projectart. + */ + private boolean isAvailableAsAWorkspaceProject( Artifact artifact ) + { + IdeDependency[] workspaceArtefacts = getWorkspaceArtefacts(); + for ( int index = 0; workspaceArtefacts != null && index < workspaceArtefacts.length; index++ ) + { + IdeDependency workspaceArtefact = workspaceArtefacts[index]; + if ( workspaceArtefact.getGroupId().equals( artifact.getGroupId() ) && + workspaceArtefact.getArtifactId().equals( artifact.getArtifactId() ) ) + { + if ( workspaceArtefact.getVersion().equals( artifact.getVersion() ) ) + { + workspaceArtefact.setAddedToClasspath( true ); + getLog().debug( "Using workspace project: " + workspaceArtefact.getEclipseProjectName() ); + return true; + } + else + { + getLog().info( + "Artifact " + + artifact.getId() + + " already available as a workspace project, but with different version. Expected: " + + artifact.getVersion() + ", found: " + workspaceArtefact.getVersion() ); + } + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.apache.maven.plugin.ide.AbstractIdeSupportMojo#doDependencyResolution() + */ + protected IdeDependency[] doDependencyResolution() + throws MojoExecutionException + { + + return super.doDependencyResolution(); + } + + /** + * Checks if jar has to be resolved for the given artifact + * + * @param art the artifact to check + * @return true if resolution should happen + */ + protected boolean hasToResolveJar( Artifact art ) + { + return !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) ) || + ( limitProjectReferencesToWorkspace && !( getUseProjectReferences() && isAvailableAsAWorkspaceProject( art ) ) ); + } + + /** + * Checks if a projects reference has to be used for the given artifact + * + * @param art the artifact to check + * @return true if a project reference has to be used. + */ + protected boolean useProjectReference( Artifact art ) + { + boolean isReactorProject = getUseProjectReferences() && isAvailableAsAReactorProject( art ); + boolean isWorkspaceProject = getUseProjectReferences() && isAvailableAsAWorkspaceProject( art ); + return ( isReactorProject && !limitProjectReferencesToWorkspace ) || // default + ( limitProjectReferencesToWorkspace && isWorkspaceProject ) || // limitProjectReferencesToWorkspace + ( !isReactorProject && isWorkspaceProject ); // default + workspace projects + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseProjectWriter.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseProjectWriter.java new file mode 100644 index 0000000000..070b9b3e8b --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/EclipseProjectWriter.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.maven.model.Resource; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.eclipse.BuildCommand; +import org.apache.maven.plugin.eclipse.writers.AbstractEclipseWriter; +import org.apache.maven.plugin.ide.IdeDependency; +import org.apache.maven.plugin.ide.IdeUtils; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; +import org.codehaus.plexus.util.xml.XMLWriter; +import org.codehaus.plexus.util.xml.Xpp3Dom; +import org.codehaus.plexus.util.xml.Xpp3DomBuilder; +import org.codehaus.plexus.util.xml.pull.XmlPullParserException; + +/** + * Writes eclipse .project file. + * + * @author <a href="mailto:trygvis@inamo.no">Trygve Laugstøl</a> + * @author <a href="mailto:kenney@neonics.com">Kenney Westerhof</a> + * @author <a href="mailto:fgiust@apache.org">Fabrizio Giustina</a> + * @version $Id: EclipseProjectWriter.java 616816 2008-01-30 17:23:08Z aheritier $ + */ +public class EclipseProjectWriter + extends AbstractEclipseWriter +{ + private static final String ELT_NAME = "name"; //$NON-NLS-1$ + + private static final String ELT_BUILD_COMMAND = "buildCommand"; //$NON-NLS-1$ + + private static final String ELT_BUILD_SPEC = "buildSpec"; //$NON-NLS-1$ + + private static final String ELT_NATURE = "nature"; //$NON-NLS-1$ + + private static final String ELT_NATURES = "natures"; //$NON-NLS-1$ + + private static final String FILE_DOT_PROJECT = ".project"; //$NON-NLS-1$ + + /** + * Constant for links to files. + */ + private static final int LINK_TYPE_FILE = 1; + + /** + * Constant for links to directories. + */ + private static final int LINK_TYPE_DIRECTORY = 2; + + /** + * @see org.apache.tuscany.sca.maven.plugin.eclipse.writers.EclipseWriter#write() + */ + public void write() + throws MojoExecutionException + { + + Set projectnatures = new LinkedHashSet(); + Set buildCommands = new LinkedHashSet(); + + File dotProject = new File( config.getEclipseProjectDirectory(), FILE_DOT_PROJECT ); + + if ( dotProject.exists() ) + { + + log.info( Messages.getString( "EclipsePlugin.keepexisting", dotProject.getAbsolutePath() ) ); //$NON-NLS-1$ + + // parse existing file in order to keep manually-added entries + Reader reader = null; + try + { + reader = new InputStreamReader( new FileInputStream( dotProject ), "UTF-8" ); + Xpp3Dom dom = Xpp3DomBuilder.build( reader ); + + Xpp3Dom naturesElement = dom.getChild( ELT_NATURES ); + if ( naturesElement != null ) + { + Xpp3Dom[] existingNatures = naturesElement.getChildren( ELT_NATURE ); + for ( int j = 0; j < existingNatures.length; j++ ) + { + // adds all the existing natures + projectnatures.add( existingNatures[j].getValue() ); + } + } + + Xpp3Dom buildSpec = dom.getChild( ELT_BUILD_SPEC ); + if ( buildSpec != null ) + { + Xpp3Dom[] existingBuildCommands = buildSpec.getChildren( ELT_BUILD_COMMAND ); + for ( int j = 0; j < existingBuildCommands.length; j++ ) + { + Xpp3Dom buildCommandName = existingBuildCommands[j].getChild( ELT_NAME ); + if ( buildCommandName != null ) + { + buildCommands.add( new BuildCommand( existingBuildCommands[j] ) ); + } + } + } + } + catch ( XmlPullParserException e ) + { + log.warn( Messages.getString( "EclipsePlugin.cantparseexisting", dotProject.getAbsolutePath() ) ); //$NON-NLS-1$ + } + catch ( IOException e ) + { + log.warn( Messages.getString( "EclipsePlugin.cantparseexisting", dotProject.getAbsolutePath() ) ); //$NON-NLS-1$ + } + finally + { + IOUtil.close( reader ); + } + } + + // adds new entries after the existing ones + for ( Iterator iter = config.getProjectnatures().iterator(); iter.hasNext(); ) + { + projectnatures.add( iter.next() ); + } + + for ( Iterator iter = config.getBuildCommands().iterator(); iter.hasNext(); ) + { + buildCommands.add( (BuildCommand) iter.next() ); + } + + Writer w; + + try + { + w = new OutputStreamWriter( new FileOutputStream( dotProject ), "UTF-8" ); + } + catch ( IOException ex ) + { + throw new MojoExecutionException( Messages.getString( "EclipsePlugin.erroropeningfile" ), ex ); //$NON-NLS-1$ + } + + XMLWriter writer = new PrettyPrintXMLWriter( w ); + + writer.startElement( "projectDescription" ); //$NON-NLS-1$ + + writer.startElement( ELT_NAME ); + writer.writeText( config.getEclipseProjectName() ); + writer.endElement(); + + // TODO: this entire element might be dropped if the comment is null. + // but as the maven1 eclipse plugin does it, it's better to be safe than sorry + // A eclipse developer might want to look at this. + writer.startElement( "comment" ); //$NON-NLS-1$ + + if ( config.getProject().getDescription() != null ) + { + writer.writeText( config.getProject().getDescription() ); + } + + writer.endElement(); + + writer.startElement( "projects" ); //$NON-NLS-1$ + + // referenced projects should not be added for plugins + if ( !config.isPde() ) + { + for ( int j = 0; j < config.getDepsOrdered().length; j++ ) + { + IdeDependency dep = config.getDepsOrdered()[j]; + if ( dep.isReferencedProject() ) + { + writer.startElement( "project" ); //$NON-NLS-1$ + writer.writeText( dep.getEclipseProjectName() ); + writer.endElement(); + } + } + } + + writer.endElement(); // projects + + writer.startElement( ELT_BUILD_SPEC ); + + for ( Iterator it = buildCommands.iterator(); it.hasNext(); ) + { + ( (BuildCommand) it.next() ).print( writer ); + } + + writer.endElement(); // buildSpec + + writer.startElement( ELT_NATURES ); + + for ( Iterator it = projectnatures.iterator(); it.hasNext(); ) + { + writer.startElement( ELT_NATURE ); + writer.writeText( (String) it.next() ); + writer.endElement(); // name + } + + writer.endElement(); // natures + + /* + boolean addLinks = !config.getProjectBaseDir().equals( config.getEclipseProjectDirectory() ); + + if ( addLinks || ( config.isPde() && config.getDepsOrdered().length > 0 ) ) + { + writer.startElement( "linkedResources" ); //$NON-NLS-1$ + + if ( addLinks ) + { + + addFileLink( writer, config.getProjectBaseDir(), config.getEclipseProjectDirectory(), + config.getProject().getFile() ); + + addSourceLinks( writer, config.getProjectBaseDir(), config.getEclipseProjectDirectory(), + config.getProject().getCompileSourceRoots() ); + addResourceLinks( writer, config.getProjectBaseDir(), config.getEclipseProjectDirectory(), + config.getProject().getBuild().getResources() ); + + addSourceLinks( writer, config.getProjectBaseDir(), config.getEclipseProjectDirectory(), + config.getProject().getTestCompileSourceRoots() ); + addResourceLinks( writer, config.getProjectBaseDir(), config.getEclipseProjectDirectory(), + config.getProject().getBuild().getTestResources() ); + + } + + if ( config.isPde() ) + { + for ( int j = 0; j < config.getDepsOrdered().length; j++ ) + { + IdeDependency dep = config.getDepsOrdered()[j]; + + if ( dep.isAddedToClasspath() && !dep.isProvided() && !dep.isReferencedProject() && + !dep.isTestDependency() && !dep.isOsgiBundle() ) + { + String name = dep.getFile().getName(); + addLink( writer, name, StringUtils.replace( IdeUtils.getCanonicalPath( dep.getFile() ), "\\", + "/" ), LINK_TYPE_FILE ); + } + } + } + + writer.endElement(); // linkedResources + } + */ + + writer.endElement(); // projectDescription + + IOUtil.close( w ); + } + + private void addFileLink( XMLWriter writer, File projectBaseDir, File basedir, File file ) + throws MojoExecutionException + { + if ( file.isFile() ) + { + String name = IdeUtils.toRelativeAndFixSeparator( projectBaseDir, file, true ); + String location = IdeUtils.getCanonicalPath( file ).replaceAll( "\\\\", "/" ); //$NON-NLS-1$ //$NON-NLS-2$ + + addLink( writer, name, location, LINK_TYPE_FILE ); + } + else + { + log.warn( Messages.getString( "EclipseProjectWriter.notafile", file ) ); //$NON-NLS-1$ + } + } + + private void addSourceLinks( XMLWriter writer, File projectBaseDir, File basedir, List sourceRoots ) + throws MojoExecutionException + { + for ( Iterator it = sourceRoots.iterator(); it.hasNext(); ) + { + String sourceRootString = (String) it.next(); + File sourceRoot = new File( sourceRootString ); + + if ( sourceRoot.isDirectory() ) + { + String name = IdeUtils.toRelativeAndFixSeparator( projectBaseDir, sourceRoot, true ); + String location = IdeUtils.getCanonicalPath( sourceRoot ).replaceAll( "\\\\", "/" ); //$NON-NLS-1$ //$NON-NLS-2$ + + addLink( writer, name, location, LINK_TYPE_DIRECTORY ); + } + } + } + + private void addResourceLinks( XMLWriter writer, File projectBaseDir, File basedir, List sourceRoots ) + throws MojoExecutionException + { + for ( Iterator it = sourceRoots.iterator(); it.hasNext(); ) + { + String resourceDirString = ( (Resource) it.next() ).getDirectory(); + File resourceDir = new File( resourceDirString ); + + if ( resourceDir.isDirectory() ) + { + String name = IdeUtils.toRelativeAndFixSeparator( projectBaseDir, resourceDir, true ); + String location = IdeUtils.getCanonicalPath( resourceDir ).replaceAll( "\\\\", "/" ); //$NON-NLS-1$ //$NON-NLS-2$ + + addLink( writer, name, location, LINK_TYPE_DIRECTORY ); + } + } + } + + /** + * @param writer + * @param name + * @param location + */ + private void addLink( XMLWriter writer, String name, String location, int type ) + { + writer.startElement( "link" ); //$NON-NLS-1$ + + writer.startElement( ELT_NAME ); + writer.writeText( name ); + writer.endElement(); // name + + writer.startElement( "type" ); //$NON-NLS-1$ + writer.writeText( Integer.toString( type ) ); + writer.endElement(); // type + + writer.startElement( "location" ); //$NON-NLS-1$ + + writer.writeText( location ); + + writer.endElement(); // location + + writer.endElement(); // link + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/Messages.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/Messages.java new file mode 100644 index 0000000000..553cae6256 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/maven/plugin/eclipse/Messages.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.maven.plugin.eclipse; + +import java.text.MessageFormat; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * @author <a href="mailto:fgiust@users.sourceforge.net">Fabrizio Giustina</a> + * @version $Id: Messages.java 485864 2006-12-11 20:41:36Z fgiust $ + */ +public class Messages +{ + + private static final String BUNDLE_NAME = "org.apache.tuscany.sca.maven.plugin.eclipse.messages"; //$NON-NLS-1$ + + private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle.getBundle( BUNDLE_NAME ); + + private Messages() + { + } + + public static String getString( String key ) + { + try + { + return RESOURCE_BUNDLE.getString( key ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + public static String getString( String key, Object[] params ) + { + try + { + return MessageFormat.format( RESOURCE_BUNDLE.getString( key ), params ); + } + catch ( MissingResourceException e ) + { + return '!' + key + '!'; + } + } + + public static String getString( String key, Object param ) + { + return getString( key, new Object[] { param } ); + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactAggregation.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactAggregation.java new file mode 100644 index 0000000000..062c935ad9 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactAggregation.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.tools.bundle.plugin; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.artifact.Artifact; + + +public class ArtifactAggregation { + private String symbolicName; + private String version; + private List<ArtifactMember> artifactMemebers = new ArrayList<ArtifactMember>(); + private transient List<Artifact> artifacts = new ArrayList<Artifact>(); + + public List<Artifact> getArtifacts() { + return artifacts; + } + + public String getSymbolicName() { + return symbolicName; + } + + public void setSymbolicName(String symbolicName) { + this.symbolicName = symbolicName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public List<ArtifactMember> getArtifactMembers() { + return artifactMemebers; + } + + public void setArtifactMembers(List<ArtifactMember> artifacts) { + this.artifactMemebers = artifacts; + } + + public String toString() { + return symbolicName + ";version=\"" + version + "\"\n" + artifactMemebers; + } + + public boolean matches(Artifact artifact) { + for(ArtifactMember m: artifactMemebers) { + if(m.matches(artifact)) { + return true; + } + } + return false; + } +}
\ No newline at end of file diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactMember.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactMember.java new file mode 100644 index 0000000000..19315aa19f --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ArtifactMember.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.tools.bundle.plugin; + +import org.apache.maven.artifact.Artifact; + +public class ArtifactMember { + private String groupId; + private String artifactId; + private String version; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String toString() { + return groupId + ":" + artifactId + ":" + version; + } + + public boolean matches(Artifact artifact) { + return groupId.equals(artifact.getGroupId()) && (artifactId == null || artifactId.equals("") + || artifactId.equals("*") || artifactId.equals(artifact.getArtifactId())) + && (version == null || version.equals("") || version.equals("*") || version.equals(artifact.getVersion())); + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/BundleUtil.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/BundleUtil.java new file mode 100644 index 0000000000..f104225483 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/BundleUtil.java @@ -0,0 +1,387 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.tools.bundle.plugin; + +import static org.osgi.framework.Constants.BUNDLE_CLASSPATH; +import static org.osgi.framework.Constants.BUNDLE_MANIFESTVERSION; +import static org.osgi.framework.Constants.BUNDLE_NAME; +import static org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME; +import static org.osgi.framework.Constants.BUNDLE_VERSION; +import static org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE; +import static org.osgi.framework.Constants.EXPORT_PACKAGE; +import static org.osgi.framework.Constants.IMPORT_PACKAGE; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.DefaultArtifactVersion; +import org.osgi.framework.Version; + +/** + * Common functions used by the plugin. + * + * @version $Rev$ $Date$ + */ +final class BundleUtil { + + /** + * Returns the name of a bundle, or null if the given file is not a bundle. + * + * @param file + * @return + * @throws IOException + */ + static String getBundleSymbolicName(File file) throws IOException { + if (!file.exists()) { + return null; + } + String bundleName = null; + if (file.isDirectory()) { + File mf = new File(file, "META-INF/MANIFEST.MF"); + if (mf.isFile()) { + Manifest manifest = new Manifest(new FileInputStream(mf)); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + } + } else { + JarFile jar = new JarFile(file, false); + Manifest manifest = jar.getManifest(); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + jar.close(); + } + if (bundleName == null) { + return bundleName; + } + int sc = bundleName.indexOf(';'); + if (sc != -1) { + bundleName = bundleName.substring(0, sc); + } + return bundleName; + } + + /** + * Generate a Bundle manifest for a set of JAR files. + * + * @param jarFiles + * @param name + * @param symbolicName + * @param version + * @param dir + * @return + * @throws IllegalStateException + */ + static Manifest libraryManifest(Set<File> jarFiles, String name, String symbolicName, String version, String dir) + throws IllegalStateException { + try { + + // List exported packages and bundle classpath entries + StringBuffer classpath = new StringBuffer(); + Set<String> exportedPackages = new HashSet<String>(); + for (File jarFile : jarFiles) { + addPackages(jarFile, exportedPackages, version); + if (dir != null) { + classpath.append(dir).append("/"); + } + classpath.append(jarFile.getName()); + classpath.append(","); + } + + // Generate export-package and import-package declarations + StringBuffer exports = new StringBuffer(); + StringBuffer imports = new StringBuffer(); + Set<String> importedPackages = new HashSet<String>(); + for (String export : exportedPackages) { + + // Add export declaration + exports.append(export); + exports.append(','); + + // Add corresponding import declaration + String packageName = packageName(export); + if (!importedPackages.contains(packageName)) { + importedPackages.add(packageName); + imports.append(packageName); + imports.append(','); + } + } + + // Create a manifest + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue("Manifest-Version", "1.0"); + attributes.putValue(BUNDLE_MANIFESTVERSION, "2"); + attributes.putValue(BUNDLE_SYMBOLICNAME, symbolicName); + attributes.putValue(BUNDLE_NAME, name); + attributes.putValue(BUNDLE_VERSION, version); + attributes.putValue(DYNAMICIMPORT_PACKAGE, "*"); + if (exports.length() > 1) { + attributes.putValue(EXPORT_PACKAGE, exports.substring(0, exports.length() - 1)); + } + if (imports.length() > 1) { + attributes.putValue(IMPORT_PACKAGE, imports.substring(0, imports.length() - 1)); + } + if (classpath.length() > 1) { + attributes.putValue(BUNDLE_CLASSPATH, classpath.substring(0, classpath.length() - 1)); + } + + return manifest; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Write a bundle manifest. + * + * @param manifest + * @param out + * @throws IOException + */ + static void write(Manifest manifest, OutputStream out) throws IOException { + DataOutputStream dos = new DataOutputStream(out); + Attributes attributes = manifest.getMainAttributes(); + write(attributes, "Manifest-Version", dos); + write(attributes, BUNDLE_MANIFESTVERSION, dos); + write(attributes, BUNDLE_SYMBOLICNAME, dos); + write(attributes, BUNDLE_NAME, dos); + write(attributes, BUNDLE_VERSION, dos); + write(attributes, DYNAMICIMPORT_PACKAGE, dos); + write(attributes, BUNDLE_CLASSPATH, dos); + write(attributes, IMPORT_PACKAGE, dos); + write(attributes, EXPORT_PACKAGE, dos); + dos.flush(); + } + + /** + * Add packages to be exported out of a JAR file. + * + * @param jarFile + * @param packages + * @throws IOException + */ + private static void addPackages(File jarFile, Set<String> packages, String version) throws IOException { + if (getBundleSymbolicName(jarFile) == null) { + String ver = ";version=" + version; + addAllPackages(jarFile, packages, ver); + } else { + addExportedPackages(jarFile, packages); + } + } + + /** + * Write manifest attributes. + * + * @param attributes + * @param key + * @param dos + * @throws IOException + */ + private static void write(Attributes attributes, String key, DataOutputStream dos) throws IOException { + String value = attributes.getValue(key); + if (value == null) { + return; + } + StringBuffer line = new StringBuffer(); + line.append(key); + line.append(": "); + line.append(new String(value.getBytes("UTF8"))); + line.append("\r\n"); + int l = line.length(); + if (l > 72) { + for (int i = 70; i < l - 2;) { + line.insert(i, "\r\n "); + i += 72; + l += 3; + } + } + dos.writeBytes(line.toString()); + } + + /** + * Strip an OSGi export, only retain the package name and version. + * + * @param export + * @return + */ + private static String stripExport(String export) { + int sc = export.indexOf(';'); + if (sc == -1) { + return export; + } + String base = export.substring(0, sc); + int v = export.indexOf("version="); + if (v != -1) { + sc = export.indexOf(';', v + 1); + if (sc != -1) { + return base + ";" + export.substring(v, sc); + } else { + return base + ";" + export.substring(v); + } + } else { + return base; + } + } + + /** + * Add all the packages out of a JAR. + * + * @param jarFile + * @param packages + * @param version + * @throws IOException + */ + private static void addAllPackages(File jarFile, Set<String> packages, String version) throws IOException { + ZipInputStream is = new ZipInputStream(new FileInputStream(jarFile)); + ZipEntry entry; + while ((entry = is.getNextEntry()) != null) { + String entryName = entry.getName(); + if (!entry.isDirectory() && entryName != null + && entryName.length() > 0 + && !entryName.startsWith(".") + && entryName.endsWith(".class") // Exclude resources from Export-Package + && entryName.lastIndexOf("/") > 0 + && Character.isJavaIdentifierStart(entryName.charAt(0))) { + String pkg = entryName.substring(0, entryName.lastIndexOf("/")).replace('/', '.'); + if (!pkg.endsWith(".enum")) { + packages.add(pkg + version); + } + } + } + is.close(); + } + + /** + * Returns the name of the exported package in the given export. + * @param export + * @return + */ + private static String packageName(String export) { + int sc = export.indexOf(';'); + if (sc != -1) { + export = export.substring(0, sc); + } + return export; + } + + /** + * Add the packages exported by a bundle. + * + * @param file + * @param packages + * @return + * @throws IOException + */ + private static void addExportedPackages(File file, Set<String> packages) throws IOException { + if (!file.exists()) { + return; + } + + // Read the export-package declaration and get a list of the packages available in a JAR + Set<String> existingPackages = null; + String exports = null; + if (file.isDirectory()) { + File mf = new File(file, "META-INF/MANIFEST.MF"); + if (mf.isFile()) { + Manifest manifest = new Manifest(new FileInputStream(mf)); + exports = manifest.getMainAttributes().getValue(EXPORT_PACKAGE); + } + } else { + JarFile jar = new JarFile(file, false); + Manifest manifest = jar.getManifest(); + exports = manifest.getMainAttributes().getValue(EXPORT_PACKAGE); + jar.close(); + existingPackages = new HashSet<String>(); + addAllPackages(file, existingPackages, ""); + } + if (exports == null) { + return; + } + + // Parse the export-package declaration, and extract the individual packages + StringBuffer buffer = new StringBuffer(); + boolean q = false; + for (int i = 0, n = exports.length(); i < n; i++) { + char c = exports.charAt(i); + if (c == '\"') { + q = !q; + } + if (!q) { + if (c == ',') { + + // Add the exported package to the set, after making sure it really exists in + // the JAR + String export = buffer.toString(); + if (existingPackages == null || existingPackages.contains(packageName(export))) { + packages.add(stripExport(export)); + } + buffer = new StringBuffer(); + continue; + } + } + buffer.append(c); + } + if (buffer.length() != 0) { + + // Add the exported package to the set, after making sure it really exists in + // the JAR + String export = buffer.toString(); + if (existingPackages == null || existingPackages.contains(packageName(export))) { + packages.add(stripExport(export)); + } + } + } + + /** + * Convert the maven version into OSGi version + * @param mavenVersion + * @return + */ + static String osgiVersion(String mavenVersion) { + ArtifactVersion ver = new DefaultArtifactVersion(mavenVersion); + String qualifer = ver.getQualifier(); + if (qualifer != null) { + StringBuffer buf = new StringBuffer(qualifer); + for (int i = 0; i < buf.length(); i++) { + char c = buf.charAt(i); + if (Character.isLetterOrDigit(c) || c == '-' || c == '_') { + // Keep as-is + } else { + buf.setCharAt(i, '_'); + } + } + qualifer = buf.toString(); + } + Version osgiVersion = + new Version(ver.getMajorVersion(), ver.getMinorVersion(), ver.getIncrementalVersion(), qualifer); + String version = osgiVersion.toString(); + return version; + } + +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ModuleBundlesBuildMojo.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ModuleBundlesBuildMojo.java new file mode 100644 index 0000000000..de03d0d6d2 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ModuleBundlesBuildMojo.java @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.tools.bundle.plugin; + +import static org.apache.tuscany.sca.tools.bundle.plugin.BundleUtil.write; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.Manifest; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; + +/** + * A maven plugin that generates a modules directory containing OSGi bundles for all the project's module dependencies. + * + * @version $Rev$ $Date$ + * @goal generate-modules + * @phase generate-resources + * @requiresDependencyResolution test + * @description Generate a modules directory containing OSGi bundles for all the project's module dependencies. + */ +public class ModuleBundlesBuildMojo extends AbstractMojo { + + /** + * The project to create a distribution for. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * Target directory. + * + * @parameter + */ + private File targetDirectory; + + /** + * Directories containing artifacts to exclude. + * + * @parameter + */ + private File[] excludeDirectories; + + /** + * Directories containing groupids to exclude. + * + * @parameter + */ + private String[] excludeGroupIds; + + /** + * Directories containing groupids to include. + * + * @parameter + */ + private String[] includeGroupIds; + + /** + * Set to true to generate a PDE target platform configuration. + * + * @parameter + */ + private boolean generateTargetPlatform; + + /** + * A list of Eclipse features to be added to the target definition + * @parameter + */ + private String[] eclipseFeatures; + + /** + * Set to true to generate a plugin.xml. + * + * @parameter + */ + private boolean generatePlugin; + + /** + * @parameter + */ + private ArtifactAggregation[] artifactAggregations; + + public void execute() throws MojoExecutionException { + Log log = getLog(); + + try { + + // Create the target directory + File root; + if (targetDirectory == null) { + root = new File(project.getBuild().getDirectory(), "plugins/"); + } else { + root = targetDirectory; + } + root.mkdirs(); + + // Build sets of exclude directories and included/excluded/groupids + Set<String> excludedFileNames = new HashSet<String>(); + if (excludeDirectories != null) { + for (File f : excludeDirectories) { + if (f.isDirectory()) { + for (String n : f.list()) { + excludedFileNames.add(n); + } + } + } + } + Set<String> includedGroupIds = new HashSet<String>(); + if (includeGroupIds != null) { + for (String g : includeGroupIds) { + includedGroupIds.add(g); + } + } + Set<String> excludedGroupIds = new HashSet<String>(); + if (excludeGroupIds != null) { + for (String g : excludeGroupIds) { + excludedGroupIds.add(g); + } + } + + // Process all the dependency artifacts + Set<String> bundleSymbolicNames = new HashSet<String>(); + for (Object o : project.getArtifacts()) { + Artifact artifact = (Artifact)o; + + // Only consider Compile and Runtime dependencies + if (!(Artifact.SCOPE_COMPILE.equals(artifact.getScope()) || Artifact.SCOPE_RUNTIME.equals(artifact + .getScope()) + || Artifact.SCOPE_PROVIDED.equals(artifact.getScope()) || (generateTargetPlatform && Artifact.SCOPE_TEST + .equals(artifact.getScope())))) { + log.info("Skipping artifact: " + artifact); + continue; + } + + // Only consider JAR and WAR files + if (!"jar".equals(artifact.getType()) && !"war".equals(artifact.getType())) { + continue; + } + + // Exclude artifact if its groupId is excluded or if it's not included + if (excludedGroupIds.contains(artifact.getGroupId())) { + log.debug("Artifact groupId is excluded: " + artifact); + continue; + } + if (!includedGroupIds.isEmpty()) { + if (!includedGroupIds.contains(artifact.getGroupId())) { + log.debug("Artifact groupId is not included: " + artifact); + continue; + } + } + + File artifactFile = artifact.getFile(); + if (!artifactFile.exists()) { + log.warn("Artifact doesn't exist: " + artifact); + continue; + } + + if (log.isDebugEnabled()) { + log.debug("Processing artifact: " + artifact); + } + + // Get the bundle name if the artifact is an OSGi bundle + String bundleName = null; + try { + bundleName = BundleUtil.getBundleSymbolicName(artifact.getFile()); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + + if (bundleName != null) { + + // Exclude artifact if its file name is excluded + if (excludedFileNames.contains(artifactFile.getName())) { + log.debug("Artifact file is excluded: " + artifact); + continue; + } + + // Copy an OSGi bundle as is + log.info("Adding OSGi bundle artifact: " + artifact); + copyFile(artifactFile, root); + bundleSymbolicNames.add(bundleName); + + } else if ("war".equals(artifact.getType())) { + + // Exclude artifact if its file name is excluded + if (excludedFileNames.contains(artifactFile.getName())) { + log.debug("Artifact file is excluded: " + artifact); + continue; + } + + // Copy a WAR as is + log.info("Adding WAR artifact: " + artifact); + copyFile(artifactFile, root); + + } else { + + File dir = new File(root, artifactFile.getName().substring(0, artifactFile.getName().length() - 4)); + + // Exclude artifact if its file name is excluded + if (excludedFileNames.contains(dir.getName())) { + log.debug("Artifact file is excluded: " + artifact); + continue; + } + + if (artifactAggregations != null) { + boolean aggregated = false; + for (ArtifactAggregation group : artifactAggregations) { + if (group.matches(artifact)) { + group.getArtifacts().add(artifact); + aggregated = true; + break; + } + } + if (aggregated) { + continue; + } + } + + // Create a bundle directory for a non-OSGi JAR + log.info("Adding JAR artifact: " + artifact); + String version = BundleUtil.osgiVersion(artifact.getVersion()); + + Set<File> jarFiles = new HashSet<File>(); + jarFiles.add(artifactFile); + String symbolicName = (artifact.getGroupId() + "." + artifact.getArtifactId()); + Manifest mf = BundleUtil.libraryManifest(jarFiles, symbolicName, symbolicName, version, null); + File file = new File(dir, "META-INF"); + file.mkdirs(); + file = new File(file, "MANIFEST.MF"); + + FileOutputStream fos = new FileOutputStream(file); + write(mf, fos); + fos.close(); + copyFile(artifactFile, dir); + bundleSymbolicNames.add(symbolicName); + } + } + + if (artifactAggregations != null) { + for (ArtifactAggregation group : artifactAggregations) { + if (group.getArtifacts().isEmpty()) { + continue; + } + String symbolicName = group.getSymbolicName(); + String version = group.getVersion(); + File dir = new File(root, symbolicName + "-" + version); + dir.mkdir(); + Set<File> jarFiles = new HashSet<File>(); + for (Artifact a : group.getArtifacts()) { + log.info("Aggragating JAR artifact: " + a); + jarFiles.add(a.getFile()); + copyFile(a.getFile(), dir); + } + Manifest mf = BundleUtil.libraryManifest(jarFiles, symbolicName, symbolicName, version, null); + File file = new File(dir, "META-INF"); + file.mkdirs(); + file = new File(file, "MANIFEST.MF"); + + FileOutputStream fos = new FileOutputStream(file); + write(mf, fos); + fos.close(); + bundleSymbolicNames.add(symbolicName); + } + } + + // Generate a PDE target + if (generateTargetPlatform) { + File target = new File(project.getBuild().getDirectory(), project.getArtifactId() + ".target"); + FileOutputStream targetFile = new FileOutputStream(target); + writeTarget(new PrintStream(targetFile), bundleSymbolicNames, eclipseFeatures); + targetFile.close(); + } + + // Generate a plugin.xml referencing the PDE target + if (generatePlugin) { + File pluginxml = new File(project.getBasedir(), "plugin.xml"); + FileOutputStream pluginXMLFile = new FileOutputStream(pluginxml); + writePluginXML(new PrintStream(pluginXMLFile)); + pluginXMLFile.close(); + } + + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + + } + + private static void copyFile(File jar, File dir) throws FileNotFoundException, IOException { + byte[] buf = new byte[4096]; + File jarFile = new File(dir, jar.getName()); + FileInputStream in = new FileInputStream(jar); + FileOutputStream out = new FileOutputStream(jarFile); + for (;;) { + int len = in.read(buf); + if (len > 0) { + out.write(buf, 0, len); + } else { + break; + } + } + in.close(); + out.close(); + } + + private void writeTarget(PrintStream ps, Set<String> ids, String[] features) { + ps.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + ps.println("<?pde version=\"3.2\"?>"); + + ps.println("<target name=\"Eclipse Target - " + project.getArtifactId() + "\">"); + + ps.println(" <targetJRE>"); + ps.println(" <execEnv>J2SE-1.5</execEnv>"); + ps.println(" </targetJRE>"); + + ps.println(" <location useDefault=\"true\"/>"); + + // ps.println("<content useAllPlugins=\"true\">"); + ps.println(" <content>"); + ps.println(" <plugins>"); + for (String id : ids) { + ps.println(" <plugin id=\"" + id + "\"/>"); + } + ps.println(" </plugins>"); + ps.println(" <features>"); + if (features != null) { + for (String f : features) { + ps.println(" <feature id=\"" + f + "\"/>"); + } + } + ps.println(" </features>"); + ps.println(" <extraLocations>"); + // Not sure why the extra path needs to the plugins folder + ps.println(" <location path=\"" + targetDirectory + "\"/>"); + ps.println(" </extraLocations>"); + ps.println(" </content>"); + + ps.println("</target>"); + + } + + private static void writePluginXML(PrintStream ps) { + ps.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + ps.println("<?pde version=\"3.2\"?>"); + ps.println("<plugin>"); + ps.println("<extension point = \"org.eclipse.pde.core.targets\">"); + ps.println("<target"); + ps.println("id=\"org.apache.tuscany.sca.target\""); + ps.println("name=\"Apache Tuscany Eclipse Target\""); + ps.println("path=\"tuscany.target\"/>"); + ps.println("</extension>"); + ps.println("</plugin>"); + } +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ThirdPartyBundleBuildMojo.java b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ThirdPartyBundleBuildMojo.java new file mode 100644 index 0000000000..e183e7e197 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/java/org/apache/tuscany/sca/tools/bundle/plugin/ThirdPartyBundleBuildMojo.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.tools.bundle.plugin; + +import static org.apache.tuscany.sca.tools.bundle.plugin.BundleUtil.write; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.Manifest; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.project.MavenProject; + +/** + * A Maven plugin that builds an OSGi bundle for the project's third-party dependencies. + * + * @version $Rev$ $Date$ + * @goal assemble-thirdparty-bundle + * @phase generate-resources + * @requiresDependencyResolution test + * @description Build an OSGi bundle for the project's third party dependencies + */ +public class ThirdPartyBundleBuildMojo extends AbstractMojo { + + /** + * The project to build the bundle for. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * The bundle symbolic name + * + * @parameter + */ + private String symbolicName; + + public void execute() throws MojoExecutionException { + Log log = getLog(); + + String projectGroupId = project.getGroupId(); + Set<File> jarFiles = new HashSet<File>(); + for (Object o : project.getArtifacts()) { + Artifact artifact = (Artifact)o; + + if (!(Artifact.SCOPE_COMPILE.equals(artifact.getScope()) || Artifact.SCOPE_RUNTIME.equals(artifact + .getScope()))) { + if (log.isDebugEnabled()) { + log.debug("Skipping artifact: " + artifact); + } + continue; + } + if (!"jar".equals(artifact.getType())) { + continue; + } + if (projectGroupId.equals(artifact.getGroupId())) { + continue; + } + + if (log.isDebugEnabled()) { + log.debug("Artifact: " + artifact); + } + String bundleName = null; + try { + bundleName = BundleUtil.getBundleSymbolicName(artifact.getFile()); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + if (bundleName == null || true) { + if (artifact.getFile().exists()) { + log.info("Adding third party jar: " + artifact); + jarFiles.add(artifact.getFile()); + } else { + log.warn("Third party jar not found: " + artifact); + } + } + } + + try { + String version = BundleUtil.osgiVersion(project.getVersion()); + + Manifest mf = BundleUtil.libraryManifest(jarFiles, project.getName(), symbolicName, version, "lib"); + File file = new File(project.getBasedir(), "META-INF"); + file.mkdir(); + file = new File(file, "MANIFEST.MF"); + if (log.isDebugEnabled()) { + log.debug("Generating " + file); + } + + FileOutputStream fos = new FileOutputStream(file); + write(mf, fos); + fos.close(); + + File lib = new File(project.getBasedir(), "lib"); + if (lib.isDirectory()) { + for (File c : lib.listFiles()) { + c.delete(); + } + } + lib.mkdir(); + byte[] buf = new byte[4096]; + for (File jar : jarFiles) { + File jarFile = new File(lib, jar.getName()); + if (log.isDebugEnabled()) { + log.debug("Copying " + jar + " to " + jarFile); + } + FileInputStream in = new FileInputStream(jar); + FileOutputStream out = new FileOutputStream(jarFile); + for (;;) { + int len = in.read(buf); + if (len > 0) { + out.write(buf, 0, len); + } else { + break; + } + } + in.close(); + out.close(); + } + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + + } + +} diff --git a/java/sca/tools/maven/maven-bundle-plugin/src/main/resources/org/apache/tuscany/sca/maven/plugin/eclipse/messages.properties b/java/sca/tools/maven/maven-bundle-plugin/src/main/resources/org/apache/tuscany/sca/maven/plugin/eclipse/messages.properties new file mode 100644 index 0000000000..11de16d7a8 --- /dev/null +++ b/java/sca/tools/maven/maven-bundle-plugin/src/main/resources/org/apache/tuscany/sca/maven/plugin/eclipse/messages.properties @@ -0,0 +1,58 @@ +EclipsePlugin.missingpom=There must be a POM in the current working directory for the Eclipse plugin to work. +EclipsePlugin.pompackaging=Not running eclipse plugin goal for pom project +EclipsePlugin.notadir=Not a directory: "{0}" +EclipsePlugin.cantcreatedir=Can''t create directory "{0}" +EclipsePlugin.erroropeningfile=Exception while opening file. +EclipsePlugin.cantwritetofile=Unable to write to file: {0} +EclipsePlugin.cantfindresource=Unable to resolve resource location: {0} +EclipsePlugin.cantreadfile=Unable to read file: {0} +EclipsePlugin.keepexisting=File {0} already exists.\n Additional settings will be preserved, run mvn eclipse:clean if you want old settings to be removed. +EclipsePlugin.cantparseexisting=Unable to parse existing file: {0}. Settings will not be preserved. +EclipsePlugin.cantresolvesources=Cannot resolve source artifact. Artifact id: {0} (Message: {1}) +EclipsePlugin.wrote=Wrote Eclipse project for "{0}" to {1}. +EclipsePlugin.missingelement=Missing element from the project descriptor: "{0}" +EclipsePlugin.includenotsupported=This plugin currently doesn't support include patterns for resources. Adding the entire directory. +EclipsePlugin.excludenotsupported=This plugin currently doesn't support exclude patterns for resources. Adding the entire directory. +EclipsePlugin.artifactpathisnull=The artifact path was null. Artifact id: {0} +EclipsePlugin.artifactissystemscoped=The artifact has scope ''system''. Artifact id: {0}. System path: {1} +EclipsePlugin.unsupportedwtp=Unsupported WTP version: {0}. This plugin currently supports only the following versions: {1}. +EclipsePlugin.wtpversion=Adding support for WTP version {0}. +EclipsePlugin.missingjrecontainer=You did specify a list of classpath containers without the base org.eclipse.jdt.launching.JRE_CONTAINER.\n If you specify custom classpath containers you should also add org.eclipse.jdt.launching.JRE_CONTAINER to the list +EclipsePlugin.deprecatedpar=Plugin parameter "{0}" is deprecated, please use "{1}" +EclipsePlugin.cantcopyartifact=Can''t copy artifact "{0}". +EclipsePlugin.foundadir={0} is a directory, ignoring. + +EclipseSettingsWriter.wrotesettings=Wrote settings to {0} +EclipseSettingsWriter.cannotcreatesettings=Cannot create settings file +EclipseSettingsWriter.errorwritingsettings=Error writing settings file +EclipseSettingsWriter.usingdefaults=Not writing settings - defaults suffice + +EclipseClasspathWriter.lookingforsources=Looking for source archive for artifact {0} +EclipseClasspathWriter.sourcesavailable=Sources attachment for artifact {0} set to {1} + +EclipseProjectWriter.notafile=Not adding a file link to {0}; it is not a file + +EclipseCleanMojo.failedtodelete=Failed to delete {0} file: {0} +EclipseCleanMojo.nofilefound=No {0} file found +EclipseCleanMojo.deletingFile=Deleting file: {0} +EclipseCleanMojo.deletingDirectory=Deleting directory: {0} + +EclipseOSGiManifestWriter.nomanifestfile=The references manifest file doesn''t exist, plugin dependencies will not be updated: {0} + +IdeDependency.cantreadfile=Unable to read file: {0} + +Rad6LibCopier.cantdeletefile=Failed to delete file: {0} + +MyEclipseSpringBeansWriter.springVersionNotFound=Spring must be declared in the project's dependencies to generate .springBeans file. + +AbstractIdeSupportMojo.sourcesnotavailable=\n Sources for some artifacts are not available.\n List of artifacts without a source archive: +AbstractIdeSupportMojo.sourcesnotdownloaded=\n Sources for some artifacts are not available.\n Please run the same goal with the -DdownloadSources=true parameter in order to check remote repositories for sources.\n List of artifacts without a source archive: +AbstractIdeSupportMojo.sourcesmissingitem=\n o {0} +AbstractIdeSupportMojo.javadocnotavailable=\n Javadoc for some artifacts is not available.\n List of artifacts without a javadoc archive: +AbstractIdeSupportMojo.javadocnotdownloaded=\n Javadoc for some artifacts is not available.\n Please run the same goal with the -DdownloadJavadocs=true parameter in order to check remote repositories for javadoc.\n List of artifacts without a javadoc archive: +AbstractIdeSupportMojo.javadocmissingitem=\n o {0} +AbstractIdeSupportMojo.errorresolving=Error resolving {0} artifact. Artifact id: {1} (Message: {2}) +AbstractIdeSupportMojo.artifactresolution=An error occurred during dependency resolution of the following artifact:\n {0}:{1}:{2}\nCaused by: {3} +AbstractIdeSupportMojo.artifactdownload=An error occurred during dependency resolution.\n Failed to retrieve {0}:{1}-{2}\nCaused by: {3} +AbstractIdeSupportMojo.cantcanonicalize=Can''t canonicalize system path: {0} +AbstractIdeSupportMojo.unabletoparseversion={0}: unable to parse version ''{1}'' for dependency ''{2}'': {3} |