package org.springframework.roo.project;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.process.manager.ActiveProcessManager;
import org.springframework.roo.process.manager.ProcessManager;
import org.springframework.roo.project.packaging.PackagingProvider;
import org.springframework.roo.project.packaging.PackagingProviderRegistry;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.DomUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Implementation of {@link MavenOperations}.
*
* @author Ben Alex
* @author Alan Stewart
* @since 1.0
*/
@Component(immediate = true)
@Service
public class MavenOperationsImpl extends AbstractProjectOperations implements
MavenOperations {
private static class LoggingInputStream extends Thread {
private InputStream inputStream;
private final ProcessManager processManager;
/**
* Constructor
*
* @param inputStream
* @param processManager
*/
public LoggingInputStream(final InputStream inputStream,
final ProcessManager processManager) {
this.inputStream = inputStream;
this.processManager = processManager;
}
@Override
public void run() {
ActiveProcessManager.setActiveProcessManager(processManager);
try {
for (String line : IOUtils.readLines(inputStream)) {
if (line.startsWith("[ERROR]")) {
LOGGER.severe(line);
}
else if (line.startsWith("[WARNING]")) {
LOGGER.warning(line);
}
else {
LOGGER.info(line);
}
}
}
catch (final IOException e) {
// 1st condition for *nix/Mac, 2nd condition for Windows
if (e.getMessage().contains("No such file or directory")
|| e.getMessage().contains("CreateProcess error=2")) {
LOGGER.severe("Could not locate Maven executable; please ensure mvn command is in your path");
}
}
finally {
IOUtils.closeQuietly(inputStream);
ActiveProcessManager.clearActiveProcessManager();
}
}
}
private static final Logger LOGGER = HandlerUtils
.getLogger(MavenOperationsImpl.class);
@Reference private PackagingProviderRegistry packagingProviderRegistry;
@Reference private ProcessManager processManager;
private void addModuleDeclaration(final String moduleName,
final Document pomDocument, final Element root) {
final Element modulesElement = createModulesElementIfNecessary(
pomDocument, root);
if (!isModuleAlreadyPresent(moduleName, modulesElement)) {
modulesElement.appendChild(XmlUtils.createTextElement(pomDocument,
"module", moduleName));
}
}
public void createModule(final JavaPackage topLevelPackage,
final GAV parentPom, final String moduleName,
final PackagingProvider selectedPackagingProvider,
final Integer majorJavaVersion,
final String artifactId) {
Validate.isTrue(isCreateModuleAvailable(),
"Cannot create modules at this time");
final PackagingProvider packagingProvider = getPackagingProvider(selectedPackagingProvider);
final String pathToNewPom = packagingProvider.createArtifacts(
topLevelPackage, artifactId, getJavaVersion(majorJavaVersion),
parentPom, moduleName, this);
updateParentModulePom(moduleName);
setModule(pomManagementService.getPomFromPath(pathToNewPom));
}
private Element createModulesElementIfNecessary(final Document pomDocument,
final Element root) {
Element modulesElement = XmlUtils.findFirstElement("/project/modules",
root);
if (modulesElement == null) {
modulesElement = pomDocument.createElement("modules");
final Element repositories = XmlUtils.findFirstElement(
"/project/repositories", root);
root.insertBefore(modulesElement, repositories);
}
return modulesElement;
}
public void createProject(final JavaPackage topLevelPackage,
final String projectName, final Integer majorJavaVersion,
final GAV parentPom,
final PackagingProvider selectedPackagingProvider) {
Validate.isTrue(isCreateProjectAvailable(),
"Project creation is unavailable at this time");
final PackagingProvider packagingProvider = getPackagingProvider(selectedPackagingProvider);
packagingProvider.createArtifacts(topLevelPackage, projectName,
getJavaVersion(majorJavaVersion), parentPom, "", this);
}
public void executeMvnCommand(final String extra) throws IOException {
final File root = new File(getProjectRoot());
Validate.isTrue(root.isDirectory() && root.exists(),
"Project root does not currently exist as a directory ('"
+ root.getCanonicalPath() + "')");
final String cmd = (File.separatorChar == '\\' ? "mvn.bat " : "mvn ")
+ extra;
final Process p = Runtime.getRuntime().exec(cmd, null, root);
// Ensure separate threads are used for logging, as per ROO-652
final LoggingInputStream input = new LoggingInputStream(
p.getInputStream(), processManager);
final LoggingInputStream errors = new LoggingInputStream(
p.getErrorStream(), processManager);
// Close OutputStream to avoid blocking by Maven commands that expect
// input, as per ROO-2034
IOUtils.closeQuietly(p.getOutputStream());
input.start();
errors.start();
try {
if (p.waitFor() != 0) {
LOGGER.warning("The command '" + cmd
+ "' did not complete successfully");
}
}
catch (final InterruptedException e) {
throw new IllegalStateException(e);
}
}
/**
* Returns the project's target Java version in POM format
*
* @param majorJavaVersion the major version provided by the user; can be
* <code>null</code> to auto-detect it
* @return a non-blank string
*/
private String getJavaVersion(final Integer majorJavaVersion) {
if (majorJavaVersion != null && majorJavaVersion >= 6
&& majorJavaVersion <= 7) {
return String.valueOf(majorJavaVersion);
}
// To be running Roo they must be on Java 6 or above
return "1.6";
}
private PackagingProvider getPackagingProvider(
final PackagingProvider selectedPackagingProvider) {
return ObjectUtils.defaultIfNull(selectedPackagingProvider,
packagingProviderRegistry.getDefaultPackagingProvider());
}
public String getProjectRoot() {
return pathResolver.getRoot(Path.ROOT
.getModulePathId(pomManagementService.getFocusedModuleName()));
}
public boolean isCreateModuleAvailable() {
return true;
}
public boolean isCreateProjectAvailable() {
return !isProjectAvailable(getFocusedModuleName());
}
private boolean isModuleAlreadyPresent(final String moduleName,
final Element modulesElement) {
for (final Element element : XmlUtils.findElements("module",
modulesElement)) {
if (element.getTextContent().trim().equals(moduleName)) {
return true;
}
}
return false;
}
private void updateParentModulePom(final String moduleName) {
final String parentPomPath = pomManagementService.getFocusedModule()
.getPath();
final Document parentPomDocument = XmlUtils.readXml(fileManager
.getInputStream(parentPomPath));
final Element parentPomRoot = parentPomDocument.getDocumentElement();
DomUtils.createChildIfNotExists("packaging", parentPomRoot,
parentPomDocument).setTextContent("pom");
addModuleDeclaration(moduleName, parentPomDocument, parentPomRoot);
final String addModuleMessage = getDescriptionOfChange(ADDED,
Collections.singleton(moduleName), "module", "modules");
fileManager.createOrUpdateTextFileIfRequired(getFocusedModule()
.getPath(), XmlUtils.nodeToString(parentPomDocument),
addModuleMessage, false);
}
}