initial commit

This commit is contained in:
Mark Pollack
2012-02-15 21:43:08 -05:00
commit 2a6b2bd679
21 changed files with 1517 additions and 0 deletions

139
pom.xml Normal file
View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples.spring</groupId>
<artifactId>spring-shell</artifactId>
<version>1.0.0.CI-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Spring Shell</name>
<url>http://www.springframework.org</url>
<description>
<![CDATA[
This project is a minimal jar utility with Spring configuration.
]]>
</description>
<properties>
<jar.mainclass>org.springframework.shell.Bootstrap</jar.mainclass>
<maven.test.failure.ignore>true</maven.test.failure.ignore>
<spring.framework.version>3.0.6.RELEASE</spring.framework.version>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!--
<dependency>
<groupId>org.springframework.roo</groupId>
<artifactId>org.springframework.roo.shell.jline</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
-->
<dependency>
<groupId>org.springframework.roo</groupId>
<artifactId>org.springframework.roo.shell</artifactId>
<version>1.2.0.RELEASE</version>
</dependency>
<!-- consider removing dependency -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
<!-- External modules -->
<dependency>
<groupId>net.sourceforge.jline</groupId>
<artifactId>jline</artifactId>
<version>1.0.S2-B</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.roo.wrapping</groupId>
<artifactId>org.springframework.roo.wrapping.json-simple</artifactId>
<version>1.1.0.0010</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${jar.mainclass}</mainClass>
</transformer>
<!--
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
-->
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-roo-repository</id>
<name>Spring Roo Maven Repository</name>
<url>http://spring-roo-repository.springsource.org/release</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2011 VMware, Inc. All rights reserved. -- VMware Confidential
*/
package org.springframework.shell;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
//import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.roo.shell.AbstractShell;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.roo.shell.Converter;
import org.springframework.roo.shell.ExitShellRequest;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.event.ShellStatus;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.Assert;
import org.springframework.util.StopWatch;
//import ch.qos.logback.classic.LoggerContext;
//import ch.qos.logback.classic.joran.JoranConfigurator;
//import ch.qos.logback.core.util.StatusPrinter;
/**
* Main class, needs some cleanup
*
* @author vnagaraja
*/
public class Bootstrap {
private static Bootstrap bootstrap;
//TODO using JLineShellComponenet to override and get access to "getSimpleParser" method on the shell - look into autowire...and move back to reference Shell interface.
private JLineShellComponent shell;
private ConfigurableApplicationContext ctx;
private static StopWatch sw = new StopWatch("Spring Sehll");
public static void main(String[] args) throws IOException {
sw.start();
SimpleShellCommandLineOptions options = SimpleShellCommandLineOptions.parseCommandLine(args);
for (Map.Entry<String, String> entry : options.extraSystemProperties.entrySet()) {
System.setProperty(entry.getKey(), entry.getValue());
}
ExitShellRequest exitShellRequest;
try {
bootstrap = new Bootstrap(options.applicationContextLocation);
exitShellRequest = bootstrap.run(options.executeThenQuit);
} catch (RuntimeException t) {
throw t;
} finally {
HandlerUtils.flushAllHandlers(Logger.getLogger(""));
}
System.exit(exitShellRequest.getExitCode());
}
public Bootstrap(String applicationContextLocation) throws IOException {
//setupLogging();
Assert.hasText(applicationContextLocation, "Application context location required");
ctx = new ClassPathXmlApplicationContext(applicationContextLocation);
Map<String, JLineShellComponent> shells = ctx.getBeansOfType(JLineShellComponent.class);
//Assert.isTrue(shells.size() == 1, "Exactly one Shell was required, but " + shells.size() + " found");
//shell = shells.values().iterator().next();
shell = new JLineShellComponent();
Map<String, CommandMarker> commands = ctx.getBeansOfType(CommandMarker.class);
for (CommandMarker command : commands.values()) {
System.out.println("Registgering command " + command);
shell.getSimpleParser().add(command);
}
Map<String, Converter> converters = ctx.getBeansOfType(Converter.class);
for (Converter converter : converters.values()) {
System.out.println("Registgering converter " + converter);
shell.getSimpleParser().add(converter);
}
shell.start();
//TODO use listener and latch..
while(true) {
//System.out.println("shell status = " + shell.getShellStatus().getStatus());
if (shell.getShellStatus().getStatus().equals(ShellStatus.Status.USER_INPUT)) {
break;
} else {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
protected ExitShellRequest run(String[] executeThenQuit) {
ExitShellRequest exitShellRequest;
if (null != executeThenQuit) {
boolean successful = false;
exitShellRequest = ExitShellRequest.FATAL_EXIT;
for(String cmd : executeThenQuit) {
successful = shell.executeCommand(cmd);
if(!successful)
break;
}
//if all commands were successful, set the normal exit status
if (successful) {
exitShellRequest = ExitShellRequest.NORMAL_EXIT;
}
} else {
shell.promptLoop();
exitShellRequest = shell.getExitShellRequest();
if (exitShellRequest == null) {
// shouldn't really happen, but we'll fallback to this anyway
exitShellRequest = ExitShellRequest.NORMAL_EXIT;
}
}
ctx.close();
sw.stop();
if (shell.isDevelopmentMode()) {
System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms");
}
return exitShellRequest;
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.shell;
import org.springframework.stereotype.Component;
/**
* {@link Service} with hard-coded input data.
*/
@Component
public class ExampleService implements Service {
/**
* Reads next record from input
*/
public String getMessage() {
return "Hello world!";
}
}

View File

@@ -0,0 +1,43 @@
package org.springframework.shell;
import java.util.ArrayList;
import java.util.List;
import jline.Completor;
import org.springframework.roo.shell.Completion;
import org.springframework.roo.shell.Parser;
import org.springframework.roo.support.util.Assert;
/**
* An implementation of JLine's {@link Completor} interface that delegates to a {@link Parser}.
*
* @author Ben Alex
* @since 1.0
*/
public class JLineCompletorAdapter implements Completor {
// Fields
private final Parser parser;
public JLineCompletorAdapter(final Parser parser) {
Assert.notNull(parser, "Parser required");
this.parser = parser;
}
@SuppressWarnings("all")
public int complete(final String buffer, final int cursor, final List candidates) {
int result;
try {
JLineLogHandler.cancelRedrawProhibition();
List<Completion> completions = new ArrayList<Completion>();
result = parser.completeAdvanced(buffer, cursor, completions);
for (Completion completion : completions) {
candidates.add(new jline.Completion(completion.getValue(), completion.getFormattedValue(), completion.getHeading()));
}
} finally {
JLineLogHandler.prohibitRedraw();
}
return result;
}
}

View File

@@ -0,0 +1,213 @@
package org.springframework.shell;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import jline.ANSIBuffer;
import jline.ConsoleReader;
import org.springframework.roo.shell.ShellPromptAccessor;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.IOUtils;
import org.springframework.roo.support.util.OsUtils;
import org.springframework.roo.support.util.StringUtils;
/**
* JDK logging {@link Handler} that emits log messages to a JLine {@link ConsoleReader}.
*
* @author Ben Alex
* @since 1.0
*/
public class JLineLogHandler extends Handler {
// Constants
private static final boolean BRIGHT_COLORS = Boolean.getBoolean("roo.bright");
// Fields
private ConsoleReader reader;
private ShellPromptAccessor shellPromptAccessor;
private static ThreadLocal<Boolean> redrawProhibit = new ThreadLocal<Boolean>();
private static String lastMessage;
private static boolean includeThreadName = false;
private boolean ansiSupported;
private String userInterfaceThreadName;
private static boolean suppressDuplicateMessages = true;
public JLineLogHandler(final ConsoleReader reader, final ShellPromptAccessor shellPromptAccessor) {
Assert.notNull(reader, "Console reader required");
Assert.notNull(shellPromptAccessor, "Shell prompt accessor required");
this.reader = reader;
this.shellPromptAccessor = shellPromptAccessor;
this.userInterfaceThreadName = Thread.currentThread().getName();
this.ansiSupported = reader.getTerminal().isANSISupported();
setFormatter(new Formatter() {
@Override
public String format(final LogRecord record) {
StringBuffer sb = new StringBuffer();
if (record.getMessage() != null) {
sb.append(record.getMessage()).append(StringUtils.LINE_SEPARATOR);
}
if (record.getThrown() != null) {
PrintWriter pw = null;
try {
StringWriter sw = new StringWriter();
pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
sb.append(sw.toString());
} catch (Exception ex) {
} finally {
IOUtils.closeQuietly(pw);
}
}
return sb.toString();
}
});
}
@Override
public void flush() {}
@Override
public void close() throws SecurityException {}
public static void prohibitRedraw() {
redrawProhibit.set(true);
}
public static void cancelRedrawProhibition() {
redrawProhibit.remove();
}
public static void setIncludeThreadName(final boolean include) {
includeThreadName = include;
}
public static void resetMessageTracking() {
lastMessage = null; // see ROO-251
}
public static boolean isSuppressDuplicateMessages() {
return suppressDuplicateMessages;
}
public static void setSuppressDuplicateMessages(final boolean suppressDuplicateMessages) {
JLineLogHandler.suppressDuplicateMessages = suppressDuplicateMessages;
}
@Override
public void publish(final LogRecord record) {
try {
// Avoid repeating the same message that displayed immediately before the current message (ROO-30, ROO-1873)
String toDisplay = toDisplay(record);
if (toDisplay.equals(lastMessage) && suppressDuplicateMessages) {
return;
}
lastMessage = toDisplay;
StringBuffer buffer = reader.getCursorBuffer().getBuffer();
int cursor = reader.getCursorBuffer().cursor;
if (reader.getCursorBuffer().length() > 0) {
// The user has semi-typed something, so put a new line in so the debug message is separated
reader.printNewline();
// We need to cancel whatever they typed (it's reset later on), so the line appears empty
reader.getCursorBuffer().setBuffer(new StringBuffer());
reader.getCursorBuffer().cursor = 0;
}
// This ensures nothing is ever displayed when redrawing the line
reader.setDefaultPrompt("");
reader.redrawLine();
// Now restore the line formatting settings back to their original
reader.setDefaultPrompt(shellPromptAccessor.getShellPrompt());
reader.getCursorBuffer().setBuffer(buffer);
reader.getCursorBuffer().cursor = cursor;
reader.printString(toDisplay);
Boolean prohibitingRedraw = redrawProhibit.get();
if (prohibitingRedraw == null) {
reader.redrawLine();
}
reader.flushConsole();
} catch (Exception e) {
reportError("Could not publish log message", e, Level.SEVERE.intValue());
}
}
private String toDisplay(final LogRecord event) {
StringBuilder sb = new StringBuilder();
String threadName;
String eventString;
if (includeThreadName && !userInterfaceThreadName.equals(Thread.currentThread().getName()) && !"".equals(Thread.currentThread().getName())) {
threadName = "[" + Thread.currentThread().getName() + "]";
// Build an event string that will indent nicely given the left hand side now contains a thread name
StringBuilder lineSeparatorAndIndentingString = new StringBuilder();
for (int i = 0; i <= threadName.length(); i++) {
lineSeparatorAndIndentingString.append(" ");
}
eventString = " " + getFormatter().format(event).replace(StringUtils.LINE_SEPARATOR, StringUtils.LINE_SEPARATOR + lineSeparatorAndIndentingString.toString());
if (eventString.endsWith(lineSeparatorAndIndentingString.toString())) {
eventString = eventString.substring(0, eventString.length() - lineSeparatorAndIndentingString.length());
}
} else {
threadName = "";
eventString = getFormatter().format(event);
}
if (ansiSupported) {
if (event.getLevel().intValue() >= Level.SEVERE.intValue()) {
sb.append(getANSIBuffer().reverse(threadName).red(eventString));
} else if (event.getLevel().intValue() >= Level.WARNING.intValue()) {
sb.append(getANSIBuffer().reverse(threadName).magenta(eventString));
} else if (event.getLevel().intValue() >= Level.INFO.intValue()) {
sb.append(getANSIBuffer().reverse(threadName).green(eventString));
} else {
sb.append(getANSIBuffer().reverse(threadName).append(eventString));
}
} else {
sb.append(threadName).append(eventString);
}
return sb.toString();
}
/**
* Makes text brighter if requested through system property 'roo.bright' and
* works around issue on Windows in using reverse() in combination with the
* Jansi lib, which leaves its 'negative' flag set unless reset explicitly.
*
* @return new patched ANSIBuffer
*/
static ANSIBuffer getANSIBuffer() {
final char esc = (char) 27;
return new ANSIBuffer() {
@Override
public ANSIBuffer reverse(final String str) {
if (OsUtils.isWindows()) {
return super.reverse(str).append(ANSICodes.attrib(esc));
}
return super.reverse(str);
};
@Override
public ANSIBuffer attrib(final String str, final int code) {
if (BRIGHT_COLORS && 30 <= code && code <= 37) {
// This is a color code: add a 'bright' code
return append(esc + "[" + code + ";1m").append(str).append(ANSICodes.attrib(0));
}
return super.attrib(str, code);
}
};
}
}

View File

@@ -0,0 +1,498 @@
package org.springframework.shell;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import jline.ANSIBuffer;
import jline.ANSIBuffer.ANSICodes;
import jline.ConsoleReader;
import jline.WindowsTerminal;
import org.springframework.roo.shell.AbstractShell;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.roo.shell.ExitShellRequest;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.event.ShellStatus;
import org.springframework.roo.shell.event.ShellStatus.Status;
import org.springframework.roo.shell.event.ShellStatusListener;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.ClassUtils;
import org.springframework.roo.support.util.FileCopyUtils;
import org.springframework.roo.support.util.IOUtils;
import org.springframework.roo.support.util.OsUtils;
import org.springframework.roo.support.util.StringUtils;
import org.springframework.shell.commands.HintCommands;
/**
* Uses the feature-rich <a href="http://sourceforge.net/projects/jline/">JLine</a> library to provide an interactive shell.
*
* <p>
* Due to Windows' lack of color ANSI services out-of-the-box, this implementation automatically detects the classpath
* presence of <a href="http://jansi.fusesource.org/">Jansi</a> and uses it if present. This library is not necessary
* for *nix machines, which support colour ANSI without any special effort. This implementation has been written to
* use reflection in order to avoid hard dependencies on Jansi.
*
* @author Ben Alex
* @since 1.0
*/
public abstract class JLineShell extends AbstractShell implements CommandMarker, Shell, Runnable {
// Constants
private static final String ANSI_CONSOLE_CLASSNAME = "org.fusesource.jansi.AnsiConsole";
private static final boolean JANSI_AVAILABLE = ClassUtils.isPresent(ANSI_CONSOLE_CLASSNAME, JLineShell.class.getClassLoader());
private static final boolean APPLE_TERMINAL = Boolean.getBoolean("is.apple.terminal");
private static final char ESCAPE = 27;
private static final String BEL = "\007";
//TODO make configurable
private static final String COMMAND_LINE_PROMPT = "spring>";
// Fields
private ConsoleReader reader;
private boolean developmentMode = false;
private FileWriter fileLog;
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
protected ShellStatusListener statusListener; // ROO-836
/** key: slot name, value: flashInfo instance */
private final Map<String, FlashInfo> flashInfoMap = new HashMap<String, FlashInfo>();
/** key: row number, value: eraseLineFromPosition */
private final Map<Integer, Integer> rowErasureMap = new HashMap<Integer, Integer>();
private boolean shutdownHookFired = false; // ROO-1599
public void run() {
try {
if (JANSI_AVAILABLE && OsUtils.isWindows()) {
try {
reader = createAnsiWindowsReader();
} catch (Exception e) {
// Try again using default ConsoleReader constructor
logger.warning("Can't initialize jansi AnsiConsole, falling back to default: " + e);
}
}
if (reader == null) {
reader = new ConsoleReader();
}
} catch (IOException ioe) {
throw new IllegalStateException("Cannot start console class", ioe);
}
setPromptPath(null);
JLineLogHandler handler = new JLineLogHandler(reader, this);
JLineLogHandler.prohibitRedraw(); // Affects this thread only
Logger mainLogger = Logger.getLogger("");
removeHandlers(mainLogger);
mainLogger.addHandler(handler);
reader.addCompletor(new JLineCompletorAdapter(getParser()));
reader.setBellEnabled(true);
if (Boolean.getBoolean("jline.nobell")) {
reader.setBellEnabled(false);
}
// reader.setDebug(new PrintWriter(new FileWriter("writer.debug", true)));
openFileLogIfPossible();
// Try to build previous command history from the project's log
try {
String logFileContents = FileCopyUtils.copyToString(new File("log.roo"));
String[] logEntries = logFileContents.split(StringUtils.LINE_SEPARATOR);
// LIFO
for (String logEntry : logEntries) {
if (!logEntry.startsWith("//")) {
reader.getHistory().addToHistory(logEntry);
}
}
} catch (IOException ignored) {}
flashMessageRenderer();
logger.info(version(null));
flash(Level.FINE, "Spring Roo " + versionInfo(), Shell.WINDOW_TITLE_SLOT);
//TODO make this configurable
logger.info("Welcome to Spring Shell. For assistance press " + completionKeys + " or type \"hint\" then hit ENTER.");
String startupNotifications = getStartupNotifications();
if (StringUtils.hasText(startupNotifications)) {
logger.info(startupNotifications);
}
setShellStatus(Status.STARTED);
// Monitor CTRL+C initiated shutdowns (ROO-1599)
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
shutdownHookFired = true;
// We don't need to closeShell(), as the shutdown hook in o.s.r.bootstrap.Main calls stop() which calls JLineShellComponent.deactivate() and that calls closeShell()
}
}, "Spring Roo JLine Shutdown Hook"));
// Handle any "execute-then-quit" operation
String rooArgs = System.getProperty("roo.args");
if (rooArgs != null && !"".equals(rooArgs)) {
setShellStatus(Status.USER_INPUT);
boolean success = executeCommand(rooArgs);
if (exitShellRequest == null) {
// The command itself did not specify an exit shell code, so we'll fall back to something sensible here
executeCommand("quit"); // ROO-839
exitShellRequest = success ? ExitShellRequest.NORMAL_EXIT : ExitShellRequest.FATAL_EXIT;
}
setShellStatus(Status.SHUTTING_DOWN);
} else {
// Normal RPEL processing
promptLoop();
}
}
public String getStartupNotifications() {
return null;
}
private void removeHandlers(final Logger l) {
Handler[] handlers = l.getHandlers();
if (handlers != null && handlers.length > 0) {
for (Handler h : handlers) {
l.removeHandler(h);
}
}
}
@Override
public void setPromptPath(final String path) {
setPromptPath(path, false);
}
@Override
public void setPromptPath(final String path, final boolean overrideStyle) {
if (reader.getTerminal().isANSISupported()) {
ANSIBuffer ansi = JLineLogHandler.getANSIBuffer();
if (path == null || "".equals(path)) {
shellPrompt = ansi.yellow(COMMAND_LINE_PROMPT).toString();
} else {
if (overrideStyle) {
ansi.append(path);
} else {
ansi.cyan(path);
}
shellPrompt = ansi.yellow(" " + COMMAND_LINE_PROMPT).toString();
}
} else {
// The superclass will do for this non-ANSI terminal
super.setPromptPath(path);
}
// The shellPrompt is now correct; let's ensure it now gets used
reader.setDefaultPrompt(JLineShell.shellPrompt);
}
private ConsoleReader createAnsiWindowsReader() throws Exception {
// Get decorated OutputStream that parses ANSI-codes
final PrintStream ansiOut = (PrintStream) ClassUtils.forName(ANSI_CONSOLE_CLASSNAME, JLineShell.class.getClassLoader()).getMethod("out").invoke(null);
WindowsTerminal ansiTerminal = new WindowsTerminal() {
@Override
public boolean isANSISupported() {
return true;
}
};
ansiTerminal.initializeTerminal();
// Make sure to reset the original shell's colors on shutdown by closing the stream
statusListener = new ShellStatusListener() {
public void onShellStatusChange(final ShellStatus oldStatus, final ShellStatus newStatus) {
if (newStatus.getStatus().equals(Status.SHUTTING_DOWN)) {
ansiOut.close();
}
}
};
addShellStatusListener(statusListener);
return new ConsoleReader(new FileInputStream(FileDescriptor.in), new PrintWriter(new OutputStreamWriter(ansiOut,
// Default to Cp850 encoding for Windows console output (ROO-439)
System.getProperty("jline.WindowsTerminal.output.encoding", "Cp850"))), null, ansiTerminal);
}
private void flashMessageRenderer() {
if (!reader.getTerminal().isANSISupported()) {
return;
}
// Setup a thread to ensure flash messages are displayed and cleared correctly
Thread t = new Thread(new Runnable() {
public void run() {
while (!shellStatus.getStatus().equals(Status.SHUTTING_DOWN) && !shutdownHookFired) {
synchronized (flashInfoMap) {
long now = System.currentTimeMillis();
Set<String> toRemove = new HashSet<String>();
for (String slot : flashInfoMap.keySet()) {
FlashInfo flashInfo = flashInfoMap.get(slot);
if (flashInfo.flashMessageUntil < now) {
// Message has expired, so clear it
toRemove.add(slot);
doAnsiFlash(flashInfo.rowNumber, Level.ALL, "");
} else {
// The expiration time for this message has not been reached, so preserve it
doAnsiFlash(flashInfo.rowNumber, flashInfo.flashLevel, flashInfo.flashMessage);
}
}
for (String slot : toRemove) {
flashInfoMap.remove(slot);
}
}
try {
Thread.sleep(200);
} catch (InterruptedException ignore) {
}
}
}
}, "Spring Roo JLine Flash Message Manager");
t.start();
}
@Override
public void flash(final Level level, final String message, final String slot) {
Assert.notNull(level, "Level is required for a flash message");
Assert.notNull(message, "Message is required for a flash message");
Assert.hasText(slot, "Slot name must be specified for a flash message");
if (Shell.WINDOW_TITLE_SLOT.equals(slot)) {
if (reader != null && reader.getTerminal().isANSISupported()) {
// We can probably update the window title, as requested
if (StringUtils.isBlank(message)) {
System.out.println("No text");
}
ANSIBuffer buff = JLineLogHandler.getANSIBuffer();
buff.append(ESCAPE + "]0;").append(message).append(BEL);
String stg = buff.toString();
try {
reader.printString(stg);
reader.flushConsole();
} catch (IOException ignored) {}
}
return;
}
if ((reader != null && !reader.getTerminal().isANSISupported())) {
super.flash(level, message, slot);
return;
}
synchronized (flashInfoMap) {
FlashInfo flashInfo = flashInfoMap.get(slot);
if ("".equals(message)) {
// Request to clear the message, but give the user some time to read it first
if (flashInfo == null) {
// We didn't have a record of displaying it in the first place, so just quit
return;
}
flashInfo.flashMessageUntil = System.currentTimeMillis() + 1500;
} else {
// Display this message displayed until further notice
if (flashInfo == null) {
// Find a row for this new slot; we basically take the first line number we discover
flashInfo = new FlashInfo();
flashInfo.rowNumber = Integer.MAX_VALUE;
outer: for (int i = 1; i < Integer.MAX_VALUE; i++) {
for (FlashInfo existingFlashInfo : flashInfoMap.values()) {
if (existingFlashInfo.rowNumber == i) {
// Veto, let's try the new candidate row number
continue outer;
}
}
// If we got to here, nobody owns this row number, so use it
flashInfo.rowNumber = i;
break outer;
}
// Store it
flashInfoMap.put(slot, flashInfo);
}
// Populate the instance with the latest data
flashInfo.flashMessageUntil = Long.MAX_VALUE;
flashInfo.flashLevel = level;
flashInfo.flashMessage = message;
// Display right now
doAnsiFlash(flashInfo.rowNumber, flashInfo.flashLevel, flashInfo.flashMessage);
}
}
}
// Externally synchronized via the two calling methods having a mutex on flashInfoMap
private void doAnsiFlash(final int row, final Level level, final String message) {
ANSIBuffer buff = JLineLogHandler.getANSIBuffer();
if (APPLE_TERMINAL) {
buff.append(ESCAPE + "7");
} else {
buff.append(ANSICodes.save());
}
// Figure out the longest line we're presently displaying (or were) and erase the line from that position
int mostFurtherLeftColNumber = Integer.MAX_VALUE;
for (Integer candidate : rowErasureMap.values()) {
if (candidate < mostFurtherLeftColNumber) {
mostFurtherLeftColNumber = candidate;
}
}
if (mostFurtherLeftColNumber == Integer.MAX_VALUE) {
// There is nothing to erase
} else {
buff.append(ANSICodes.gotoxy(row, mostFurtherLeftColNumber));
buff.append(ANSICodes.clreol()); // Clear what was present on the line
}
if (("".equals(message))) {
// They want the line blank; we've already achieved this if needed via the erasing above
// Just need to record we no longer care about this line the next time doAnsiFlash is invoked
rowErasureMap.remove(row);
} else {
if (shutdownHookFired) {
return; // ROO-1599
}
// They want some message displayed
int startFrom = reader.getTermwidth() - message.length() + 1;
if (startFrom < 1) {
startFrom = 1;
}
buff.append(ANSICodes.gotoxy(row, startFrom));
buff.reverse(message);
// Record we want to erase from this positioning next time (so we clean up after ourselves)
rowErasureMap.put(row, startFrom);
}
if (APPLE_TERMINAL) {
buff.append(ESCAPE + "8");
} else {
buff.append(ANSICodes.restore());
}
String stg = buff.toString();
try {
reader.printString(stg);
reader.flushConsole();
} catch (IOException ignored) {}
}
public void promptLoop() {
setShellStatus(Status.USER_INPUT);
String line;
try {
while (exitShellRequest == null && ((line = reader.readLine()) != null)) {
JLineLogHandler.resetMessageTracking();
setShellStatus(Status.USER_INPUT);
if ("".equals(line)) {
continue;
}
executeCommand(line);
}
} catch (IOException ioe) {
throw new IllegalStateException("Shell line reading failure", ioe);
}
setShellStatus(Status.SHUTTING_DOWN);
}
public void setDevelopmentMode(final boolean developmentMode) {
JLineLogHandler.setIncludeThreadName(developmentMode);
JLineLogHandler.setSuppressDuplicateMessages(!developmentMode); // We want to see duplicate messages during development time (ROO-1873)
this.developmentMode = developmentMode;
}
public boolean isDevelopmentMode() {
return this.developmentMode;
}
private void openFileLogIfPossible() {
try {
fileLog = new FileWriter("log.roo", true);
// First write, so let's record the date and time of the first user command
fileLog.write("// Spring Roo " + versionInfo() + " log opened at " + df.format(new Date()) + "\n");
fileLog.flush();
} catch (IOException ignoreIt) {
}
}
@Override
protected void logCommandToOutput(final String processedLine) {
if (fileLog == null) {
openFileLogIfPossible();
if (fileLog == null) {
// Still failing, so give up
return;
}
}
try {
fileLog.write(processedLine + "\n"); // Unix line endings only from Roo
fileLog.flush(); // So tail -f will show it's working
if (getExitShellRequest() != null) {
// Shutting down, so close our file (we can always reopen it later if needed)
fileLog.write("// Spring Roo " + versionInfo() + " log closed at " + df.format(new Date()) + "\n");
IOUtils.closeQuietly(fileLog);
fileLog = null;
}
} catch (IOException ignoreIt) {
}
}
/**
* Obtains the "roo.home" from the system property, falling back to the current working directory if missing.
*
* @return the 'roo.home' system property
*/
@Override
protected String getHomeAsString() {
String rooHome = System.getProperty("roo.home");
if (rooHome == null) {
try {
rooHome = new File(".").getCanonicalPath();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
return rooHome;
}
/**
* Should be called by a subclass before deactivating the shell.
*/
protected void closeShell() {
// Notify we're closing down (normally our status is already shutting_down, but if it was a CTRL+C via the o.s.r.bootstrap.Main hook)
setShellStatus(Status.SHUTTING_DOWN);
if (statusListener != null) {
removeShellStatusListener(statusListener);
}
}
private static class FlashInfo {
String flashMessage;
long flashMessageUntil;
Level flashLevel;
int rowNumber;
}
}

View File

@@ -0,0 +1,94 @@
package org.springframework.shell;
import static org.springframework.roo.support.util.StringUtils.LINE_SEPARATOR;
import java.net.URL;
import java.util.Collection;
import org.springframework.context.Lifecycle;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.ExecutionStrategy;
import org.springframework.roo.shell.Parser;
import org.springframework.roo.shell.SimpleParser;
/**
* Launcher for {@link JLineShell}.
*
* @author Ben Alex
* @since 1.1
*/
public class JLineShellComponent extends JLineShell implements Lifecycle {
private volatile boolean running = false;
// Fields
private ExecutionStrategy executionStrategy = new SimpleExecutionStrategy(); //ProcessManagerHostedExecutionStrategy is not what i think we need outside of Roo.
private SimpleParser parser = new SimpleParser();
// Dont' need this, used to get twitter status.
//@Reference private UrlInputStreamService urlInputStreamService;
//
public SimpleParser getSimpleParser() {
return parser;
}
public void start() {
Thread thread = new Thread(this, "Spring Roo JLine Shell");
thread.start();
running=true;
}
public void stop() {
closeShell();
running=false;
}
public boolean isRunning() {
return running;
}
@Override
protected Collection<URL> findResources(final String path) {
// For an OSGi bundle search, we add the root prefix to the given path
throw new UnsupportedOperationException("TODO: need to use standard classpath search");
//return OSGiUtils.findEntriesByPath(context.getBundleContext(), OSGiUtils.ROOT_PATH + path);
}
@Override
protected ExecutionStrategy getExecutionStrategy() {
return executionStrategy;
}
@Override
protected Parser getParser() {
return parser;
}
@Override
public String getStartupNotifications() {
return null;
}
@CliCommand(value = { "version" }, help = "Displays shell version")
public String version(@CliOption(key = "", help = "Special version flags") final String extra) {
StringBuilder sb = new StringBuilder();
sb.append(" _____ _ ").append(LINE_SEPARATOR);
sb.append("/ ___| (_)").append(LINE_SEPARATOR);
sb.append("\\ `--, _ __ _ __ _ _ __ __ _ ").append(LINE_SEPARATOR);
sb.append(" `--. \\ '_ \\| '__| | '_ \\ / _` |").append(LINE_SEPARATOR);
sb.append("/\\__/ / |_) | | | | | | | (_| |").append(LINE_SEPARATOR);
sb.append("\\____/| .__/|_| |_|_| |_|\\__, |").append(LINE_SEPARATOR);
sb.append(" | | __/ |").append(LINE_SEPARATOR);
sb.append(" |_| |___/ ").append(" ").append(versionInfo()).append(LINE_SEPARATOR);
sb.append(LINE_SEPARATOR);
return sb.toString();
}
}

View File

@@ -0,0 +1,7 @@
package org.springframework.shell;
public interface Service {
String getMessage();
}

View File

@@ -0,0 +1,37 @@
package org.springframework.shell;
import org.springframework.roo.shell.ExecutionStrategy;
import org.springframework.roo.shell.ParseResult;
import org.springframework.roo.support.util.Assert;
import org.springframework.roo.support.util.ReflectionUtils;
public class SimpleExecutionStrategy implements ExecutionStrategy {
private final Class<?> mutex = SimpleExecutionStrategy.class;
public Object execute(ParseResult parseResult) throws RuntimeException {
Assert.notNull(parseResult, "Parse result required");
synchronized (mutex) {
Assert.isTrue(isReadyForCommands(), "ProcessManagerHostedExecutionStrategy not yet ready for commands");
return ReflectionUtils.invokeMethod(parseResult.getMethod(), parseResult.getInstance(), parseResult.getArguments());
/*
Assert.isTrue(isReadyForCommands(), "ProcessManagerHostedExecutionStrategy not yet ready for commands");
return processManager.execute(new CommandCallback<Object>() {
public Object callback() {
return ReflectionUtils.invokeMethod(parseResult.getMethod(), parseResult.getInstance(), parseResult.getArguments());
}
});*/
}
}
public boolean isReadyForCommands() {
// TODO Auto-generated method stub
return true;
}
public void terminate() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2011 VMware, Inc. All rights reserved. -- VMware Confidential
*/
package org.springframework.shell;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
/**
* Not really used much, but keeping for future use
*
* @author vnagaraja
*/
public class SimpleShellCommandLineOptions {
public static final String DEFAULT_APP_CTX = "classpath*:/META-INF/spring/app-context.xml";
String applicationContextLocation = DEFAULT_APP_CTX;
String[] executeThenQuit = null;
Map<String, String> extraSystemProperties = new HashMap<String, String>();
public static SimpleShellCommandLineOptions parseCommandLine(String[] args) throws IOException {
SimpleShellCommandLineOptions options = new SimpleShellCommandLineOptions();
List<String> commands = new ArrayList<String>();
int i = 0;
while (i < args.length) {
String arg = args[i++];
if (arg.equals("-environment")) {
String environment = args[i++];
options.extraSystemProperties.put("napa.application.profile", environment);
} else if (arg.equals("-ctx")) {
options.applicationContextLocation = args[i++];
} else if (arg.equals("-cmdfile")) {
File f = new File(args[i++]);
commands.addAll(FileUtils.readLines(f));
} else {
i--;
break;
}
}
StringBuilder sb = new StringBuilder();
for (; i < args.length; i++) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append(args[i]);
}
if(sb.length()>0) {
String[] cmdLineCommands = sb.toString().split(";");
for(String s : cmdLineCommands) {
//add any command line commands after the commands loaded from the file
commands.add(s.trim());
}
}
if(commands.size()>0)
options.executeThenQuit = commands.toArray(new String[commands.size()]);
return options;
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.shell.commands;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.roo.shell.ExitShellRequest;
import org.springframework.stereotype.Component;
@Component
public class EssentialCommands implements CommandMarker {
@CliCommand(value={"exit", "quit"}, help="Exits the shell")
public ExitShellRequest quit() {
return ExitShellRequest.NORMAL_EXIT;
}
}

View File

@@ -0,0 +1,23 @@
package org.springframework.shell.commands;
import org.springframework.roo.shell.CliAvailabilityIndicator;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldCommands implements CommandMarker {
@CliAvailabilityIndicator({"hw help"})
public boolean isCommandAvailable() {
return true;
}
@CliCommand(value = "hw echo", help = "Print a hello world message")
public void config(
@CliOption(key = { "message" }, mandatory = true, help = "The hello world message") final String message) {
System.out.println("Hello world " + message);
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.shell.commands;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.shell.CliCommand;
import org.springframework.roo.shell.CliOption;
import org.springframework.roo.shell.CommandMarker;
import org.springframework.stereotype.Component;
@Component
public class HintCommands implements CommandMarker {
// Fields
@Autowired private HintOperations hintOperations;
@CliCommand(value = "hint", help = "Provides step-by-step hints and context-sensitive guidance")
public String hint(
@CliOption(key = { "topic", "" }, mandatory = false, unspecifiedDefaultValue = "", optionContext = "disable-string-converter,topics", help = "The topic for which advice should be provided") final String topic) {
return hintOperations.hint(topic);
}
}

View File

@@ -0,0 +1,37 @@
package org.springframework.shell.commands;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.shell.Completion;
import org.springframework.roo.shell.Converter;
import org.springframework.roo.shell.MethodTarget;
import org.springframework.stereotype.Component;
/**
* {@link Converter} for {@link String} that understands the "topics" option context.
*
* @author Ben Alex
* @since 1.1
*/
@Component
public class HintConverter implements Converter<String> {
// Fields
@Autowired private HintOperations hintOperations;
public String convertFromText(final String value, final Class<?> requiredType, final String optionContext) {
return value;
}
public boolean getAllPossibleValues(final List<Completion> completions, final Class<?> requiredType, final String existingData, final String optionContext, final MethodTarget target) {
for (String currentTopic : hintOperations.getCurrentTopics()) {
completions.add(new Completion(currentTopic));
}
return false;
}
public boolean supports(final Class<?> requiredType, final String optionContext) {
return String.class.isAssignableFrom(requiredType) && optionContext.contains("topics");
}
}

View File

@@ -0,0 +1,10 @@
package org.springframework.shell.commands;
import java.util.SortedSet;
public interface HintOperations {
String hint(String topic);
SortedSet<String> getCurrentTopics();
}

View File

@@ -0,0 +1,49 @@
package org.springframework.shell.commands;
import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.SortedSet;
import java.util.TreeSet;
import org.springframework.roo.shell.AbstractShell;
import org.springframework.roo.support.util.StringUtils;
import org.springframework.stereotype.Component;
@Component
public class ResourceBundleHintOperations implements HintOperations {
private static ResourceBundle bundle = ResourceBundle.getBundle(HintCommands.class.getName());
public String hint(String topic) {
if (StringUtils.isBlank(topic)) {
topic = determineTopic();
}
try {
String message = bundle.getString(topic);
return message.replace("\r", StringUtils.LINE_SEPARATOR).replace("${completion_key}", AbstractShell.completionKeys);
} catch (MissingResourceException exception) {
return "Cannot find topic '" + topic + "'";
}
}
public SortedSet<String> getCurrentTopics() {
SortedSet<String> result = new TreeSet<String>();
String topic = determineTopic();
if ("general".equals(topic)) {
for (Enumeration<String> keys = bundle.getKeys(); keys.hasMoreElements();) {
result.add(keys.nextElement());
}
// result.addAll(bundle.keySet()); ResourceBundle.keySet() method in JDK 6+
} else {
result.add(topic);
}
return result;
}
private String determineTopic() {
return "start";
//return "general";
}
}

View File

@@ -0,0 +1,18 @@
package org.springframework.shell.converters;
import java.io.File;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.roo.shell.Shell;
import org.springframework.roo.shell.converters.FileConverter;
import org.springframework.stereotype.Component;
//@Component
public class SimpleFileConverter extends FileConverter {
@Autowired private Shell shell;
@Override
protected File getWorkingDirectory() {
return shell.getHome();
}
}

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<description>Example configuration to get you started.</description>
<context:component-scan base-package="org.springframework.shell" />
<bean class="org.springframework.roo.shell.converters.StringConverter"/>
<bean class="org.springframework.roo.shell.converters.AvailableCommandsConverter"/>
<bean class="org.springframework.roo.shell.converters.BigDecimalConverter"/>
<bean class="org.springframework.roo.shell.converters.BigIntegerConverter"/>
<bean class="org.springframework.roo.shell.converters.BooleanConverter"/>
<bean class="org.springframework.roo.shell.converters.CharacterConverter"/>
<bean class="org.springframework.roo.shell.converters.DateConverter"/>
<bean class="org.springframework.roo.shell.converters.DoubleConverter"/>
<bean class="org.springframework.roo.shell.converters.EnumConverter"/>
<bean class="org.springframework.roo.shell.converters.FloatConverter"/>
<bean class="org.springframework.roo.shell.converters.IntegerConverter"/>
<bean class="org.springframework.roo.shell.converters.LocaleConverter"/>
<bean class="org.springframework.roo.shell.converters.LongConverter"/>
<bean class="org.springframework.roo.shell.converters.ShortConverter"/>
<bean class="org.springframework.roo.shell.converters.StaticFieldConverterImpl"/>
<bean class="org.springframework.roo.shell.converters.StringConverter"/>
</beans>

View File

@@ -0,0 +1,14 @@
topics=The following hints are available to help you use the vFabric shell:\r\r general, start, persistence, entities, fields, relationships, \r web mvc, finders, eclipse, logging, gwt\r\rJust type 'hint topic_name' (without quotes) to view a specific hint.
general=At this stage of the project, you have a few options:\r\r * List all hint topics via 'hint topics'\r * Create more fields with 'hint fields'\r * Create more entities with 'hint entities'\r * Create a web controller with 'hint web mvc'\r * Create dynamic finders with 'hint finders'\r * Setup your logging levels via 'hint logging'\r * Run tests via Maven (type 'perform tests')\r * Build a deployment artifact (type 'perform package')\r * Learn about Eclipse integration by typing 'hint eclipse'\r * Add support for Google Web Toolkit via 'hint gwt'\r * Discover all Roo commands by typing 'help'
start=Welcome to the Spring Shell! To start your journey.....
persistence=Roo requires the installation of a persistence configuration,\rfor example, JPA or MongoDB.\r\rFor JPA, type 'jpa setup' and then hit ${completion_key} three times.\rWe suggest you type 'H' then ${completion_key} to complete "HIBERNATE".\rAfter the --provider, press ${completion_key} twice for database choices.\rFor testing purposes, type (or ${completion_key}) HYPERSONIC_IN_MEMORY.\rIf you press ${completion_key} again, you'll see there are no more options.\rAs such, you're ready to press ENTER to execute the command.\r\rOnce JPA is installed, type 'hint' and ENTER for the next suggestion.\n\nSimilarly, for MongoDB persistence, type 'mongo setup' and ENTER.
entities=You can create entities either via Roo or your IDE.\nUsing the Roo shell is fast and easy, especially thanks to the ${completion_key} completion.\r\rStart by typing 'ent' and then hitting ${completion_key} twice.\rEnter the --class in the form '~.domain.MyEntityClassName'\rIn Roo, '~' means the --topLevelPackage you specified via 'create project'.\r\rAfter specify a --class argument, press SPACE then ${completion_key}. Note nothing appears.\rBecause nothing appears, it means you've entered all mandatory arguments.\rHowever, optional arguments do exist for this command (and most others in Roo).\rTo see the optional arguments, type '--' and then hit ${completion_key}. Mostly you won't\nneed any optional arguments, but let's select the --testAutomatically option\nand hit ENTER. You can always use this approach to view optional arguments.\r\rAfter creating an entity, use 'hint' for the next suggestion.
fields=You can add fields to your entities using either Roo or your IDE.\n\nTo add a new field, type 'field' and then hit ${completion_key}. Be sure to select\nyour entity and provide a legal Java field name. Use ${completion_key} to find an entity\nname, and '~' to refer to the top level package. Also remember to use ${completion_key}\nto access each mandatory argument for the command.\n\nAfter completing the mandatory arguments, press SPACE, type '--' and then ${completion_key}.\nThe optional arguments shown reflect official JSR 303 Validation constraints.\nFeel free to use an optional argument, or delete '--' and hit ENTER.\n\nIf creating multiple fields, use the UP arrow to access command history.\n\nAfter adding your fields, type 'hint' for the next suggestion.\nTo learn about setting up many-to-one fields, type 'hint relationships'.
relationships=You create persistent relationships via 'field set' and 'field reference'.\n\nFor example, consider the typical Order <-> LineItem case:\n\n ENTITY: Order ENTITY: LineItem\n Set<LineItem> items Order order\n\nTo setup this relationship in Roo, you would use:\n\nfield set --fieldName items --class Order --type LineItem --mappedBy order\nfield reference --fieldName order --class LineItem --type Order --notNull\n\nLearn about fields addition using 'hint fields'.
web\ mvc=Creating RESTful Spring MVC web controllers is quick and easy using Roo.\nControllers can be made that automatically expose an entity. Alternately, we\ncan create a stub, empty controller for you to finish off.\n\nFor the former, type 'web mvc setup' and hit ENTER followed by 'web mvc scaffold' and hit ${completion_key} three times.\nThe --class is the controller name; it need not reflect an entity name.\nWe suggest putting controllers under a '~.web' package to improve maintenance.\nYou can also specify the --backingType the controller should expose.\n\nAfter creating a controller, use 'hint' for further suggestions.
finders=Roo can automatically create complex, type-safe finder methods.\n\nBe sure to add fields to your entity before creating finders.\nLearn how to add fields by typing 'hint fields'.\n\nTo view available finders, type 'finder list' then ${completion_key}.\nNext select your entity class and hit ENTER. Names are then displayed.\nYou can see even more finder combinations by using --depth 2 or higher.\n\nTo add a finder, type 'finder add' and hit ${completion_key} twice.\nSpecify a --finderName from the earlier displayed output of 'list finders for'.\n\nFor more hints, type 'hint' and hit ENTER.
eclipse=It's easy to use your project in Eclipse or SpringSource Tool Suite (STS).\r\rTo set this up, you'll need to use the command prompt and then Eclipse itself:\r\r 1. Start by typing 'exit', to leave the Roo shell\r 2. Type 'mvn eclipse:clean eclipse:eclipse' to create Eclipse project files\r 3. Load Eclipse, then File > Import > Existing Projects Into Workspace\r 4. Ensure AJDT 1.6.5 or above is installed in Eclipse\r 5. Enable Window > Preferences > General > Workspace > Refresh Automatically\r 6. Finally, restart Roo (type 'roo' at the OS prompt) and enter 'hint'\r\rPlease note if you have the m2eclipse plugin installed, you need only select\rImport > Maven Projects from the Eclipse Import menu.\r\rFor the best Eclipse experience, we recommend SpringSource Tool Suite (STS):\r\r * Graphical editing of Roo commands\r * No need to use the Roo shell\r * Immediate importing of Roo projects\r * Full Spring projects integration\r * Many other productivity-increasing features\r\rDownload STS from http://springsource.com (it's free!).\r\rYou can also use 'perform eclipse' instead of leaving the Roo interface.
logging=You can easily configure logging levels in your project.\r\rRoo will update the log4j.properties file to control your logging.\r\rType 'logging setup' then hit ${completion_key} twice. We suggest 'DEBUG' level.\rYou may wish to specify the optional --package argument (defaults to 'ALL').\r\rRemember to type 'hint' for further hints and suggestions.
gwt=It's easy to create a GWT client in your project.\r\rJust type 'web gwt setup' and press ENTER.\r\rNote Roo's GWT support outputs GWT 2.2 applications.

25
src/site/site.xml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="Spring Batch: ${project.name}">
<bannerLeft>
<name>Spring Sample: ${project.name}</name>
<href>index.html</href>
</bannerLeft>
<skin>
<groupId>org.springframework.maven.skins</groupId>
<artifactId>maven-spring-skin</artifactId>
<version>1.0.5</version>
</skin>
<body>
<links>
<item name="${project.name}" href="index.html"/>
</links>
<menu ref="reports"/>
</body>
</project>

View File

@@ -0,0 +1,6 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n