diff --git a/gradle.properties b/gradle.properties index dd9b71dc..e53150ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,4 @@ mockitoVersion = 1.8.5 # -------------------- # Project wide version # -------------------- -version=1.0.1.BUILD-SNAPSHOT \ No newline at end of file +version=1.1.0.BUILD-SNAPSHOT diff --git a/samples/helloworld/gradle.properties b/samples/helloworld/gradle.properties index 02cfd69b..6d9730c7 100644 --- a/samples/helloworld/gradle.properties +++ b/samples/helloworld/gradle.properties @@ -1,4 +1,4 @@ -springShellVersion = 1.0.1.BUILD-SNAPSHOT +springShellVersion = 1.1.0.BUILD-SNAPSHOT log4jVersion = 1.2.17 version = 1.0.0.RELEASE diff --git a/samples/helloworld/pom.xml b/samples/helloworld/pom.xml index 7f6a5df0..bc0880cf 100644 --- a/samples/helloworld/pom.xml +++ b/samples/helloworld/pom.xml @@ -11,7 +11,7 @@ http://maven.apache.org - 1.0.0.RELEASE + 1.1.0.BUILD-SNAPSHOT org.springframework.shell.Bootstrap 1.2.17 diff --git a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/Main.java b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/Main.java new file mode 100644 index 00000000..1ce398e3 --- /dev/null +++ b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/Main.java @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell.samples.helloworld; + +import java.io.IOException; + +import org.springframework.shell.Bootstrap; + +/** + * Driver class to run the helloworld example. + * + * @author Mark Pollack + * + */ +public class Main { + + /** + * Main class that delegates to Spring Shell's Bootstrap class in order to simplify debugging inside an IDE + * @param args + * @throws IOException + */ + public static void main(String[] args) throws IOException { + Bootstrap.main(args); + + } + +} diff --git a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyBannerProvider.java b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyBannerProvider.java index 96cb19b8..32481e2a 100644 --- a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyBannerProvider.java +++ b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyBannerProvider.java @@ -54,7 +54,7 @@ public class MyBannerProvider extends DefaultBannerProvider } @Override - public String name() { - return "helloworld"; + public String getProviderName() { + return "Hello World Banner"; } } \ No newline at end of file diff --git a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyHistoryFileNameProvider.java b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyHistoryFileNameProvider.java index 33b309b0..77c745c4 100644 --- a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyHistoryFileNameProvider.java +++ b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyHistoryFileNameProvider.java @@ -28,15 +28,15 @@ import org.springframework.stereotype.Component; */ @Component @Order(Ordered.HIGHEST_PRECEDENCE) -public class MyHistoryFileNameProvider extends DefaultHistoryFileNameProvider{ +public class MyHistoryFileNameProvider extends DefaultHistoryFileNameProvider { public String getHistoryFileName() { return "my.log"; } @Override - public String name() { - return "my history file name provider"; + public String getProviderName() { + return "My history file name provider"; } } diff --git a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyPromptProvider.java b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyPromptProvider.java index 469da31c..ba875c27 100644 --- a/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyPromptProvider.java +++ b/samples/helloworld/src/main/java/org/springframework/shell/samples/helloworld/commands/MyPromptProvider.java @@ -35,8 +35,8 @@ public class MyPromptProvider extends DefaultPromptProvider { @Override - public String name() { - return "my prompt provider"; + public String getProviderName() { + return "My prompt provider"; } } diff --git a/samples/helloworld/src/main/resources/META-INF/spring/spring-shell-plugin.xml b/samples/helloworld/src/main/resources/META-INF/spring/spring-shell-plugin.xml index c8d367a0..9c76b239 100644 --- a/samples/helloworld/src/main/resources/META-INF/spring/spring-shell-plugin.xml +++ b/samples/helloworld/src/main/resources/META-INF/spring/spring-shell-plugin.xml @@ -3,7 +3,7 @@ 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.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> diff --git a/src/main/java/org/springframework/shell/Bootstrap.java b/src/main/java/org/springframework/shell/Bootstrap.java index 841e61e3..59df1695 100644 --- a/src/main/java/org/springframework/shell/Bootstrap.java +++ b/src/main/java/org/springframework/shell/Bootstrap.java @@ -19,10 +19,13 @@ import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ClassPathBeanDefinitionScanner; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.shell.core.ExitShellRequest; import org.springframework.shell.core.JLineShellComponent; import org.springframework.shell.core.Shell; @@ -38,10 +41,13 @@ import org.springframework.util.StopWatch; */ public class Bootstrap { + private final static String[] CONTEXT_PATH = { "classpath*:/META-INF/spring/spring-shell-plugin.xml" }; + private static Bootstrap bootstrap; - private AnnotationConfigApplicationContext parentApplicationContext; private static StopWatch sw = new StopWatch("Spring Shell"); private static CommandLine commandLine; + + private GenericApplicationContext ctx; public static void main(String[] args) throws IOException { @@ -60,25 +66,34 @@ public class Bootstrap { } public Bootstrap(String[] args) throws IOException { - commandLine = SimpleShellCommandLineOptions.parseCommandLine(args); - - parentApplicationContext = new AnnotationConfigApplicationContext(); - configureParentApplicationContext(parentApplicationContext); - - ConfigurableApplicationContext childPluginApplicationContext = createChildPluginApplicationContext(parentApplicationContext); - - parentApplicationContext.refresh(); - childPluginApplicationContext.refresh(); - + this(args, CONTEXT_PATH); } - public AnnotationConfigApplicationContext getParentApplicationContext() { - return parentApplicationContext; + public Bootstrap(String[] args, String[] contextPath) { + try { + commandLine = SimpleShellCommandLineOptions.parseCommandLine(args); + } catch (IOException e) { + throw new ShellException(e.getMessage(), e); + } + + ctx = new GenericApplicationContext(); + ctx.registerShutdownHook(); + configureApplicationContext(ctx); + //built-in commands and converters + ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(ctx); + scanner.scan("org.springframework.shell.commands", "org.springframework.shell.converters", "org.springframework.shell.plugin.support"); + //user contributed commands + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) ctx); + reader.loadBeanDefinitions(contextPath); + ctx.refresh(); + } + + + public ApplicationContext getApplicationContext() { + return ctx; } - private void configureParentApplicationContext(AnnotationConfigApplicationContext annctx) { - // create parent/base childPluginApplicationContext - + private void configureApplicationContext(GenericApplicationContext annctx) { createAndRegisterBeanDefinition(annctx, org.springframework.shell.converters.StringConverter.class); createAndRegisterBeanDefinition(annctx, org.springframework.shell.converters.AvailableCommandsConverter.class); @@ -99,35 +114,21 @@ public class Bootstrap { createAndRegisterBeanDefinition(annctx, org.springframework.shell.converters.SimpleFileConverter.class); annctx.getBeanFactory().registerSingleton("commandLine", commandLine); - annctx.scan("org.springframework.shell.commands"); - annctx.scan("org.springframework.shell.converters"); - annctx.scan("org.springframework.shell.plugin.support"); - } - /** - * Init plugin ApplicationContext - * - * @param annctx parent ApplicationContext in core spring shell - * @return new ApplicationContext in the plugin with core spring shell's context as parent - */ - private ConfigurableApplicationContext createChildPluginApplicationContext(AnnotationConfigApplicationContext annctx) { - return new ClassPathXmlApplicationContext( - new String[] { "classpath*:/META-INF/spring/spring-shell-plugin.xml" }, false, annctx); - } - - protected void createAndRegisterBeanDefinition(AnnotationConfigApplicationContext annctx, Class clazz) { + protected void createAndRegisterBeanDefinition(GenericApplicationContext annctx, Class clazz) { createAndRegisterBeanDefinition(annctx, clazz, null); } - protected void createAndRegisterBeanDefinition(AnnotationConfigApplicationContext annctx, Class clazz, String name) { + protected void createAndRegisterBeanDefinition(GenericApplicationContext annctx, Class clazz, String name) { RootBeanDefinition rbd = new RootBeanDefinition(); rbd.setBeanClass(clazz); + DefaultListableBeanFactory bf = (DefaultListableBeanFactory)annctx.getBeanFactory(); if (name != null) { - annctx.registerBeanDefinition(name, rbd); + bf.registerBeanDefinition(name, rbd); } else { - annctx.registerBeanDefinition(clazz.getSimpleName(), rbd); + bf.registerBeanDefinition(clazz.getSimpleName(), rbd); } } @@ -152,7 +153,7 @@ public class Bootstrap { String[] commandsToExecuteAndThenQuit = commandLine.getShellCommandsToExecute(); // The shell is used - JLineShellComponent shell = parentApplicationContext.getBean("shell", JLineShellComponent.class); + JLineShellComponent shell = ctx.getBean("shell", JLineShellComponent.class); ExitShellRequest exitShellRequest; if (null != commandsToExecuteAndThenQuit) { @@ -181,7 +182,7 @@ public class Bootstrap { shell.waitForComplete(); } - parentApplicationContext.close(); + ctx.close(); sw.stop(); if (shell.isDevelopmentMode()) { System.out.println("Total execution time: " + sw.getLastTaskTimeMillis() + " ms"); @@ -190,6 +191,6 @@ public class Bootstrap { } JLineShellComponent getJLineShellComponent() { - return parentApplicationContext.getBean("shell", JLineShellComponent.class); + return ctx.getBean("shell", JLineShellComponent.class); } } \ No newline at end of file diff --git a/src/main/java/org/springframework/shell/ShellException.java b/src/main/java/org/springframework/shell/ShellException.java new file mode 100644 index 00000000..3caa04d9 --- /dev/null +++ b/src/main/java/org/springframework/shell/ShellException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.shell; + + +/** + * Shell exception. + * + * @author David Wintefeldt + */ +public class ShellException extends RuntimeException { + + private static final long serialVersionUID = 1123895874463364743L; + + public ShellException() {} + + public ShellException(String message) { + super(message); + } + + public ShellException(Throwable t) { + super(t); + } + + public ShellException(String message, Throwable t) { + super(message, t); + } + +} diff --git a/src/main/java/org/springframework/shell/core/JLineShellComponent.java b/src/main/java/org/springframework/shell/core/JLineShellComponent.java index c06821ca..f3107bb4 100644 --- a/src/main/java/org/springframework/shell/core/JLineShellComponent.java +++ b/src/main/java/org/springframework/shell/core/JLineShellComponent.java @@ -22,6 +22,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactoryUtils; @@ -36,8 +38,9 @@ import org.springframework.core.io.Resource; import org.springframework.shell.CommandLine; import org.springframework.shell.plugin.BannerProvider; import org.springframework.shell.plugin.HistoryFileNameProvider; -import org.springframework.shell.plugin.PluginProvider; +import org.springframework.shell.plugin.NamedProvider; import org.springframework.shell.plugin.PromptProvider; +import org.springframework.shell.support.logging.HandlerUtils; /** * Launcher for {@link JLineShell}. @@ -187,7 +190,9 @@ public class JLineShellComponent extends JLineShell implements SmartLifecycle, A * @return history file name */ protected String getHistoryFileName() { - String providerHistoryFileName = getHighestPriorityProvider(HistoryFileNameProvider.class).getHistoryFileName(); + HistoryFileNameProvider historyFileNameProvider = getHighestPriorityProvider(HistoryFileNameProvider.class); + this.logger.log(Level.FINE, "Using HistoryFileName Provider Class " + historyFileNameProvider.getClass()); + String providerHistoryFileName = historyFileNameProvider.getHistoryFileName(); if (providerHistoryFileName != null) { return providerHistoryFileName; } else { @@ -202,7 +207,9 @@ public class JLineShellComponent extends JLineShell implements SmartLifecycle, A * @return prompt text */ protected String getPromptText() { - String providerPromptText = getHighestPriorityProvider(PromptProvider.class).getPrompt(); + PromptProvider promptProvider = getHighestPriorityProvider(PromptProvider.class); + this.logger.log(Level.FINE, "Using Prompt Provider Class " + promptProvider.getClass()); + String providerPromptText = promptProvider.getPrompt(); if (providerPromptText != null) { return providerPromptText; } else { @@ -221,15 +228,16 @@ public class JLineShellComponent extends JLineShell implements SmartLifecycle, A private String[] getBannerText() { String[] bannerText = new String[4]; BannerProvider provider = getHighestPriorityProvider(BannerProvider.class); + this.logger.log(Level.FINE, "Using BannerProvider class " + provider.getClass()); bannerText[0] = provider.getBanner(); bannerText[1] = provider.getWelcomeMessage(); bannerText[2] = provider.getVersion(); - bannerText[3] = provider.name(); + bannerText[3] = provider.getProviderName(); return bannerText; } - private T getHighestPriorityProvider(Class t) { + private T getHighestPriorityProvider(Class t) { Map providers = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.applicationContext, t); List sortedProviders = new ArrayList(providers.values()); Collections.sort(sortedProviders, annotationOrderComparator); diff --git a/src/main/java/org/springframework/shell/plugin/BannerProvider.java b/src/main/java/org/springframework/shell/plugin/BannerProvider.java index 39d17248..16f1150c 100644 --- a/src/main/java/org/springframework/shell/plugin/BannerProvider.java +++ b/src/main/java/org/springframework/shell/plugin/BannerProvider.java @@ -26,7 +26,7 @@ package org.springframework.shell.plugin; * @since 1.0 * */ -public interface BannerProvider extends PluginProvider { +public interface BannerProvider extends NamedProvider { /** * Returns the banner. diff --git a/src/main/java/org/springframework/shell/plugin/HistoryFileNameProvider.java b/src/main/java/org/springframework/shell/plugin/HistoryFileNameProvider.java index f7670c5e..f26b41ca 100644 --- a/src/main/java/org/springframework/shell/plugin/HistoryFileNameProvider.java +++ b/src/main/java/org/springframework/shell/plugin/HistoryFileNameProvider.java @@ -25,7 +25,7 @@ package org.springframework.shell.plugin; * @since 1.0 * */ -public interface HistoryFileNameProvider extends PluginProvider { +public interface HistoryFileNameProvider extends NamedProvider { /** * get history file name diff --git a/src/main/java/org/springframework/shell/plugin/PluginProvider.java b/src/main/java/org/springframework/shell/plugin/NamedProvider.java similarity index 68% rename from src/main/java/org/springframework/shell/plugin/PluginProvider.java rename to src/main/java/org/springframework/shell/plugin/NamedProvider.java index 3d36e2f7..490f372b 100644 --- a/src/main/java/org/springframework/shell/plugin/PluginProvider.java +++ b/src/main/java/org/springframework/shell/plugin/NamedProvider.java @@ -16,16 +16,19 @@ package org.springframework.shell.plugin; /** - * Generic plugin provider. + * Returns the name of the provider. Providers customize features of the shell such as the banner and command line prompt. + * * * @author Jarred Li + * @author Mark Pollack + * @see BannerProvider + * @see PromptProvider + * @see HistoryFileNameProvider */ -public interface PluginProvider { +public interface NamedProvider { /** - * Returns the name of the plugin. - * - * @return + * Return the name of the provider. */ - String name(); + String getProviderName(); } diff --git a/src/main/java/org/springframework/shell/plugin/PromptProvider.java b/src/main/java/org/springframework/shell/plugin/PromptProvider.java index 9c000d5d..70721e5c 100644 --- a/src/main/java/org/springframework/shell/plugin/PromptProvider.java +++ b/src/main/java/org/springframework/shell/plugin/PromptProvider.java @@ -24,7 +24,7 @@ package org.springframework.shell.plugin; * @author Jarred Li * */ -public interface PromptProvider extends PluginProvider { +public interface PromptProvider extends NamedProvider { /** * Returns the prompt text. diff --git a/src/main/java/org/springframework/shell/plugin/support/DefaultBannerProvider.java b/src/main/java/org/springframework/shell/plugin/support/DefaultBannerProvider.java index 0b063d32..df951fea 100644 --- a/src/main/java/org/springframework/shell/plugin/support/DefaultBannerProvider.java +++ b/src/main/java/org/springframework/shell/plugin/support/DefaultBannerProvider.java @@ -48,10 +48,10 @@ public class DefaultBannerProvider implements BannerProvider { } public String getWelcomeMessage() { - return "Welcome to " + name() + ". For assistance press or type \"hint\" then hit ENTER."; + return "Welcome to " + getProviderName() + ". For assistance press or type \"hint\" then hit ENTER."; } - public String name() { + public String getProviderName() { return "Spring Shell"; } } \ No newline at end of file diff --git a/src/main/java/org/springframework/shell/plugin/support/DefaultHistoryFileNameProvider.java b/src/main/java/org/springframework/shell/plugin/support/DefaultHistoryFileNameProvider.java index 74ab23ac..3518cba1 100644 --- a/src/main/java/org/springframework/shell/plugin/support/DefaultHistoryFileNameProvider.java +++ b/src/main/java/org/springframework/shell/plugin/support/DefaultHistoryFileNameProvider.java @@ -34,7 +34,7 @@ public class DefaultHistoryFileNameProvider implements HistoryFileNameProvider { return "spring-shell.log"; } - public String name() { + public String getProviderName() { return "default history provider"; } } diff --git a/src/main/java/org/springframework/shell/plugin/support/DefaultPromptProvider.java b/src/main/java/org/springframework/shell/plugin/support/DefaultPromptProvider.java index 922de503..26451239 100644 --- a/src/main/java/org/springframework/shell/plugin/support/DefaultPromptProvider.java +++ b/src/main/java/org/springframework/shell/plugin/support/DefaultPromptProvider.java @@ -34,7 +34,7 @@ public class DefaultPromptProvider implements PromptProvider { return "spring-shell>"; } - public String name() { + public String getProviderName() { return "default prompt provider"; } } diff --git a/src/test/java/org/springframework/shell/core/CommandLineInjectedTest.java b/src/test/java/org/springframework/shell/core/CommandLineInjectedTest.java index c8663a84..62b91bf4 100644 --- a/src/test/java/org/springframework/shell/core/CommandLineInjectedTest.java +++ b/src/test/java/org/springframework/shell/core/CommandLineInjectedTest.java @@ -22,7 +22,7 @@ import java.util.logging.Logger; import junit.framework.Assert; import org.junit.Test; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.ApplicationContext; import org.springframework.shell.Bootstrap; import org.springframework.shell.commands.test.OptionsInjectedDummyCommand; import org.springframework.shell.support.logging.HandlerUtils; @@ -33,7 +33,7 @@ public class CommandLineInjectedTest { public void commandLineInjected() throws IOException { try { Bootstrap bootstrap = new Bootstrap(null); - AnnotationConfigApplicationContext ctx = bootstrap.getParentApplicationContext(); + ApplicationContext ctx = bootstrap.getApplicationContext(); OptionsInjectedDummyCommand dummyCommand = ctx.getBean(OptionsInjectedDummyCommand.class); Assert.assertNotNull("commandLine was not injected into a command", dummyCommand.getCommandLine()); } catch (RuntimeException t) {