/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.maven.generator.archetype;
import org.eclipse.che.api.core.ServerException;
import org.eclipse.che.api.core.util.AbstractLineConsumer;
import org.eclipse.che.api.core.util.CommandLine;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.core.util.ProcessUtil;
import org.eclipse.che.api.core.util.ValueHolder;
import org.eclipse.che.api.core.util.Watchdog;
import org.eclipse.che.api.core.util.WebsocketMessageConsumer;
import org.eclipse.che.ide.maven.tools.MavenArtifact;
import org.eclipse.che.ide.maven.tools.MavenUtils;
import org.eclipse.che.plugin.maven.shared.MavenArchetype;
import org.eclipse.che.plugin.maven.shared.dto.ArchetypeOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Singleton;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.eclipse.che.plugin.maven.shared.MavenAttributes.MAVEN_ARCHETYPE_CHANEL_NAME;
/**
* Generates projects with maven-archetype-plugin.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class ArchetypeGenerator {
private static final Logger LOG = LoggerFactory.getLogger(ArchetypeGenerator.class);
/**
* Generates a new project from the specified archetype by given maven artifact descriptor.
* @param workDir
* folder where command will execute in common use root dir of workspace
* @param archetype
* archetype from which need to generate new project
* @param mavenArtifact
* maven artifact descriptor
* @throws ServerException
* if an error occurs while generating project
*/
public void generateFromArchetype(@NotNull File workDir, @NotNull MavenArchetype archetype, MavenArtifact mavenArtifact) throws ServerException {
Map<String, String> archetypeProperties = new HashMap<>();
archetypeProperties.put("-DinteractiveMode", "false"); // get rid of the interactivity of the archetype plugin
archetypeProperties.put("-DarchetypeGroupId", archetype.getGroupId());
archetypeProperties.put("-DarchetypeArtifactId", archetype.getArtifactId());
archetypeProperties.put("-DarchetypeVersion", archetype.getVersion());
archetypeProperties.put("-DgroupId", mavenArtifact.getGroupId());
archetypeProperties.put("-DartifactId", mavenArtifact.getArtifactId());
archetypeProperties.put("-Dversion", mavenArtifact.getVersion());
if (archetype.getRepository() != null) {
archetypeProperties.put("-DarchetypeRepository", archetype.getRepository());
}
if (archetype.getProperties() != null) {
archetypeProperties.putAll(archetype.getProperties());
}
final CommandLine commandLine = createCommandLine(archetypeProperties);
try {
execute(commandLine.toShellCommand(), workDir);
} catch (TimeoutException e) {
LOG.error(e.getMessage());
} catch (IOException e) {
LOG.error(e.getMessage());
} catch (InterruptedException e) {
LOG.error(e.getMessage());
}
}
/**
* Execute maven archetype command
*
* @param commandLine
* command to execution e.g.
* mvn archetype:generate -DarchetypeGroupId=<archetype-groupId> -DarchetypeArtifactId=<archetype-artifactId>
* -DarchetypeVersion=<archetype-version> -DgroupId=<my.groupid> -DartifactId=<my-artifactId>
* @param workDir
* folder where command will execute in common use root dir of workspace
* @throws TimeoutException
* @throws IOException
* @throws InterruptedException
*/
private void execute(String[] commandLine, File workDir) throws TimeoutException, IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder(commandLine).redirectErrorStream(true).directory(workDir);
WebsocketMessageConsumer<ArchetypeOutput> websocketMessageConsumer = new WebsocketMessageConsumer(MAVEN_ARCHETYPE_CHANEL_NAME);
websocketMessageConsumer.consume(new ArchetypeOutputImpl("Start Project generation", ArchetypeOutput.State.START));
LineConsumer lineConsumer = new AbstractLineConsumer() {
@Override
public void writeLine(String line) throws IOException {
websocketMessageConsumer.consume(new ArchetypeOutputImpl(line, ArchetypeOutput.State.IN_PROGRESS));
}
};
// process will be stopped after timeout
Watchdog watcher = new Watchdog(60, TimeUnit.SECONDS);
try {
final Process process = pb.start();
final ValueHolder<Boolean> isTimeoutExceeded = new ValueHolder<>(false);
watcher.start(() -> {
isTimeoutExceeded.set(true);
ProcessUtil.kill(process);
});
// consume logs until process ends
ProcessUtil.process(process, lineConsumer);
process.waitFor();
websocketMessageConsumer.consume(new ArchetypeOutputImpl("Done", ArchetypeOutput.State.DONE));
if (isTimeoutExceeded.get()) {
LOG.error("Generation project time expired : command-line " + Arrays.toString(commandLine));
websocketMessageConsumer.consume(new ArchetypeOutputImpl("Generation project time expired", ArchetypeOutput.State.ERROR));
throw new TimeoutException();
} else if (process.exitValue() != 0) {
LOG.error("Generation project fail : command-line " + Arrays.toString(commandLine));
websocketMessageConsumer.consume(new ArchetypeOutputImpl("Generation project occurs error", ArchetypeOutput.State.ERROR));
throw new IOException("Process failed. Exit code " + process.exitValue() + " command-line : " + Arrays.toString(commandLine));
}
} finally {
watcher.stop();
}
}
/**
* Create specified command for maven archetype plugin
* e.g mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart
* @param archetypeProperties
* @return command line ready to execute
*/
private CommandLine createCommandLine(Map<String, String> archetypeProperties) {
final CommandLine commandLine = new CommandLine(MavenUtils.getMavenExecCommand());
commandLine.add("--batch-mode");
commandLine.add("org.apache.maven.plugins:maven-archetype-plugin:RELEASE:generate");
commandLine.add(archetypeProperties);
return commandLine;
}
}