Skip to content

Commit

Permalink
Let maven do the hard work of archetype generation
Browse files Browse the repository at this point in the history
Prerequisite for #249
  • Loading branch information
laeubi committed Jun 11, 2022
1 parent fb4d7a1 commit da19243
Show file tree
Hide file tree
Showing 16 changed files with 425 additions and 131 deletions.
4 changes: 2 additions & 2 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "m2e-core-tests"]
path = m2e-core-tests
url = https://github.com/tesla/m2e-core-tests.git
branch = master
url = https://github.com/laeubi/m2e-core-tests.git
branch = adjust_repo
Original file line number Diff line number Diff line change
Expand Up @@ -10,178 +10,151 @@
* Contributors:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/

package org.eclipse.m2e.core.ui.internal.archetype;

import java.util.ArrayList;
import java.util.Collections;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;

import org.codehaus.plexus.util.StringUtils;

import org.apache.maven.archetype.ArchetypeGenerationRequest;
import org.apache.maven.archetype.ArchetypeGenerationResult;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;

import org.eclipse.m2e.core.embedder.IMaven;
import org.eclipse.m2e.core.embedder.IMavenExecutionContext;
import org.eclipse.m2e.core.embedder.MavenModelManager;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.Messages;
import org.eclipse.m2e.core.internal.embedder.MavenImpl;
import org.eclipse.m2e.core.internal.launch.IMavenLauncher;
import org.eclipse.m2e.core.project.IArchetype;
import org.eclipse.m2e.core.project.IMavenProjectImportResult;
import org.eclipse.m2e.core.project.IProjectConfigurationManager;
import org.eclipse.m2e.core.project.IProjectCreationListener;
import org.eclipse.m2e.core.project.LocalProjectScanner;
import org.eclipse.m2e.core.project.MavenProjectInfo;
import org.eclipse.m2e.core.project.ProjectImportConfiguration;


