Browse Source

Introduce CommandLinePropertySource and impls

Users may now work with command line arguments as a source of
properties for use with the PropertySource and Environment APIs.
An implementation based on the jopt library and a "simple"
implementation requiring no external libraries are are provided
out-of-the box.

See Javadoc for CommandLinePropertySource, JOptCommandLinePropertySource
and SimpleCommandLinePropertySource for details.

Issue: SPR-8482
pull/7/head
Chris Beams 14 years ago
parent
commit
1eb5811347
  1. 1
      org.springframework.core/.classpath
  2. 1
      org.springframework.core/ivy.xml
  3. 91
      org.springframework.core/src/main/java/org/springframework/core/env/CommandLineArgs.java
  4. 296
      org.springframework.core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java
  5. 106
      org.springframework.core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java
  6. 86
      org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java
  7. 113
      org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java
  8. 165
      org.springframework.core/src/test/java/org/springframework/core/env/JOptCommandLinePropertySourceTests.java
  9. 114
      org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLineParserTests.java
  10. 126
      org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLinePropertySourceTests.java
  11. 1
      org.springframework.core/template.mf

1
org.springframework.core/.classpath

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
<classpathentry kind="var" path="IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-1.2.0.jar" sourcepath="/IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-sources-1.2.0.jar"/>
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-2.5.1.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-sources-2.5.1.jar"/>
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-3.2.7.jar" sourcepath="/IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-sources-3.2.7.jar"/>
<classpathentry kind="var" path="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-3.0.0.jar" sourcepath="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-sources-3.0.0.jar"/>
<classpathentry kind="lib" path="/org.springframework.asm/target/artifacts/org.springframework.asm.jar" sourcepath="/org.springframework.asm/target/artifacts/org.springframework.asm-sources.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

1
org.springframework.core/ivy.xml

@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="optional, log4j->compile"/>
<dependency org="org.aspectj" name="com.springsource.org.aspectj.weaver" rev="${aspectj.version}" conf="optional, aspectj->compile"/>
<dependency org="org.springframework" name="org.springframework.asm" rev="latest.integration" conf="optional->compile"/>
<dependency org="net.sourceforge.jopt-simple" name="com.springsource.joptsimple" rev="3.0.0" conf="optional->compile"/>
<!-- test dependencies -->
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.5.0" conf="test->compile"/>
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->runtime"/>

91
org.springframework.core/src/main/java/org/springframework/core/env/CommandLineArgs.java vendored

@ -0,0 +1,91 @@ @@ -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<String, List<String>> optionArgs = new HashMap<String, List<String>>();
private final List<String> nonOptionArgs = new ArrayList<String>();
/**
* 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<String>());
}
if (optionValue != null) {
this.optionArgs.get(optionName).add(optionValue);
}
}
/**
* Return the set of all option arguments present on the command line.
*/
public Set<String> 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<String> 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<String> getNonOptionArgs() {
return Collections.unmodifiableList(this.nonOptionArgs);
}
}

296
org.springframework.core/src/main/java/org/springframework/core/env/CommandLinePropertySource.java vendored

@ -0,0 +1,296 @@ @@ -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}.
*
* <h3>Purpose and General Usage</h3>
* 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:
* <pre class="code">
* public static void main(String[] args) {
* CommandLinePropertySource clps = ...;
* AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
* ctx.getEnvironment().getPropertySources().addFirst(clps);
* ctx.register(AppConfig.class);
* ctx.refresh();
* }</pre>
* With the bootstrap logic above, the {@code AppConfig} class may {@code @Inject} the
* Spring {@code Environment} and query it directly for properties:
* <pre class="code">
* &#064;Configuration
* public class AppConfig {
* &#064;Inject Environment env;
*
* &#064;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;
* }
* }</pre>
* 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.
*
* <p>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 <context:property-placeholder>} element. For example:
* <pre class="code">
* &#064;Component
* public class MyComponent {
* &#064;Value("my.property:defaultVal")
* private String myProperty;
*
* public void getMyProperty() {
* return this.myProperty;
* }
*
* // ...
* }</pre>
*
* <h3>Working with option arguments</h3>
*
* <p>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:
* <pre class="code">
* --o1=v1 --o2</pre>
* 'o1' and 'o2' are treated as "option arguments", and the following assertions would
* evaluate true:
* <pre class="code">
* 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;</pre>
*
* 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.
*
* <p>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.
*
* <h3>Working with non-option arguments</h3>
*
* <p>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:
* <pre class="code">
* --o1=v1 --o2=v2 /path/to/file1 /path/to/file2</pre>
* 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:
* <pre class="code">
* 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");</pre>
*
* <p>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:
* <pre class="code">
* 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");</pre>
*
* <p>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":
* <pre class="code">
* 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();
* }</pre>
*
* <h3>Limitations</h3>
* 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
* <em>after</em> 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<T> extends PropertySource<T> {
/** 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.
* <p>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}
* <p>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<String> nonOptionArguments = this.getNonOptionArgs();
if (nonOptionArguments.isEmpty()) {
return null;
}
else {
return StringUtils.collectionToCommaDelimitedString(nonOptionArguments);
}
}
Collection<String> 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.
* <ul>
* <li>if the option is present and has no argument (e.g.: "--foo"), return an empty
* collection ({@code []})</li>
* <li>if the option is present and has a single value (e.g. "--foo=bar"), return a
* collection having one element ({@code ["bar"]})</li>
* <li>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"]})</li>
* <li>if the option is not present, return {@code null}</li>
* </ul>
*/
protected abstract List<String> getOptionValues(String name);
/**
* Return the collection of non-option arguments parsed from the command line. Never
* {@code null}.
*/
protected abstract List<String> getNonOptionArgs();
}

