diff --git a/org.springframework.core/.classpath b/org.springframework.core/.classpath index ed6eb816f65..2fd07db6ff0 100644 --- a/org.springframework.core/.classpath +++ b/org.springframework.core/.classpath @@ -13,6 +13,7 @@ + diff --git a/org.springframework.core/ivy.xml b/org.springframework.core/ivy.xml index d6c8db9c654..9c7deacd0b5 100644 --- a/org.springframework.core/ivy.xml +++ b/org.springframework.core/ivy.xml @@ -29,6 +29,7 @@ + diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/CommandLineArgs.java b/org.springframework.core/src/main/java/org/springframework/core/env/CommandLineArgs.java new file mode 100644 index 00000000000..189484a6dfc --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/env/CommandLineArgs.java @@ -0,0 +1,91 @@ +/* + * Copyright 2002-2011 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.core.env; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * A simple representation of command line arguments, broken into "option arguments" and + * "non-option arguments". + * + * @author Chris Beams + * @since 3.1 + * @see SimpleCommandLineArgsParser + */ +class CommandLineArgs { + + private final Map> optionArgs = new HashMap>(); + private final List nonOptionArgs = new ArrayList(); + + /** + * Add an option argument for the given option name and add the given value to the + * list of values associated with this option (of which there may be zero or more). + * The given value may be {@code null}, indicating that the option was specified + * without an associated value (e.g. "--foo" vs. "--foo=bar"). + */ + public void addOptionArg(String optionName, String optionValue) { + if (!this.optionArgs.containsKey(optionName)) { + this.optionArgs.put(optionName, new ArrayList()); + } + if (optionValue != null) { + this.optionArgs.get(optionName).add(optionValue); + } + } + + /** + * Return the set of all option arguments present on the command line. + */ + public Set getOptionNames() { + return Collections.unmodifiableSet(this.optionArgs.keySet()); + } + + /** + * Return whether the option with the given name was present on the command line. + */ + public boolean containsOption(String optionName) { + return this.optionArgs.containsKey(optionName); + } + + /** + * Return the list of values associated with the given option. {@code null} signifies + * that the option was not present; empty list signifies that no values were associated + * with this option. + */ + public List getOptionValues(String optionName) { + return this.optionArgs.get(optionName); + } + + /** + * Add the given value to the list of non-option arguments. + */ + public void addNonOptionArg(String value) { + this.nonOptionArgs.add(value); + } + + /** + * Return the list of non-option arguments specified on the command line. + */ + public List getNonOptionArgs() { + return Collections.unmodifiableList(this.nonOptionArgs); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java new file mode 100644 index 00000000000..1620fd427b3 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java @@ -0,0 +1,296 @@ +/* + * Copyright 2002-2011 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.core.env; + +import java.util.Collection; +import java.util.List; + +import org.springframework.util.StringUtils; + +/** + * Abstract base class for {@link PropertySource} implementations backed by command line + * arguments. The parameterized type {@code T} represents the underlying source of command + * line options. This may be as simple as a String array in the case of + * {@link SimpleCommandLinePropertySource}, or specific to a particular API such as JOpt's + * {@code OptionSet} in the case of {@link JOptCommandLinePropertySource}. + * + *

Purpose and General Usage

+ * For use in standalone Spring-based applications, i.e. those that are bootstrapped via + * a traditional {@code main} method accepting a {@code String[]} of arguments from the + * command line. In many cases, processing command-line arguments directly within the + * {@code main} method may be sufficient, but in other cases, it may be desirable to + * inject arguments as values into Spring beans. It is this latter set of cases in which + * a {@code CommandLinePropertySource} becomes useful. A {@code CommandLinePropertySource} + * will typically be added to the {@link Environment} of the Spring + * {@code ApplicationContext}, at which point all command line arguments become available + * through the {@link Environment#getProperty(String)} family of methods. For example: + *
+ * public static void main(String[] args) {
+ *     CommandLinePropertySource clps = ...;
+ *     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ *     ctx.getEnvironment().getPropertySources().addFirst(clps);
+ *     ctx.register(AppConfig.class);
+ *     ctx.refresh();
+ * }
+ * With the bootstrap logic above, the {@code AppConfig} class may {@code @Inject} the + * Spring {@code Environment} and query it directly for properties: + *
+ * @Configuration
+ * public class AppConfig {
+ *     @Inject Environment env;
+ *
+ *     @Bean
+ *     public void DataSource dataSource() {
+ *         MyVendorDataSource dataSource = new MyVendorDataSource();
+ *         dataSource.setHostname(env.getProperty("db.hostname", "localhost"));
+ *         dataSource.setUsername(env.getRequiredProperty("db.username"));
+ *         dataSource.setPassword(env.getRequiredProperty("db.password"));
+ *         // ...
+ *         return dataSource;
+ *     }
+ * }
+ * Because the {@code CommandLinePropertySource} was added to the {@code Environment}'s + * set of {@link MutablePropertySources} using the {@code #addFirst} method, it has + * highest search precedence, meaning that while "db.hostname" and other properties may + * exist in other property sources such as the system environment variables, it will be + * chosen from the command line property source first. This is a reasonable approach + * given that arguments specified on the command line are naturally more specific than + * those specified as environment variables. + * + *

As an alternative to injecting the {@code Environment}, Spring's {@code @Value} + * annotation may be used to inject these properties, given that a {@link + * PropertySourcesPropertyResolver} bean has been registered, either directly or through + * using the {@code } element. For example: + *

+ * @Component
+ * public class MyComponent {
+ *     @Value("my.property:defaultVal")
+ *     private String myProperty;
+ *
+ *     public void getMyProperty() {
+ *         return this.myProperty;
+ *     }
+ *
+ *     // ...
+ * }
+ * + *

Working with option arguments

+ * + *

Individual command line arguments are represented as properties through the usual + * {@link PropertySource#getProperty(String)} and + * {@link PropertySource#containsProperty(String)} methods. For example, given the + * following command line: + *

+ * --o1=v1 --o2
+ * 'o1' and 'o2' are treated as "option arguments", and the following assertions would + * evaluate true: + *
+ * CommandLinePropertySource ps = ...
+ * assert ps.containsProperty("o1") == true;
+ * assert ps.containsProperty("o2") == true;
+ * assert ps.containsProperty("o3") == false;
+ * assert ps.getProperty("o1").equals("v1");
+ * assert ps.getProperty("o2").equals("");
+ * assert ps.getProperty("o3") == null;
+ * + * Note that the 'o2' option has no argument, but {@code getProperty("o2")} resolves to + * empty string ({@code ""}) as opposed to {@code null}, while {@code getProperty("o3")} + * resolves to {@code null} because it was not specified. This behavior is consistent with + * the general contract to be followed by all {@code PropertySource} implementations. + * + *

Note also that while "--" was used in the examples above to denote an option + * argument, this syntax may vary across individual command line argument libraries. For + * example, a JOpt- or Commons CLI-based implementation may allow for single dash ("-") + * "short" option arguments, etc. + * + *

Working with non-option arguments

+ * + *

Non-option arguments are also supported through this abstraction. Any arguments + * supplied without an option-style prefix such as "-" or "--" are considered "non-option + * arguments" and available through the special {@linkplain + * #DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME "nonOptionArgs"} property. If multiple + * non-option arguments are specified, the value of this property will be a + * comma-delimited string containing all of the arguments. This approach ensures a simple + * and consistent return type (String) for all properties from a {@code + * CommandLinePropertySource} and at the same time lends itself to conversion when used + * in conjunction with the Spring {@link Environment} and its built-in {@code + * ConversionService}. Consider the following example: + *

+ * --o1=v1 --o2=v2 /path/to/file1 /path/to/file2
+ * In this example, "o1" and "o2" would be considered "option arguments", while the two + * filesystem paths qualify as "non-option arguments". As such, the following assertions + * will evaluate true: + *
+ * CommandLinePropertySource ps = ...
+ * assert ps.containsProperty("o1") == true;
+ * assert ps.containsProperty("o2") == true;
+ * assert ps.containsProperty("nonOptionArgs") == true;
+ * assert ps.getProperty("o1").equals("v1");
+ * assert ps.getProperty("o2").equals("v2");
+ * assert ps.getProperty("nonOptionArgs").equals("/path/to/file1,/path/to/file2");
+ * + *

As mentioned above, when used in conjunction with the Spring {@code Environment} + * abstraction, this comma-delimited string may easily be converted to a String array or + * list: + *

+ * Environment env = applicationContext.getEnvironment();
+ * String[] nonOptionArgs = env.getProperty("nonOptionArgs", String[].class);
+ * assert nonOptionArgs[0].equals("/path/to/file1");
+ * assert nonOptionArgs[1].equals("/path/to/file2");
+ * + *

The name of the special "non-option arguments" property may be customized through + * the {@link #setNonOptionArgsPropertyName(String)} method. Doing so is recommended as + * it gives proper semantic value to non-option arguments. For example, if filesystem + * paths are being specified as non-option arguments, it is likely preferable to refer to + * these as something like "file.locations" than the default of "nonOptionArgs": + *

+ * public static void main(String[] args) {
+ *     CommandLinePropertySource clps = ...;
+ *     clps.setNonOptionArgsPropertyName("file.locations");
+ *
+ *     AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ *     ctx.getEnvironment().getPropertySources().addFirst(clps);
+ *     ctx.register(AppConfig.class);
+ *     ctx.refresh();
+ * }
+ * + *

Limitations

+ * This abstraction is not intended to expose the full power of underlying command line + * parsing APIs such as JOpt or Commons CLI. It's intent is rather just the opposite: to + * provide the simplest possible abstraction for accessing command line arguments + * after they have been parsed. So the typical case will involve fully configuring + * the underlying command line parsing API, parsing the {@code String[]} of arguments + * coming into the main method, and then simply providing the parsing results to an + * implementation of {@code CommandLinePropertySource}. At that point, all arguments can + * be considered either 'option' or 'non-option' arguments and as described above can be + * accessed through the normal {@code PropertySource} and {@code Environment} APIs. + * + * @author Chris Beams + * @since 3.1 + * @see PropertySource + * @see SimpleCommandLinePropertySource + * @see JOptCommandLinePropertySource + */ +public abstract class CommandLinePropertySource extends PropertySource { + + /** The default name given to {@link CommandLinePropertySource} instances: {@value} */ + public static final String DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME = "commandLineArgs"; + + /** The default name of the property representing non-option arguments: {@value} */ + public static final String DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME = "nonOptionArgs"; + + private String nonOptionArgsPropertyName = DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME; + + /** + * Create a new {@code CommandLinePropertySource} having the default name {@value + * #DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME} and backed by the given source object. + */ + public CommandLinePropertySource(T source) { + super(DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME, source); + } + + /** + * Create a new {@link CommandLinePropertySource} having the given name and backed by + * the given source object. + */ + public CommandLinePropertySource(String name, T source) { + super(name, source); + } + + /** + * Specify the name of the special "non-option arguments" property. The default is + * {@value #DEFAULT_NON_OPTION_ARGS_PROPERTY_NAME}. + */ + public void setNonOptionArgsPropertyName(String nonOptionArgsPropertyName) { + this.nonOptionArgsPropertyName = nonOptionArgsPropertyName; + } + + /** + * Return whether this {@code PropertySource} contains the given key. + *

This implementation first checks to see if the key specified is the special + * {@linkplain #setNonOptionArgsPropertyName(String) "non-option arguments" property}, + * and if so delegates to the abstract {@link #getNonOptionArgs()} method + * checking to see whether it returns an empty collection. Otherwise delegates to and + * returns the value of the abstract {@link #containsOption(String)} method. + */ + @Override + public final boolean containsProperty(String key) { + if (this.nonOptionArgsPropertyName.equals(key)) { + return !this.getNonOptionArgs().isEmpty(); + } + return this.containsOption(key); + } + + /** + * {@inheritDoc} + *

This implementation first checks to see if the key specified is the special + * {@linkplain #setNonOptionArgsPropertyName(String) "non-option arguments" property}, + * and if so delegates to the abstract {@link #getNonOptionArgs()} method. If so + * and the collection of non-option arguments is empty, this method returns {@code + * null}. If not empty, it returns a comma-separated String of all non-option + * arguments. Otherwise delegates to and returns the result of the abstract {@link + * #getOptionValues(String)} method. + */ + @Override + public final String getProperty(String key) { + if (this.nonOptionArgsPropertyName.equals(key)) { + Collection nonOptionArguments = this.getNonOptionArgs(); + if (nonOptionArguments.isEmpty()) { + return null; + } + else { + return StringUtils.collectionToCommaDelimitedString(nonOptionArguments); + } + } + Collection optionValues = this.getOptionValues(key); + if (optionValues == null) { + return null; + } + else { + return StringUtils.collectionToCommaDelimitedString(optionValues); + } + } + + /** + * Return whether the set of option arguments parsed from the command line contains + * an option with the given name. + */ + protected abstract boolean containsOption(String name); + + /** + * Return the collection of values associated with the command line option having the + * given name. + *

    + *
  • if the option is present and has no argument (e.g.: "--foo"), return an empty + * collection ({@code []})
  • + *
  • if the option is present and has a single value (e.g. "--foo=bar"), return a + * collection having one element ({@code ["bar"]})
  • + *
  • if the option is present and the underlying command line parsing library + * supports multiple arguments (e.g. "--foo=bar --foo=baz"), return a collection + * having elements for each value ({@code ["bar", "baz"]})
  • + *
  • if the option is not present, return {@code null}
  • + *
+ */ + protected abstract List getOptionValues(String name); + + /** + * Return the collection of non-option arguments parsed from the command line. Never + * {@code null}. + */ + protected abstract List getNonOptionArgs(); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java new file mode 100644 index 00000000000..48fa996c2b1 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java @@ -0,0 +1,106 @@ +/* + * Copyright 2002-2011 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.core.env; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import joptsimple.OptionSet; + +/** + * {@link CommandLinePropertySource} implementation backed by a JOpt {@link OptionSet}. + * + *

Typical usage

+ * Configure and execute an {@code OptionParser} against the {@code String[]} of arguments + * supplied to the {@code main} method, and create a {@link JOptCommandLinePropertySource} + * using the resulting {@code OptionSet} object: + *
+ * public static void main(String[] args) {
+ *     OptionParser parser = new OptionParser();
+ *     parser.accepts("option1");
+ *     parser.accepts("option2").withRequiredArg();
+ *     OptionSet options = parser.parse(args);
+ *     PropertySource ps = new JOptCommandLinePropertySource(options);
+ *     // ...
+ * }
+ * + * See {@link CommandLinePropertySource} for complete general usage examples. + * + *

Requirements

+ * + *

Use of this class requires adding the jopt-simple JAR to your application classpath. + * Versions 3.0 and better are supported. + * + * @author Chris Beams + * @since 3.1 + * @see CommandLinePropertySource + * @see joptsimple.OptionParser + * @see joptsimple.OptionSet + */ +public class JOptCommandLinePropertySource extends CommandLinePropertySource { + + /** + * Create a new {@code JOptCommandLinePropertySource} having the default name + * and backed by the given {@code OptionSet}. + * @see CommandLinePropertySource#DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME + * @see CommandLinePropertySource#CommandLinePropertySource(Object) + */ + public JOptCommandLinePropertySource(OptionSet options) { + super(options); + } + + /** + * Create a new {@code JOptCommandLinePropertySource} having the given name + * and backed by the given {@code OptionSet}. + */ + public JOptCommandLinePropertySource(String name, OptionSet options) { + super(name, options); + } + + @Override + protected boolean containsOption(String key) { + return this.source.has(key); + } + + @Override + public List getOptionValues(String key) { + List argValues = this.source.valuesOf(key); + List stringArgValues = new ArrayList(); + for(Object argValue : argValues) { + if (!(argValue instanceof String)) { + throw new IllegalArgumentException("argument values must be of type String"); + } + stringArgValues.add((String)argValue); + } + if (stringArgValues.size() == 0) { + if (this.source.has(key)) { + return Collections.emptyList(); + } + else { + return null; + } + } + return Collections.unmodifiableList(stringArgValues); + } + + @Override + protected List getNonOptionArgs() { + return this.source.nonOptionArguments(); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java b/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java new file mode 100644 index 00000000000..d0e21979987 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java @@ -0,0 +1,86 @@ +/* + * Copyright 2002-2011 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.core.env; + +/** + * Parses a {@code String[]} of command line arguments in order to populate a + * {@link CommandLineArgs} object. + * + *

Working with option arguments

+ * Option arguments must adhere to the exact syntax: + *
--optName[=optValue]
+ * That is, options must be prefixed with "{@code --}", and may or may not specify a value. + * If a value is specified, the name and value must be separated without spaces + * by an equals sign ("="). + * + *

Valid examples of option arguments

+ *
+ * --foo
+ * --foo=bar
+ * --foo="bar then baz"
+ * --foo=bar,baz,biz
+ * + *

Invalid examples of option arguments

+ *
+ * -foo
+ * --foo bar
+ * --foo = bar
+ * --foo=bar --foo=baz --foo=biz
+ * + *

Working with non-option arguments

+ * Any and all arguments specified at the command line without the "{@code --}" option + * prefix will be considered as "non-option arguments" and made available through the + * {@link CommandLineArgs#getNonOptionArgs()} method. + * + * @author Chris Beams + * @since 3.1 + */ +class SimpleCommandLineArgsParser { + + /** + * Parse the given {@code String} array based on the rules described {@linkplain + * SimpleCommandLineArgsParser above}, returning a fully-populated + * {@link CommandLineArgs} object. + * @param args command line arguments, typically from a {@code main()} method + */ + public CommandLineArgs parse(String... args) { + CommandLineArgs commandLineArgs = new CommandLineArgs(); + for (String arg : args) { + if (arg.startsWith("--")) { + String optionText = arg.substring(2, arg.length()); + String optionName; + String optionValue = null; + if (optionText.contains("=")) { + optionName = optionText.substring(0, optionText.indexOf("=")); + optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length()); + } + else { + optionName = optionText; + } + if (optionName.isEmpty() || (optionValue != null && optionValue.isEmpty())) { + throw new IllegalArgumentException("Invalid argument syntax: " + arg); + } + commandLineArgs.addOptionArg(optionName, optionValue); + } + else { + commandLineArgs.addNonOptionArg(arg); + } + } + return commandLineArgs; + } + +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java b/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java new file mode 100644 index 00000000000..17a1d36e7d1 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java @@ -0,0 +1,113 @@ +/* + * Copyright 2002-2011 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.core.env; + +import java.util.List; + +/** + * {@link CommandLinePropertySource} implementation backed by a simple String array. + * + *

Purpose

+ * This {@code CommandLinePropertySource} implementation aims to provide the simplest + * possible approach to parsing command line arguments. As with all {@code + * CommandLinePropertySource} implementations, command line arguments are broken into two + * distinct groups: option arguments and non-option arguments, as + * described below (some sections copied from Javadoc for {@link SimpleCommandLineArgsParser}): + * + *

Working with option arguments

+ * Option arguments must adhere to the exact syntax: + *
--optName[=optValue]
+ * That is, options must be prefixed with "{@code --}", and may or may not specify a value. + * If a value is specified, the name and value must be separated without spaces + * by an equals sign ("="). + * + *

Valid examples of option arguments

+ *
+ * --foo
+ * --foo=bar
+ * --foo="bar then baz"
+ * --foo=bar,baz,biz
+ * + *

Invalid examples of option arguments

+ *
+ * -foo
+ * --foo bar
+ * --foo = bar
+ * --foo=bar --foo=baz --foo=biz
+ * + *

Working with non-option arguments

+ * Any and all arguments specified at the command line without the "{@code --}" option + * prefix will be considered as "non-option arguments" and made available through the + * {@link #getNonOptionArgs()} method. + * + *

Typical usage

+ *
+ * public static void main(String[] args) {
+ *     PropertySource ps = new SimpleCommandLinePropertySource(args);
+ *     // ...
+ * }
+ * + * See {@link CommandLinePropertySource} for complete general usage examples. + * + *

Beyond the basics

+ * + *

When more fully-featured command line parsing is necessary, consider using + * the provided {@link JOptCommandLinePropertySource}, or implement your own + * {@code CommandLinePropertySource} against the command line parsing library of your + * choice! + * + * @author Chris Beams + * @since 3.1 + * @see CommandLinePropertySource + * @see JOptCommandLinePropertySource + */ +public class SimpleCommandLinePropertySource extends CommandLinePropertySource { + + /** + * Create a new {@code SimpleCommandLinePropertySource} having the default name + * and backed by the given {@code String[]} of command line arguments. + * @see CommandLinePropertySource#DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME + * @see CommandLinePropertySource#CommandLinePropertySource(Object) + */ + public SimpleCommandLinePropertySource(String... args) { + super(new SimpleCommandLineArgsParser().parse(args)); + } + + /** + * Create a new {@code SimpleCommandLinePropertySource} having the given name + * and backed by the given {@code String[]} of command line arguments. + */ + public SimpleCommandLinePropertySource(String name, String[] args) { + super(name, new SimpleCommandLineArgsParser().parse(args)); + } + + @Override + protected boolean containsOption(String key) { + return this.source.containsOption(key); + } + + @Override + protected List getOptionValues(String key) { + return this.source.getOptionValues(key); + } + + @Override + protected List getNonOptionArgs() { + return this.source.getNonOptionArgs(); + } + +} diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/JOptCommandLinePropertySourceTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/JOptCommandLinePropertySourceTests.java new file mode 100644 index 00000000000..6eced35d0d5 --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/core/env/JOptCommandLinePropertySourceTests.java @@ -0,0 +1,165 @@ +/* + * Copyright 2002-2011 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.core.env; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.util.Arrays; + +import joptsimple.OptionParser; +import joptsimple.OptionSet; + +import org.junit.Test; + +/** + * Unit tests for {@link JOptCommandLinePropertySource}. + * + * @author Chris Beams + * @since 3.1 + */ +public class JOptCommandLinePropertySourceTests { + + @Test + public void withRequiredArg_andArgIsPresent() { + OptionParser parser = new OptionParser(); + parser.accepts("foo").withRequiredArg(); + OptionSet options = parser.parse("--foo=bar"); + + PropertySource ps = new JOptCommandLinePropertySource(options); + assertThat((String)ps.getProperty("foo"), equalTo("bar")); + } + + @Test + public void withOptionalArg_andArgIsMissing() { + OptionParser parser = new OptionParser(); + parser.accepts("foo").withOptionalArg(); + OptionSet options = parser.parse("--foo"); + + PropertySource ps = new JOptCommandLinePropertySource(options); + assertThat(ps.containsProperty("foo"), is(true)); + assertThat((String)ps.getProperty("foo"), equalTo("")); + } + + @Test + public void withNoArg() { + OptionParser parser = new OptionParser(); + parser.accepts("o1"); + parser.accepts("o2"); + OptionSet options = parser.parse("--o1"); + + PropertySource ps = new JOptCommandLinePropertySource(options); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(false)); + assertThat((String)ps.getProperty("o1"), equalTo("")); + assertThat(ps.getProperty("o2"), nullValue()); + } + + @Test + public void withRequiredArg_andMultipleArgsPresent_usingDelimiter() { + OptionParser parser = new OptionParser(); + parser.accepts("foo").withRequiredArg().withValuesSeparatedBy(','); + OptionSet options = parser.parse("--foo=bar,baz,biz"); + + CommandLinePropertySource ps = new JOptCommandLinePropertySource(options); + assertEquals(Arrays.asList("bar","baz","biz"), ps.getOptionValues("foo")); + assertThat(ps.getProperty("foo"), equalTo("bar,baz,biz")); + } + + @Test + public void withRequiredArg_andMultipleArgsPresent_usingRepeatedOption() { + OptionParser parser = new OptionParser(); + parser.accepts("foo").withRequiredArg().withValuesSeparatedBy(','); + OptionSet options = parser.parse("--foo=bar", "--foo=baz", "--foo=biz"); + + CommandLinePropertySource ps = new JOptCommandLinePropertySource(options); + assertEquals(Arrays.asList("bar","baz","biz"), ps.getOptionValues("foo")); + assertThat(ps.getProperty("foo"), equalTo("bar,baz,biz")); + } + + @Test + public void withMissingOption() { + OptionParser parser = new OptionParser(); + parser.accepts("foo").withRequiredArg().withValuesSeparatedBy(','); + OptionSet options = parser.parse(); // <-- no options whatsoever + + PropertySource ps = new JOptCommandLinePropertySource(options); + assertThat(ps.getProperty("foo"), nullValue()); + } + + @Test + public void withDottedOptionName() { + OptionParser parser = new OptionParser(); + parser.accepts("spring.profiles.active").withRequiredArg(); + OptionSet options = parser.parse("--spring.profiles.active=p1"); + + CommandLinePropertySource ps = new JOptCommandLinePropertySource(options); + assertThat(ps.getProperty("spring.profiles.active"), equalTo("p1")); + } + + @Test + public void withDefaultNonOptionArgsNameAndNoNonOptionArgsPresent() { + OptionParser parser = new OptionParser(); + parser.accepts("o1").withRequiredArg(); + parser.accepts("o2"); + OptionSet optionSet = parser.parse("--o1=v1", "--o2"); + PropertySource ps = new JOptCommandLinePropertySource(optionSet); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.getProperty("nonOptionArgs"), nullValue()); + } + + @Test + public void withDefaultNonOptionArgsNameAndNonOptionArgsPresent() { + OptionParser parser = new OptionParser(); + parser.accepts("o1").withRequiredArg(); + parser.accepts("o2"); + OptionSet optionSet = parser.parse("--o1=v1", "noa1", "--o2", "noa2"); + PropertySource ps = new JOptCommandLinePropertySource(optionSet); + + assertThat(ps.containsProperty("nonOptionArgs"), is(true)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + + String nonOptionArgs = (String)ps.getProperty("nonOptionArgs"); + assertThat(nonOptionArgs, equalTo("noa1,noa2")); + } + + @Test + public void withCustomNonOptionArgsNameAndNoNonOptionArgsPresent() { + OptionParser parser = new OptionParser(); + parser.accepts("o1").withRequiredArg(); + parser.accepts("o2"); + OptionSet optionSet = parser.parse("--o1=v1", "noa1", "--o2", "noa2"); + CommandLinePropertySource ps = new JOptCommandLinePropertySource(optionSet); + ps.setNonOptionArgsPropertyName("NOA"); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.containsProperty("NOA"), is(true)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + String nonOptionArgs = ps.getProperty("NOA"); + assertThat(nonOptionArgs, equalTo("noa1,noa2")); + } +} diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLineParserTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLineParserTests.java new file mode 100644 index 00000000000..08111c70e56 --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLineParserTests.java @@ -0,0 +1,114 @@ +/* + * Copyright 2002-2011 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.core.env; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class SimpleCommandLineParserTests { + + @Test + public void withNoOptions() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + assertThat(parser.parse().getOptionValues("foo"), nullValue()); + } + + @Test + public void withSingleOptionAndNoValue() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + CommandLineArgs args = parser.parse("--o1"); + assertThat(args.containsOption("o1"), is(true)); + assertThat(args.getOptionValues("o1"), equalTo(Collections.EMPTY_LIST)); + } + + @Test + public void withSingleOptionAndValue() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + CommandLineArgs args = parser.parse("--o1=v1"); + assertThat(args.containsOption("o1"), is(true)); + assertThat(args.getOptionValues("o1").get(0), equalTo("v1")); + } + + @Test + public void withMixOfOptionsHavingValueAndOptionsHavingNoValue() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + CommandLineArgs args = parser.parse("--o1=v1", "--o2"); + assertThat(args.containsOption("o1"), is(true)); + assertThat(args.containsOption("o2"), is(true)); + assertThat(args.containsOption("o3"), is(false)); + assertThat(args.getOptionValues("o1").get(0), equalTo("v1")); + assertThat(args.getOptionValues("o2"), equalTo(Collections.EMPTY_LIST)); + assertThat(args.getOptionValues("o3"), nullValue()); + } + + @Test(expected=IllegalArgumentException.class) + public void withEmptyOptionText() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + parser.parse("--"); + } + + @Test(expected=IllegalArgumentException.class) + public void withEmptyOptionName() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + parser.parse("--=v1"); + } + + @Test(expected=IllegalArgumentException.class) + public void withEmptyOptionValue() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + parser.parse("--o1="); + } + + @Test(expected=IllegalArgumentException.class) + public void withEmptyOptionNameAndEmptyOptionValue() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + parser.parse("--="); + } + + @Test + public void withNonOptionArguments() { + SimpleCommandLineArgsParser parser = new SimpleCommandLineArgsParser(); + CommandLineArgs args = parser.parse("--o1=v1", "noa1", "--o2=v2", "noa2"); + assertThat(args.getOptionValues("o1").get(0), equalTo("v1")); + assertThat(args.getOptionValues("o2").get(0), equalTo("v2")); + + List nonOptions = args.getNonOptionArgs(); + assertThat(nonOptions.get(0), equalTo("noa1")); + assertThat(nonOptions.get(1), equalTo("noa2")); + assertThat(nonOptions.size(), equalTo(2)); + } + + @Test(expected=UnsupportedOperationException.class) + public void assertOptionNamesIsUnmodifiable() { + CommandLineArgs args = new SimpleCommandLineArgsParser().parse(); + args.getOptionNames().add("bogus"); + } + + @Test(expected=UnsupportedOperationException.class) + public void assertNonOptionArgsIsUnmodifiable() { + CommandLineArgs args = new SimpleCommandLineArgsParser().parse(); + args.getNonOptionArgs().add("foo"); + } + +} diff --git a/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLinePropertySourceTests.java b/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLinePropertySourceTests.java new file mode 100644 index 00000000000..79b502946ec --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLinePropertySourceTests.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2011 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.core.env; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.junit.Assert.assertThat; + +import java.util.List; + +import org.junit.Test; + +/** + * Unit tests for {@link SimpleCommandLinePropertySource}. + * + * @author Chris Beams + * @since 3.1 + */ +public class SimpleCommandLinePropertySourceTests { + + @Test + public void withDefaultName() { + PropertySource ps = new SimpleCommandLinePropertySource(); + assertThat(ps.getName(), + equalTo(CommandLinePropertySource.DEFAULT_COMMAND_LINE_PROPERTY_SOURCE_NAME)); + } + + @Test + public void withCustomName() { + PropertySource ps = new SimpleCommandLinePropertySource("ps1", new String[0]); + assertThat(ps.getName(), equalTo("ps1")); + } + + @Test + public void withNoArgs() { + PropertySource ps = new SimpleCommandLinePropertySource(); + assertThat(ps.containsProperty("foo"), is(false)); + assertThat(ps.getProperty("foo"), nullValue()); + } + + @Test + public void withOptionArgsOnly() { + CommandLinePropertySource ps = + new SimpleCommandLinePropertySource("--o1=v1", "--o2"); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + assertThat(ps.containsProperty("o3"), is(false)); + assertThat(ps.getProperty("o1"), equalTo("v1")); + assertThat(ps.getProperty("o2"), equalTo("")); + assertThat(ps.getProperty("o3"), nullValue()); + } + + @Test + public void withDefaultNonOptionArgsNameAndNoNonOptionArgsPresent() { + PropertySource ps = new SimpleCommandLinePropertySource("--o1=v1", "--o2"); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.getProperty("nonOptionArgs"), nullValue()); + } + + @Test + public void withDefaultNonOptionArgsNameAndNonOptionArgsPresent() { + CommandLinePropertySource ps = + new SimpleCommandLinePropertySource("--o1=v1", "noa1", "--o2", "noa2"); + + assertThat(ps.containsProperty("nonOptionArgs"), is(true)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + + String nonOptionArgs = ps.getProperty("nonOptionArgs"); + assertThat(nonOptionArgs, equalTo("noa1,noa2")); + } + + @Test + public void withCustomNonOptionArgsNameAndNoNonOptionArgsPresent() { + CommandLinePropertySource ps = + new SimpleCommandLinePropertySource("--o1=v1", "noa1", "--o2", "noa2"); + ps.setNonOptionArgsPropertyName("NOA"); + + assertThat(ps.containsProperty("nonOptionArgs"), is(false)); + assertThat(ps.containsProperty("NOA"), is(true)); + assertThat(ps.containsProperty("o1"), is(true)); + assertThat(ps.containsProperty("o2"), is(true)); + String nonOptionArgs = ps.getProperty("NOA"); + assertThat(nonOptionArgs, equalTo("noa1,noa2")); + } + + @Test + public void covertNonOptionArgsToStringArrayAndList() { + CommandLinePropertySource ps = + new SimpleCommandLinePropertySource("--o1=v1", "noa1", "--o2", "noa2"); + StandardEnvironment env = new StandardEnvironment(); + env.getPropertySources().addFirst(ps); + + String nonOptionArgs = env.getProperty("nonOptionArgs"); + assertThat(nonOptionArgs, equalTo("noa1,noa2")); + + String[] nonOptionArgsArray = env.getProperty("nonOptionArgs", String[].class); + assertThat(nonOptionArgsArray[0], equalTo("noa1")); + assertThat(nonOptionArgsArray[1], equalTo("noa2")); + + @SuppressWarnings("unchecked") + List nonOptionArgsList = env.getProperty("nonOptionArgs", List.class); + assertThat(nonOptionArgsList.get(0), equalTo("noa1")); + assertThat(nonOptionArgsList.get(1), equalTo("noa2")); + } +} diff --git a/org.springframework.core/template.mf b/org.springframework.core/template.mf index 3197d0d629a..d964ac37f4f 100644 --- a/org.springframework.core/template.mf +++ b/org.springframework.core/template.mf @@ -11,6 +11,7 @@ Import-Template: org.apache.commons.logging.*;version="[1.1.1, 2.0.0)", org.springframework.asm.*;version=${spring.osgi.range};resolution:=optional, org.apache.log4j.*;version="[1.2.15, 2.0.0)";resolution:=optional, + joptsimple.*;version="[3.0.0, 4.0.0)";resolution:=optional, org.aspectj.*;version=${aj.osgi.range};resolution:=optional, org.xml.sax.*;version="0";resolution:=optional, org.w3c.dom.*;version="0";resolution:=optional