@Component(service = {ArchetypeGenerator.class})
@SuppressWarnings("restriction")
public class ArchetypeGenerator {

private static final Logger log = LoggerFactory.getLogger(ArchetypeGenerator.class);

@Reference
IMaven maven;

@Reference
IProjectConfigurationManager projectConfigurationManager;

@Reference
MavenModelManager mavenModelManager;

@Reference
IMavenLauncher mavenLauncher;

/**
* Creates project structure using Archetype and then imports created project(s)
*
* @return an unmodifiable list of created projects.
* @return a list of created projects.
* @since 1.8
*/
public List<IProject> createArchetypeProjects(IPath location, IArchetype archetype, String groupId, String artifactId,
String version, String javaPackage, Properties properties, ProjectImportConfiguration configuration,
IProjectCreationListener listener, IProgressMonitor monitor) throws CoreException {
return IMavenExecutionContext.getThreadContext().orElseGet(maven::createExecutionContext)
.execute((context, m) -> createArchetypeProjects0(location, archetype, groupId,
artifactId, version,
javaPackage, properties, configuration, listener, m), monitor);
}

List<IProject> createArchetypeProjects0(IPath location, IArchetype archetype, String groupId, String artifactId,
String version, String javaPackage, Properties properties, ProjectImportConfiguration configuration,
IProjectCreationListener listener, IProgressMonitor monitor) throws CoreException {
monitor.beginTask(NLS.bind(Messages.ProjectConfigurationManager_task_creating_project1, artifactId), 2);

public Collection<MavenProjectInfo> createArchetypeProjects(IPath location, IArchetype archetype, String groupId,
String artifactId, String version, String javaPackage, Properties properties, IProgressMonitor monitor)
throws CoreException {
SubMonitor subMonitor = SubMonitor.convert(monitor,
NLS.bind(Messages.ProjectConfigurationManager_task_creating_project1, artifactId), 3);
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

monitor.subTask(NLS.bind(Messages.ProjectConfigurationManager_task_executing_archetype, archetype.getGroupId(),
subMonitor.subTask(NLS.bind(Messages.ProjectConfigurationManager_task_executing_archetype, archetype.getGroupId(),
archetype.getArtifactId()));
if(location == null) {
// if the project should be created in the workspace, figure out the path
location = workspaceRoot.getLocation();
}

List<IProject> createdProjects = new ArrayList<>();

File basedir = location.toFile();
if(basedir == null || (!basedir.mkdirs() && !basedir.isDirectory())) {
throw new CoreException(Status.error(Messages.ProjectConfigurationManager_error_failed));
}
//See https://maven.apache.org/archetype/maven-archetype-plugin/generate-mojo.html
Properties userProperties = new Properties(properties);
userProperties.setProperty("archetypeGroupId", archetype.getGroupId());
userProperties.setProperty("archetypeArtifactId", archetype.getArtifactId());
userProperties.setProperty("archetypeVersion", archetype.getVersion());
String repository = archetype.getRepository();
if(repository != null) {
userProperties.setProperty("archetypeRepository", repository);
}
userProperties.setProperty("groupId", groupId);
userProperties.setProperty("artifactId", artifactId);
userProperties.setProperty("version", version);
userProperties.setProperty("package", javaPackage);
userProperties.setProperty("outputDirectory", basedir.getAbsolutePath());
String projectFolder = location.append(artifactId).toFile().getAbsolutePath();
File emptyPom = getEmptyPom(basedir);
try {

Artifact artifact = resolveArchetype(archetype, monitor);

ArchetypeGenerationRequest request = new ArchetypeGenerationRequest() //
.setTransferListener(((MavenImpl) maven).createTransferListener(monitor)) //
.setArchetypeGroupId(artifact.getGroupId()) //
.setArchetypeArtifactId(artifact.getArtifactId()) //
.setArchetypeVersion(artifact.getVersion()) //
.setArchetypeRepository(archetype.getRepository()) //
.setGroupId(groupId) //
.setArtifactId(artifactId) //
.setVersion(version) //
.setPackage(javaPackage) // the model does not have a package field
.setLocalRepository(maven.getLocalRepository()) //
.setRemoteArtifactRepositories(maven.getArtifactRepositories(true)).setProperties(properties)
.setOutputDirectory(location.toPortableString());

ArchetypeGenerationResult result = Adapters.adapt(archetype, org.apache.maven.archetype.ArchetypeManager.class)
.generateProjectFromArchetype(request);

Exception cause = result.getCause();
if(cause != null) {
String msg = NLS.bind(Messages.ProjectConfigurationManager_error_unable_archetype, archetype);
log.error(msg, cause);
throw new CoreException(Status.error(msg, cause));
String goals = "org.apache.maven.plugins:maven-archetype-plugin:2.4:generate";
if(emptyPom != null) {
goals += " -f " + emptyPom.getAbsolutePath();
}
monitor.worked(1);

// XXX Archetyper don't allow to specify project folder
String projectFolder = location.append(artifactId).toFile().getAbsolutePath();

LocalProjectScanner scanner = new LocalProjectScanner(List.of(projectFolder), true, mavenModelManager);
scanner.run(monitor);

Set<MavenProjectInfo> projectSet = projectConfigurationManager.collectProjects(scanner.getProjects());

List<IMavenProjectImportResult> importResults = projectConfigurationManager.importProjects(projectSet,
configuration, listener, monitor);
for(IMavenProjectImportResult r : importResults) {
IProject p = r.getProject();
if(p != null && p.exists()) {
createdProjects.add(p);
CompletableFuture<?> maven = mavenLauncher.runMaven(basedir, goals, userProperties);
subMonitor.worked(1);
Display current = Display.getCurrent();
while(!maven.isDone()) {
if(current != null) {
while(!current.isDisposed() && current.readAndDispatch()) {
//loop to process events
}
}
Thread.onSpinWait();
}

monitor.worked(1);
} catch(CoreException e) {
throw e;
} catch(InterruptedException e) {
maven.get(); //wait for maven build to complete...
subMonitor.worked(1);
LocalProjectScanner scanner = new LocalProjectScanner(List.of(projectFolder), true, mavenModelManager);
try {
scanner.run(subMonitor.split(1));
} catch(InterruptedException e) {
return List.of();
}
return projectConfigurationManager.collectProjects(scanner.getProjects());
} catch(InterruptedException | CancellationException ex) {
throw new CoreException(Status.CANCEL_STATUS);
} catch(Exception ex) {
} catch(ExecutionException ex) {
if(ex.getCause() instanceof CoreException) {
CoreException coreException = (CoreException) ex.getCause();
throw coreException;
}
throw new CoreException(Status.error(Messages.ProjectConfigurationManager_error_failed, ex)); //$NON-NLS-1$
} finally {
if(emptyPom != null) {
emptyPom.delete();
}
}
return Collections.unmodifiableList(createdProjects);
}

/**
* Apparently, Archetype#generateProjectFromArchetype 2.0-alpha-4 does not attempt to resolve archetype from
* configured remote repositories. To compensate, we populate local repo with archetype pom/jar.
*/
private Artifact resolveArchetype(IArchetype a, IProgressMonitor monitor) throws CoreException {
List<ArtifactRepository> repos = new ArrayList<>();
repos.addAll(maven.getArtifactRepositories()); // see org.apache.maven.archetype.downloader.DefaultDownloader#download

//MNGECLIPSE-1399 use archetype repository too, not just the default ones
String artifactRemoteRepository = a.getRepository();

try {

if(StringUtils.isNotBlank(artifactRemoteRepository)) {
ArtifactRepository archetypeRepository = maven.createArtifactRepository(a.getArtifactId() + "-repo", //$NON-NLS-1$
a.getRepository().trim());
repos.add(0, archetypeRepository);//If the archetype doesn't exist locally, this will be the first remote repo to be searched.
private File getEmptyPom(File basedir) {
if(new File(basedir, IMavenConstants.POM_FILE_NAME).isFile()) {
try {
File tempFile = File.createTempFile("pom", ".xml", basedir);
tempFile.deleteOnExit();
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile),
StandardCharsets.UTF_8)) {
writer.write(
"<project><modelVersion>4.0.0</modelVersion><groupId>empty</groupId><artifactId>empty</artifactId><version>1</version><name>Generating archetype</name></project>");
}
return tempFile;
} catch(IOException ex) {
}

maven.resolve(a.getGroupId(), a.getArtifactId(), a.getVersion(), "pom", null, repos, monitor); //$NON-NLS-1$
return maven.resolve(a.getGroupId(), a.getArtifactId(), a.getVersion(), "jar", null, repos, monitor); //$NON-NLS-1$
} catch(CoreException e) {
String msg = Messages.ProjectConfigurationManager_error_resolve + a.getGroupId() + ':'
+ Messages.ProjectConfigurationManager_error_resolve2;
throw new CoreException(Status.error(msg, e));
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.textEquals;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -53,6 +55,7 @@

import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.project.ProjectConfigurationManager;
import org.eclipse.m2e.core.project.MavenProjectInfo;
import org.eclipse.m2e.core.ui.internal.M2EUIPluginActivator;
import org.eclipse.m2e.core.ui.internal.MavenImages;
import org.eclipse.m2e.core.ui.internal.Messages;
Expand Down Expand Up @@ -247,15 +250,17 @@ protected List<IProject> doCreateMavenProjects(IProgressMonitor monitor) throws
job = new AbstractCreateMavenProjectJob(NLS.bind(Messages.wizardProjectJobCreating, archetype.getArtifactId())) {
@Override
protected List<IProject> doCreateMavenProjects(IProgressMonitor monitor) throws CoreException {
List<IProject> projects = M2EUIPluginActivator.getDefault().getArchetypeManager().getGenerator()
Collection<MavenProjectInfo> projects = M2EUIPluginActivator.getDefault().getArchetypeManager().getGenerator()
.createArchetypeProjects(location,
new MavenArchetype(archetype), //
groupId, artifactId, version, javaPackage, //
properties, importConfiguration, new MavenProjectWorkspaceAssigner(workingSets), monitor);

properties, monitor);
setModule(moduleName);

return projects;
return MavenPlugin.getProjectConfigurationManager()
.importProjects(projects, importConfiguration, new MavenProjectWorkspaceAssigner(workingSets), monitor)
.stream().filter(r -> r.getProject() != null && r.getProject().exists()).map(r -> r.getProject())
.collect(Collectors.toList());
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
package org.eclipse.m2e.core.ui.internal.wizards;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
Expand Down Expand Up @@ -47,6 +49,7 @@
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.internal.project.ProjectConfigurationManager;
import org.eclipse.m2e.core.project.MavenProjectInfo;
import org.eclipse.m2e.core.project.ProjectImportConfiguration;
import org.eclipse.m2e.core.ui.internal.M2EUIPluginActivator;
import org.eclipse.m2e.core.ui.internal.MavenImages;
Expand Down Expand Up @@ -238,12 +241,15 @@ protected List<IProject> doCreateMavenProjects(IProgressMonitor monitor) throws
job = new AbstractCreateMavenProjectJob(NLS.bind(Messages.wizardProjectJobCreating, archetype.getArtifactId())) {
@Override
protected List<IProject> doCreateMavenProjects(IProgressMonitor monitor) throws CoreException {
List<IProject> projects = M2EUIPluginActivator.getDefault().getArchetypeManager().getGenerator()
Collection<MavenProjectInfo> projects = M2EUIPluginActivator.getDefault().getArchetypeManager().getGenerator()
.createArchetypeProjects(location,
new MavenArchetype(archetype), //
groupId, artifactId, version, javaPackage, //
properties, importConfiguration, new MavenProjectWorkspaceAssigner(workingSets), monitor);
return projects;
properties, monitor);
return MavenPlugin.getProjectConfigurationManager().importProjects(projects,
importConfiguration, new MavenProjectWorkspaceAssigner(workingSets), monitor).stream()
.filter(r -> r.getProject() != null && r.getProject().exists()).map(r -> r.getProject())
.collect(Collectors.toList());
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class MavenPluginActivator extends Plugin {

private BundleContext bundleContext;

private String version = "0.0.0"; //$NON-NLS-1$
private static String version = "0.0.0"; //$NON-NLS-1$

private final BundleListener bundleListener = event -> LifecycleMappingFactory.setBundleMetadataSources(null);

Expand Down Expand Up @@ -197,7 +197,8 @@ public ProjectRegistryRefreshJob getProjectManagerRefreshJob() {
}

public static String getVersion() {
return plugin.version;

return version;
}

public static String getUserAgent() {
Expand Down
Loading

0 comments on commit da19243

Please sign in to comment.