/*
* Copyright 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.xd.dirt.server.options;
import static java.lang.Boolean.TRUE;
import static org.springframework.core.env.CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
import java.util.HashMap;
import java.util.Map;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.SimpleCommandLinePropertySource;
/**
* An {@code ApplicationListener} that will parse command line options and also replace the default boot commandline
* {@link PropertySource} with those values. This turns out to be the most elegant solution if we want to keep the
* {@link SpringApplicationBuilder} code clean.
*
* @author Eric Bottard
* @author Luke Taylor
*/
public class CommandLinePropertySourceOverridingListener<T extends CommonOptions> implements
ApplicationListener<ApplicationEnvironmentPreparedEvent> {
private T options;
private static ThreadLocal<Environment> environmentHolder = new ThreadLocal<Environment>();
public CommandLinePropertySourceOverridingListener(T options) {
super();
this.options = options;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
if (event.getArgs().length == 0) {
// If there are no arguments, create cmdLineArgs property source here. This would at least hold the default
// command line options.
if (event.getEnvironment().getPropertySources().get(COMMAND_LINE_PROPERTY_SOURCE_NAME) == null) {
event.getEnvironment().getPropertySources().addFirst(new SimpleCommandLinePropertySource());
}
}
CmdLineParser parser = null;
try {
environmentHolder.set(event.getEnvironment());
parser = new CmdLineParser(options);
parser.parseArgument(event.getArgs());
if (TRUE.equals(options.isShowHelp())) {
System.err.println("Usage:");
parser.printUsage(System.err);
System.exit(0);
}
}
catch (CmdLineException e) {
System.err.println(e.getMessage());
System.err.println();
System.err.println("Usage:");
parser.printUsage(System.err);
System.exit(1);
}
finally {
environmentHolder.set(null);
}
final EnumerablePropertySource<T> ps = new BeanPropertiesPropertySource<T>(COMMAND_LINE_PROPERTY_SOURCE_NAME,
options);
// Convert all properties to their String representation
// Also, don't advertise property names for which value is null
Map<String, Object> map = new HashMap<String, Object>();
for (String key : ps.getPropertyNames()) {
Object raw = ps.getProperty(key);
if (raw != null) {
map.put(key, raw.toString());
}
}
event.getEnvironment().getPropertySources().replace(COMMAND_LINE_PROPERTY_SOURCE_NAME,
new MapPropertySource(COMMAND_LINE_PROPERTY_SOURCE_NAME, map));
}
/*default*/static Environment getCurrentEnvironment() {
return environmentHolder.get();
}
}