diff --git a/.gitignore b/.gitignore index acb06b14..48ccb9bb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ target/ /log.roo /bin/ +/*.log diff --git a/src/main/java/org/springframework/shell/Bootstrap.java b/src/main/java/org/springframework/shell/Bootstrap.java index a627e526..44a487a0 100644 --- a/src/main/java/org/springframework/shell/Bootstrap.java +++ b/src/main/java/org/springframework/shell/Bootstrap.java @@ -44,9 +44,11 @@ public class Bootstrap { private static Bootstrap bootstrap; private JLineShellComponent shell; - private ConfigurableApplicationContext ctx; + private AnnotationConfigApplicationContext parentApplicationContext; private static StopWatch sw = new StopWatch("Spring Shell"); - private static SimpleShellCommandLineOptions options; + + //Initialize to empty option to facilitate testing of Bootstrap class + private static SimpleShellCommandLineOptions options = new SimpleShellCommandLineOptions(); public static void main(String[] args) throws IOException { sw.start(); @@ -69,30 +71,24 @@ public class Bootstrap { } public Bootstrap(String applicationContextLocation) throws IOException { - //setupLogging(); - createApplicationContext(); - - shell = ctx.getBean("shell", JLineShellComponent.class); - shell.setApplicationContext(ctx); + AnnotationConfigApplicationContext parentApplicationContext = new AnnotationConfigApplicationContext(); + configureParentApplicationContext(parentApplicationContext); + + ConfigurableApplicationContext childPluginApplicationContext = createChildPluginApplicationContext(parentApplicationContext); + + parentApplicationContext.refresh(); + childPluginApplicationContext.refresh(); + + shell = childPluginApplicationContext.getBean("shell", JLineShellComponent.class); shell.setHistorySize(options.historySize); if (options.executeThenQuit != null) { shell.setPrintBanner(false); } - - Map commands = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, CommandMarker.class); - for (CommandMarker command : commands.values()) { - shell.getSimpleParser().add(command); - } - - Map converters = BeanFactoryUtils.beansOfTypeIncludingAncestors(ctx, Converter.class); - for (Converter converter : converters.values()) { - shell.getSimpleParser().add(converter); - } } - private void createApplicationContext() { - // create parent/base ctx - AnnotationConfigApplicationContext annctx = new AnnotationConfigApplicationContext(); + private void configureParentApplicationContext(AnnotationConfigApplicationContext annctx) { + // create parent/base childPluginApplicationContext + createAndRegisterBeanDefinition(annctx, org.springframework.shell.converters.StringConverter.class); createAndRegisterBeanDefinition(annctx, org.springframework.shell.converters.AvailableCommandsConverter.class); @@ -115,10 +111,7 @@ public class Bootstrap { annctx.scan("org.springframework.shell.commands"); annctx.scan("org.springframework.shell.converters"); annctx.scan("org.springframework.shell.plugin.support"); - annctx.refresh(); - ctx = initPluginApplicationContext(annctx); - ctx.refresh(); } /** @@ -127,9 +120,9 @@ public class Bootstrap { * @param annctx parent ApplicationContext in core spring shell * @return new ApplicationContext in the plugin with core spring shell's context as parent */ - private ConfigurableApplicationContext initPluginApplicationContext(AnnotationConfigApplicationContext annctx) { + private ConfigurableApplicationContext createChildPluginApplicationContext(AnnotationConfigApplicationContext annctx) { return new ClassPathXmlApplicationContext( - new String[] { "classpath*:/META-INF/spring/spring-shell-plugin.xml" }, true, annctx); + new String[] { "classpath*:/META-INF/spring/spring-shell-plugin.xml" }, false, annctx); } protected void createAndRegisterBeanDefinition(AnnotationConfigApplicationContext annctx, Class clazz) { @@ -194,11 +187,15 @@ public class Bootstrap { shell.waitForComplete(); } - ctx.close(); + parentApplicationContext.close(); sw.stop(); if (shell.isDevelopmentMode()) { System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms"); } return exitShellRequest; } + + JLineShellComponent getJLineShellComponent() { + return shell; + } } \ No newline at end of file diff --git a/src/main/java/org/springframework/shell/core/JLineShellComponent.java b/src/main/java/org/springframework/shell/core/JLineShellComponent.java index 5230b69f..f918ceca 100644 --- a/src/main/java/org/springframework/shell/core/JLineShellComponent.java +++ b/src/main/java/org/springframework/shell/core/JLineShellComponent.java @@ -25,8 +25,12 @@ import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.Lifecycle; +import org.springframework.context.SmartLifecycle; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.io.Resource; import org.springframework.shell.plugin.BannerProvider; @@ -40,7 +44,7 @@ import org.springframework.shell.plugin.PromptProvider; * @author Ben Alex * @since 1.1 */ -public class JLineShellComponent extends JLineShell implements Lifecycle { +public class JLineShellComponent extends JLineShell implements SmartLifecycle, ApplicationContextAware, InitializingBean { private volatile boolean running = false; private Thread shellThread; @@ -59,15 +63,26 @@ public class JLineShellComponent extends JLineShell implements Lifecycle { // Fields - private ExecutionStrategy executionStrategy = new SimpleExecutionStrategy(); //ProcessManagerHostedExecutionStrategy is not what i think we need outside of Roo. + private ExecutionStrategy executionStrategy = new SimpleExecutionStrategy(); private SimpleParser parser = new SimpleParser(); - public SimpleParser getSimpleParser() { return parser; } - + public boolean isAutoStartup() { + return false; + } + + public void stop(Runnable callback) { + stop(); + callback.run(); + } + + public int getPhase() { + return 1; + } + public void start() { //customizePlug must run before start thread to take plugin's configuration into effect customizePlugin(); @@ -87,6 +102,19 @@ public class JLineShellComponent extends JLineShell implements Lifecycle { public boolean isRunning() { return running; } + + public void afterPropertiesSet() { + + Map commands = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, CommandMarker.class); + for (CommandMarker command : commands.values()) { + getSimpleParser().add(command); + } + + Map converters = BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, Converter.class); + for (Converter converter : converters.values()) { + getSimpleParser().add(converter); + } + } /** * wait the shell command to complete by typing "quit" or "exit" diff --git a/src/main/java/org/springframework/shell/core/SimpleParser.java b/src/main/java/org/springframework/shell/core/SimpleParser.java index af8229e2..cfd22910 100644 --- a/src/main/java/org/springframework/shell/core/SimpleParser.java +++ b/src/main/java/org/springframework/shell/core/SimpleParser.java @@ -20,6 +20,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -1041,6 +1042,12 @@ public class SimpleParser implements Parser { } } } + + public final Set getCommandMarkers() { + synchronized (mutex) { + return Collections.unmodifiableSet(commands); + } + } public final void remove(final CommandMarker command) { synchronized (mutex) { @@ -1067,4 +1074,10 @@ public class SimpleParser implements Parser { converters.remove(converter); } } + + public final Set> getConverters() { + synchronized (mutex) { + return Collections.unmodifiableSet(converters); + } + } } diff --git a/src/test/java/org/springframework/shell/BootstrapTest.java b/src/test/java/org/springframework/shell/BootstrapTest.java new file mode 100644 index 00000000..199ef831 --- /dev/null +++ b/src/test/java/org/springframework/shell/BootstrapTest.java @@ -0,0 +1,38 @@ +package org.springframework.shell; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.util.logging.Logger; + +import junit.framework.Assert; + +import org.junit.Test; +import org.springframework.shell.core.JLineShellComponent; +import org.springframework.shell.event.ShellStatus; +import org.springframework.shell.event.ShellStatus.Status; +import org.springframework.shell.support.logging.HandlerUtils; + +public class BootstrapTest { + + @Test + public void test() throws IOException, InterruptedException { + try { + Bootstrap bootstrap = new Bootstrap(null); + JLineShellComponent shell = bootstrap.getJLineShellComponent(); + shell.start(); + while (shell.getShellStatus().getStatus() != ShellStatus.Status.USER_INPUT) { + Thread.sleep(100); + } + Assert.assertEquals("Number of CommandMarkers is incorrect", 4, shell.getSimpleParser().getCommandMarkers().size()); + Assert.assertEquals("Number of Converters is incorrect", 16, shell.getSimpleParser().getConverters().size()); + shell.stop(); + } catch (RuntimeException t) { + throw t; + } finally { + HandlerUtils.flushAllHandlers(Logger.getLogger("")); + } + + } + +}