106
org.springframework.core/src/main/java/org/springframework/core/env/JOptCommandLinePropertySource.java vendored

@ -0,0 +1,106 @@ @@ -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}.
*
* <h2>Typical usage</h2>
* 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:
* <pre class="code">
* 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);
* // ...
* }</pre>
*
* See {@link CommandLinePropertySource} for complete general usage examples.
*
* <h3>Requirements</h3>
*
* <p>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<OptionSet> {
/**
* 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<String> getOptionValues(String key) {
List<?> argValues = this.source.valuesOf(key);
List<String> stringArgValues = new ArrayList<String>();
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<String> getNonOptionArgs() {
return this.source.nonOptionArguments();
}
}

86
org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java vendored

@ -0,0 +1,86 @@ @@ -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.
*
* <h3>Working with option arguments</h3>
* Option arguments must adhere to the exact syntax:
* <pre class="code">--optName[=optValue]</pre>
* 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 <em>without spaces</em>
* by an equals sign ("=").
*
* <h4>Valid examples of option arguments</h4>
* <pre class="code">
* --foo
* --foo=bar
* --foo="bar then baz"
* --foo=bar,baz,biz</pre>
*
* <h4>Invalid examples of option arguments</h4>
* <pre class="code">
* -foo
* --foo bar
* --foo = bar
* --foo=bar --foo=baz --foo=biz</pre>
*
* <h3>Working with non-option arguments</h3>
* 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;
}
}

113
org.springframework.core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java vendored

@ -0,0 +1,113 @@ @@ -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.
*
* <h3>Purpose</h3>
* 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: <em>option arguments</em> and <em>non-option arguments</em>, as
* described below <em>(some sections copied from Javadoc for {@link SimpleCommandLineArgsParser})</em>:
*
* <h3>Working with option arguments</h3>
* Option arguments must adhere to the exact syntax:
* <pre class="code">--optName[=optValue]</pre>
* 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 <em>without spaces</em>
* by an equals sign ("=").
*
* <h4>Valid examples of option arguments</h4>
* <pre class="code">
* --foo
* --foo=bar
* --foo="bar then baz"
* --foo=bar,baz,biz</pre>
*
* <h4>Invalid examples of option arguments</h4>
* <pre class="code">
* -foo
* --foo bar
* --foo = bar
* --foo=bar --foo=baz --foo=biz</pre>
*
* <h3>Working with non-option arguments</h3>
* 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.
*
* <h2>Typical usage</h2>
* <pre class="code">
* public static void main(String[] args) {
* PropertySource<?> ps = new SimpleCommandLinePropertySource(args);
* // ...
* }</pre>
*
* See {@link CommandLinePropertySource} for complete general usage examples.
*
* <h3>Beyond the basics</h3>
*
* <p>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<CommandLineArgs> {
/**
* 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<String> getOptionValues(String key) {
return this.source.getOptionValues(key);
}
@Override
protected List<String> getNonOptionArgs() {
return this.source.getNonOptionArgs();
}
}

165
org.springframework.core/src/test/java/org/springframework/core/env/JOptCommandLinePropertySourceTests.java vendored

@ -0,0 +1,165 @@ @@ -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"));
}
}

114
org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLineParserTests.java vendored

@ -0,0 +1,114 @@ @@ -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<String> 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");
}
}

126
org.springframework.core/src/test/java/org/springframework/core/env/SimpleCommandLinePropertySourceTests.java vendored

@ -0,0 +1,126 @@ @@ -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<String> nonOptionArgsList = env.getProperty("nonOptionArgs", List.class);
assertThat(nonOptionArgsList.get(0), equalTo("noa1"));
assertThat(nonOptionArgsList.get(1), equalTo("noa2"));
}
}

1
org.springframework.core/template.mf

@ -11,6 +11,7 @@ Import-Template: @@ -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

Loading…
Cancel
Save