package org.springframework.roo.project;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.process.manager.ProcessManager;
import org.springframework.roo.project.packaging.JarPackaging;
import org.springframework.roo.project.packaging.PackagingProvider;
import org.springframework.roo.shell.CliAvailabilityIndicator;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.CliOptionAutocompleteIndicator;
import org.springframework.roo.shell.CliOptionVisibilityIndicator;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.ShellContext;
import org.springframework.roo.support.logging.HandlerUtils;
/**
* Commands related to file system monitoring and process management.
*
* @author Ben Alex
* @author Juan Carlos GarcĂa
* @since 1.1
*/
@Component
@Service
public class ProjectCommands implements CommandMarker {
private static final String DEVELOPMENT_MODE_COMMAND = "addon development mode";
private static final String PROJECT_SETUP_COMMAND = "project setup";
private static final String PROJECT_SCAN_SPEED_COMMAND = "project scan speed";
private static final String PROJECT_SCAN_STATUS_COMMAND = "project scan status";
private static final String PROJECT_SCAN_NOW_COMMAND = "project scan now";
protected final static Logger LOGGER = HandlerUtils.getLogger(ProjectCommands.class);
// ------------ OSGi component attributes ----------------
private BundleContext context;
private ProcessManager processManager;
private Shell shell;
private ProjectOperations projectOperations;
private MavenOperations mavenOperations;
protected void activate(final ComponentContext context) {
this.context = context.getBundleContext();
}
@CliAvailabilityIndicator(PROJECT_SETUP_COMMAND)
public boolean isCreateProjectAvailable() {
return getMavenOperations().isCreateProjectAvailable();
}
@CliOptionVisibilityIndicator(command = PROJECT_SETUP_COMMAND, params = {"packaging"},
help = "Packaging parameter is not available if multimodule is specified.")
public boolean isPackagingVisible(ShellContext shellContext) {
// Getting all defined parameters
Map<String, String> params = shellContext.getParameters();
// If multimodule is enabled, packaging parameter should not
// be visible
String multimodule = params.get("multimodule");
if (multimodule == null) {
return true;
}
return false;
}
@CliOptionAutocompleteIndicator(param = "java", command = PROJECT_SETUP_COMMAND,
help = "Java version 6, 7 and 8 available.")
public List<String> getJavaVersions(ShellContext context) {
List<String> javaVersions = new ArrayList<String>();
javaVersions.add("6");
javaVersions.add("7");
javaVersions.add("8");
return javaVersions;
}
@CliCommand(value = PROJECT_SETUP_COMMAND, help = "Creates a new Maven project.")
public void createProject(
@CliOption(
key = {"topLevelPackage"},
mandatory = true,
optionContext = "update",
help = "The uppermost package name (this becomes the `<groupId>` in Maven and also the `~` value "
+ "when using Roo Shell).") final JavaPackage topLevelPackage,
@CliOption(key = "projectName",
help = "The name of the project (this becomes the `<artifactId>` in Maven). "
+ "Default if option not present: last segment of `--topLevelPackage` name used.") final String projectName,
@CliOption(
key = "multimodule",
mandatory = false,
specifiedDefaultValue = "STANDARD",
help = "Option to use a multimodule architecture. "
+ "Possible values are: `BASIC` (root module with child 'application' module), and "
+ "`STANDARD` (root module with children modules: 'application', 'model', 'repository', "
+ "'service-api', 'service-impl' and 'integration'). "
+ "Default if option present: `STANDARD`") final Multimodule multimodule,
@CliOption(key = "java", help = "Forces a particular major version of Java to be used. "
+ "Default if option not present: Java 6 inherited from Spring Boot.") final Integer majorJavaVersion,
@CliOption(key = "packaging", help = "The Maven packaging of this project."
+ "This option is not available if 'multimodule' is specified. "
+ "Default if option not present: 'jar'.", unspecifiedDefaultValue = JarPackaging.NAME) final PackagingProvider packaging) {
if (multimodule != null) {
getMavenOperations().createMultimoduleProject(topLevelPackage, projectName, majorJavaVersion,
multimodule);
} else {
getMavenOperations().createProject(topLevelPackage, projectName, majorJavaVersion, packaging);
}
}
@CliAvailabilityIndicator({PROJECT_SCAN_SPEED_COMMAND, PROJECT_SCAN_STATUS_COMMAND,
PROJECT_SCAN_NOW_COMMAND})
public boolean isProjecScanAvailable() {
return getProjectOperations().isFocusedProjectAvailable();
}
@CliCommand(
value = DEVELOPMENT_MODE_COMMAND,
help = "Switches the system into development mode, which enables add-on development commands and "
+ "shows greater diagnostic information.")
public String developmentMode(@CliOption(key = {"enabled"}, mandatory = false,
specifiedDefaultValue = "true", unspecifiedDefaultValue = "true",
help = "Activates development mode") final boolean enabled) {
if (processManager == null) {
processManager = getProcessManager();
}
Validate.notNull(processManager, "ProcessManager is required");
if (shell == null) {
shell = getShell();
}
Validate.notNull(shell, "Shell is required");
processManager.setDevelopmentMode(enabled);
shell.setDevelopmentMode(enabled);
return "Development mode set to " + enabled;
}
@CliCommand(
value = PROJECT_SCAN_NOW_COMMAND,
help = "Performs a manual file system scan, calling thread monitors and checking that all files "
+ "are updated.")
public String scan() {
if (processManager == null) {
processManager = getProcessManager();
}
Validate.notNull(processManager, "ProcessManager is required");
final long originalSetting = processManager.getMinimumDelayBetweenScan();
try {
processManager.setMinimumDelayBetweenScan(1);
processManager.timerBasedScan();
} finally {
// Switch on manual scan again
processManager.setMinimumDelayBetweenScan(originalSetting);
}
return "Manual scan completed";
}
@CliCommand(value = PROJECT_SCAN_STATUS_COMMAND,
help = "Displays file system scanning information such as the time lasted for last scan "
+ "and scanning frequency.")
public String scanningInfo() {
if (processManager == null) {
processManager = getProcessManager();
}
Validate.notNull(processManager, "ProcessManager is required");
final StringBuilder sb = new StringBuilder("File system scanning ");
final long duration = processManager.getLastScanDuration();
if (duration == 0) {
sb.append("never executed; ");
} else {
sb.append("last took ").append(duration).append(" ms; ");
}
final long minimum = processManager.getMinimumDelayBetweenScan();
if (minimum == 0) {
sb.append("automatic scanning is disabled");
} else if (minimum < 0) {
sb.append("auto-scaled scanning is enabled");
} else {
sb.append("scanning frequency has a minimum interval of ").append(minimum).append(" ms");
}
return sb.toString();
}
@CliCommand(value = PROJECT_SCAN_SPEED_COMMAND,
help = "Changes the time inteval between file system scans.")
public String scanningSpeed(@CliOption(key = {"", "ms"}, mandatory = true,
help = "The number of milliseconds between each scan") final long minimumDelayBetweenScan) {
if (processManager == null) {
processManager = getProcessManager();
}
Validate.notNull(processManager, "ProcessManager is required");
processManager.setMinimumDelayBetweenScan(minimumDelayBetweenScan);
return scanningInfo();
}
public ProcessManager getProcessManager() {
// Get all components implement ProcessManager interface
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(ProcessManager.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (ProcessManager) this.context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load ProcessManager on ProcessManagerCommands.");
return null;
}
}
public Shell getShell() {
// Get all Shell implement Shell interface
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(Shell.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (Shell) this.context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load Shell on ProcessManagerCommands.");
return null;
}
}
public ProjectOperations getProjectOperations() {
if (projectOperations == null) {
// Get all Services implement ProjectOperations interface
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(ProjectOperations.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (ProjectOperations) this.context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load ProjectOperations on ProcessManagerCommands.");
return null;
}
} else {
return projectOperations;
}
}
public MavenOperations getMavenOperations() {
if (mavenOperations == null) {
// Get all Services implement MavenOperations interface
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(MavenOperations.class.getName(), null);
for (ServiceReference<?> ref : references) {
return (MavenOperations) this.context.getService(ref);
}
return null;
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load MavenOperations on MavenCommands.");
return null;
}
} else {
return mavenOperations;
}
}
}