Further maven module restructure
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
/**
|
||||
* Writes the 'Spring Zero' banner.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class Banner {
|
||||
|
||||
private static final String[] BANNER = {
|
||||
" . ____ _ _____ __ _",
|
||||
" /\\\\ / ___'_ __ _ _(_)_ __ __ _ |__ /___ _ __ ___\\ \\ \\",
|
||||
"( ( )\\___ | '_ | '_| | '_ \\/ _` | / // _ \\ '__/ _ \\\\ \\ \\",
|
||||
" \\\\/ ___)| |_)| | | | | || (_| | / /| __/ | | (/) |} } }",
|
||||
" ' |____| .__|_| |_|_| |_\\__, |'_____\\___|_| \\___// / /",
|
||||
" =========|_|==============|___/====================/_/_/" };
|
||||
|
||||
/**
|
||||
* Write the banner to the specified print stream.
|
||||
* @param printStream the output print stream
|
||||
*/
|
||||
public static void write(PrintStream printStream) {
|
||||
printStream.println();
|
||||
for (String line : BANNER) {
|
||||
printStream.println(line);
|
||||
}
|
||||
printStream.println();
|
||||
printStream.println();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.AbstractTypeHierarchyTraversingFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Loads bean definitions from underlying sources, including XML and JavaConfig. Acts as a
|
||||
* simple facade over {@link AnnotatedBeanDefinitionReader},
|
||||
* {@link XmlBeanDefinitionReader} and {@link ClassPathBeanDefinitionScanner}. See
|
||||
* {@link SpringApplication} for the types of sources that are supported.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #setBeanNameGenerator(BeanNameGenerator)
|
||||
*/
|
||||
class BeanDefinitionLoader {
|
||||
|
||||
private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new DefaultResourceLoader();
|
||||
|
||||
private Object[] sources;
|
||||
|
||||
private AnnotatedBeanDefinitionReader annotatedReader;
|
||||
|
||||
private XmlBeanDefinitionReader xmlReader;
|
||||
|
||||
private ClassPathBeanDefinitionScanner scanner;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
/**
|
||||
* Create a new {@link BeanDefinitionLoader} that will load beans into the specified
|
||||
* {@link BeanDefinitionRegistry}.
|
||||
* @param registry the bean definition registry that will contain the loaded beans
|
||||
* @param sources the bean sources
|
||||
*/
|
||||
public BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
|
||||
Assert.notNull(registry, "Registry must not be null");
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
this.sources = sources;
|
||||
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
|
||||
this.xmlReader = new XmlBeanDefinitionReader(registry);
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(registry);
|
||||
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean name generator to be used by the underlying readers and scanner.
|
||||
* @param beanNameGenerator the bean name generator
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.annotatedReader.setBeanNameGenerator(beanNameGenerator);
|
||||
this.xmlReader.setBeanNameGenerator(beanNameGenerator);
|
||||
this.scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource loader to be used by the underlying readers and scanner.
|
||||
* @param resourceLoader the resource loader
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.xmlReader.setResourceLoader(resourceLoader);
|
||||
this.scanner.setResourceLoader(resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the environment to be used by the underlying readers and scanner.
|
||||
* @param environment
|
||||
*/
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
this.annotatedReader.setEnvironment(environment);
|
||||
this.xmlReader.setEnvironment(environment);
|
||||
this.scanner.setEnvironment(environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the sources into the reader.
|
||||
* @return the number of loaded beans
|
||||
*/
|
||||
public int load() {
|
||||
int count = 0;
|
||||
for (Object source : this.sources) {
|
||||
count += load(source);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private int load(Object source) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
if (source instanceof Class<?>) {
|
||||
return load((Class<?>) source);
|
||||
}
|
||||
if (source instanceof Resource) {
|
||||
return load((Resource) source);
|
||||
}
|
||||
if (source instanceof Package) {
|
||||
return load((Package) source);
|
||||
}
|
||||
if (source instanceof CharSequence) {
|
||||
return load((CharSequence) source);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid source type " + source.getClass());
|
||||
}
|
||||
|
||||
private int load(Class<?> source) {
|
||||
if (isComponent(source)) {
|
||||
this.annotatedReader.register(source);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int load(Resource source) {
|
||||
return this.xmlReader.loadBeanDefinitions(source);
|
||||
}
|
||||
|
||||
private int load(Package source) {
|
||||
// FIXME register the scanned package for data to pick up
|
||||
return this.scanner.scan(source.getName());
|
||||
}
|
||||
|
||||
private int load(CharSequence source) {
|
||||
try {
|
||||
return load(Class.forName(source.toString()));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
|
||||
Resource loadedResource = (this.resourceLoader != null ? this.resourceLoader
|
||||
: DEFAULT_RESOURCE_LOADER).getResource(source.toString());
|
||||
if (loadedResource != null && loadedResource.exists()) {
|
||||
return load(loadedResource);
|
||||
}
|
||||
Package packageResource = Package.getPackage(source.toString());
|
||||
if (packageResource != null) {
|
||||
return load(packageResource);
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid source '" + source + "'");
|
||||
}
|
||||
|
||||
private boolean isComponent(Class<?> type) {
|
||||
// This has to be a bit of a guess. The only way to be sure that this type is
|
||||
// eligible is to make a bean definition out of it and try to instantiate it.
|
||||
if (AnnotationUtils.findAnnotation(type, Component.class) != null) {
|
||||
return true;
|
||||
}
|
||||
// Nested anonymous classes are not eligible for registration, nor are groovy
|
||||
// closures
|
||||
if (type.isAnonymousClass() || type.getName().matches(".*\\$_.*closure.*")
|
||||
|| type.getConstructors() == null || type.getConstructors().length == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple {@link TypeFilter} used to ensure that specified {@link Class} sources are
|
||||
* not accidentally re-added during scanning.
|
||||
*/
|
||||
private static class ClassExcludeFilter extends AbstractTypeHierarchyTraversingFilter {
|
||||
|
||||
private Set<String> classNames = new HashSet<String>();
|
||||
|
||||
public ClassExcludeFilter(Object... sources) {
|
||||
super(false, false);
|
||||
for (Object source : sources) {
|
||||
if (source instanceof Class<?>) {
|
||||
this.classNames.add(((Class<?>) source).getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matchClassName(String className) {
|
||||
return this.classNames.contains(className);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Interface used to indicate that a bean should <em>run</em> when it is contained within
|
||||
* a {@link SpringApplication}. Multiple {@link CommandLineRunner} beans can be defined
|
||||
* within the same application context and can be ordered using the {@link Ordered}
|
||||
* interface or {@link Order @Order} annotation.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public interface CommandLineRunner {
|
||||
|
||||
/**
|
||||
* Callback used to run the bean.
|
||||
* @param args incoming main method arguments
|
||||
* @throws Exception on error
|
||||
*/
|
||||
void run(String... args) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
/**
|
||||
* Interface used to generate an 'exit code' from a running command line
|
||||
* {@link SpringApplication}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see SpringApplication#exit(org.springframework.context.ApplicationContext,
|
||||
* ExitCodeGenerator...)
|
||||
*/
|
||||
public interface ExitCodeGenerator {
|
||||
|
||||
/**
|
||||
* Returns the exit code that should be returned from the application.
|
||||
* @return the exit code.
|
||||
*/
|
||||
int getExitCode();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
|
||||
/**
|
||||
* Classes that can be used to bootstrap and launch a Spring application from a Java main
|
||||
* method. By default class will perform the following steps to bootstrap your
|
||||
* application:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Create an appropriate {@link ApplicationContext} instance (depending on your
|
||||
* classpath)</li>
|
||||
*
|
||||
* <li>Register a {@link CommandLinePropertySource} to expose command line arguments as
|
||||
* Spring properties</li>
|
||||
*
|
||||
* <li>Refresh the application context, loading all singleton beans</li>
|
||||
*
|
||||
* <li>Trigger any {@link CommandLineRunner} beans</li>
|
||||
* </ul>
|
||||
*
|
||||
* In most circumstances the static {@link #run(Object, String[])} method can be called
|
||||
* directly from your {@literal main} method to bootstrap your application:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableAutoConfiguration
|
||||
* public class MyApplication {
|
||||
*
|
||||
* // ... Bean definitions
|
||||
*
|
||||
* public static void main(String[] args) throws Exception {
|
||||
* SpringApplication.run(MyApplication.class, args);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For more advanced configuration a {@link SpringApplication} instance can be created and
|
||||
* customized before being run:
|
||||
*
|
||||
* <pre class="code">
|
||||
* public static void main(String[] args) throws Exception {
|
||||
* SpringApplication app = new SpringApplication(MyApplication.class);
|
||||
* // ... customize app settings here
|
||||
* app.run(args)
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* {@link SpringApplication}s can read beans from a variety of different sources. It is
|
||||
* generally recommended that a single {@code @Configuration} class is used to bootstrap
|
||||
* your application, however, any of the following sources can also be used:
|
||||
*
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>{@link Class} - A Java class to be loaded by {@link AnnotatedBeanDefinitionReader}</li>
|
||||
*
|
||||
* <li>{@link Resource} - A XML resource to be loaded by {@link XmlBeanDefinitionReader}</li>
|
||||
*
|
||||
* <li>{@link Package} - A Java package to be scanned by
|
||||
* {@link ClassPathBeanDefinitionScanner}</li>
|
||||
*
|
||||
* <li>{@link CharSequence} - A class name, resource handle or package name to loaded as
|
||||
* appropriate. If the {@link CharSequence} cannot be resolved to class and does not
|
||||
* resolve to a {@link Resource} that exists it will be considered a {@link Package}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see #run(Object, String[])
|
||||
* @see #run(Object[], String[])
|
||||
* @see #SpringApplication(Object...)
|
||||
*/
|
||||
public class SpringApplication {
|
||||
|
||||
private static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
|
||||
+ "annotation.AnnotationConfigApplicationContext";
|
||||
|
||||
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.zero."
|
||||
+ "context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
|
||||
|
||||
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
|
||||
"org.springframework.web.context.ConfigurableWebApplicationContext" };
|
||||
|
||||
private Object[] sources;
|
||||
|
||||
private boolean showBanner = true;
|
||||
|
||||
private boolean addCommandLineProperties = true;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private BeanNameGenerator beanNameGenerator;
|
||||
|
||||
private ConfigurableEnvironment environment;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private Class<? extends ApplicationContext> applicationContextClass;
|
||||
|
||||
private boolean webEnvironment;
|
||||
|
||||
private List<ApplicationContextInitializer<?>> initializers;
|
||||
|
||||
private String[] defaultCommandLineArgs;
|
||||
|
||||
/**
|
||||
* Crate a new {@link SpringApplication} instance. The application context will load
|
||||
* beans from the specified sources (see {@link SpringApplication class-level}
|
||||
* documentation for details. The instance can be customized before calling
|
||||
* {@link #run(String...)}.
|
||||
* @param sources the bean sources
|
||||
* @see #run(Object, String[])
|
||||
* @see #SpringApplication(ResourceLoader, Object...)
|
||||
*/
|
||||
public SpringApplication(Object... sources) {
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
this.sources = sources;
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crate a new {@link SpringApplication} instance. The application context will load
|
||||
* beans from the specified sources (see {@link SpringApplication class-level}
|
||||
* documentation for details. The instance can be customized before calling
|
||||
* {@link #run(String...)}.
|
||||
* @param resourceLoader the resource loader to use
|
||||
* @param sources the bean sources
|
||||
* @see #run(Object, String[])
|
||||
* @see #SpringApplication(ResourceLoader, Object...)
|
||||
*/
|
||||
public SpringApplication(ResourceLoader resourceLoader, Object... sources) {
|
||||
Assert.notEmpty(sources, "Sources must not be empty");
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.sources = sources;
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
this.webEnvironment = deduceWebEnvironment();
|
||||
this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
|
||||
@SuppressWarnings("rawtypes")
|
||||
Collection<ApplicationContextInitializer> factories = SpringFactoriesLoader
|
||||
.loadFactories(ApplicationContextInitializer.class,
|
||||
SpringApplication.class.getClassLoader());
|
||||
for (ApplicationContextInitializer<?> initializer : factories) {
|
||||
this.initializers.add(initializer);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deduceWebEnvironment() {
|
||||
for (String className : WEB_ENVIRONMENT_CLASSES) {
|
||||
if (!ClassUtils.isPresent(className, null)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the Spring application, creating and refreshing a new
|
||||
* {@link ApplicationContext}.
|
||||
* @param args the application arguments (usually passed from a Java main method)
|
||||
* @return a running {@link ApplicationContext}
|
||||
*/
|
||||
public ApplicationContext run(String... args) {
|
||||
if (this.showBanner) {
|
||||
printBanner();
|
||||
}
|
||||
ApplicationContext context = createApplicationContext();
|
||||
postProcessApplicationContext(context);
|
||||
addPropertySources(context, args);
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
applyInitializers((ConfigurableApplicationContext) context);
|
||||
}
|
||||
load(context, this.sources);
|
||||
refresh(context);
|
||||
runCommandLineRunners(context, args);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a simple banner message to the console. Subclasses can override this method
|
||||
* to provide additional or alternative banners.
|
||||
* @see #setShowBanner(boolean)
|
||||
*/
|
||||
protected void printBanner() {
|
||||
Banner.write(System.out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any {@link ApplicationContextInitializer}s to the context before it is
|
||||
* refreshed.
|
||||
* @param context the configured ApplicationContext (not refreshed yet)
|
||||
* @see ConfigurableApplicationContext#refresh()
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
protected void applyInitializers(ConfigurableApplicationContext context) {
|
||||
for (ApplicationContextInitializer initializer : this.initializers) {
|
||||
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
|
||||
initializer.getClass(), ApplicationContextInitializer.class);
|
||||
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
|
||||
initializer.initialize(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy method used to create the {@link ApplicationContext}. By default this
|
||||
* method will respect any explicitly set application context or application context
|
||||
* class before falling back to a suitable default.
|
||||
* @return the application context (not yet refreshed)
|
||||
* @see #setApplicationContext(ApplicationContext)
|
||||
* @see #setApplicationContextClass(Class)
|
||||
*/
|
||||
protected ApplicationContext createApplicationContext() {
|
||||
if (this.applicationContext != null) {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
Class<?> contextClass = this.applicationContextClass;
|
||||
if (contextClass == null) {
|
||||
try {
|
||||
contextClass = Class
|
||||
.forName(this.webEnvironment ? DEFAULT_WEB_CONTEXT_CLASS
|
||||
: DEFAULT_CONTEXT_CLASS);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable create a default ApplicationContext, "
|
||||
+ "please specify an ApplicationContextClass", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return (ApplicationContext) BeanUtils.instantiate(contextClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
|
||||
* apply additional processing as required.
|
||||
* @param context the application context
|
||||
*/
|
||||
protected void postProcessApplicationContext(ApplicationContext context) {
|
||||
if (this.webEnvironment) {
|
||||
if (context instanceof ConfigurableWebApplicationContext) {
|
||||
ConfigurableWebApplicationContext configurableContext = (ConfigurableWebApplicationContext) context;
|
||||
if (this.beanNameGenerator != null) {
|
||||
configurableContext.getBeanFactory().registerSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
|
||||
this.beanNameGenerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context instanceof AbstractApplicationContext && this.environment != null) {
|
||||
((AbstractApplicationContext) context).setEnvironment(this.environment);
|
||||
}
|
||||
|
||||
if (context instanceof GenericApplicationContext && this.resourceLoader != null) {
|
||||
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any {@link PropertySource}s to the application context environment.
|
||||
* @param context the application context
|
||||
* @param args run arguments
|
||||
*/
|
||||
protected void addPropertySources(ApplicationContext context, String[] args) {
|
||||
Environment environment = context.getEnvironment();
|
||||
if (environment instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment;
|
||||
if (this.addCommandLineProperties) {
|
||||
// Don't use SimpleCommandLinePropertySource (SPR-10579)
|
||||
PropertySource<?> propertySource = new MapPropertySource(
|
||||
"commandLineArgs", mergeCommandLineArgs(
|
||||
this.defaultCommandLineArgs, args));
|
||||
configurable.getPropertySources().addFirst(propertySource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two sets of command lines, the defaults and the ones passed in at run time.
|
||||
*
|
||||
* @param defaults the default values
|
||||
* @param args the ones passed in at runtime
|
||||
* @return a new command line
|
||||
*/
|
||||
protected Map<String, Object> mergeCommandLineArgs(String[] defaults, String[] args) {
|
||||
|
||||
if (defaults == null) {
|
||||
defaults = new String[0];
|
||||
}
|
||||
|
||||
List<String> nonopts = new ArrayList<String>();
|
||||
Map<String, Object> options = new LinkedHashMap<String, Object>();
|
||||
|
||||
for (String arg : defaults) {
|
||||
if (isOptionArg(arg)) {
|
||||
addOptionArg(options, arg);
|
||||
}
|
||||
else {
|
||||
nonopts.add(arg);
|
||||
}
|
||||
}
|
||||
for (String arg : args) {
|
||||
if (isOptionArg(arg)) {
|
||||
addOptionArg(options, arg);
|
||||
}
|
||||
else if (!nonopts.contains(arg)) {
|
||||
nonopts.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
for (String key : nonopts) {
|
||||
options.put(key, "");
|
||||
}
|
||||
|
||||
return options;
|
||||
|
||||
}
|
||||
|
||||
private boolean isOptionArg(String arg) {
|
||||
return arg.startsWith("--");
|
||||
}
|
||||
|
||||
private void addOptionArg(Map<String, Object> map, String arg) {
|
||||
String optionText = arg.substring(2, arg.length());
|
||||
String optionName;
|
||||
String optionValue = "";
|
||||
if (optionText.contains("=")) {
|
||||
optionName = optionText.substring(0, optionText.indexOf('='));
|
||||
optionValue = optionText.substring(optionText.indexOf('=') + 1,
|
||||
optionText.length());
|
||||
}
|
||||
else {
|
||||
optionName = optionText;
|
||||
}
|
||||
if (optionName.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid argument syntax: " + arg);
|
||||
}
|
||||
map.put(optionName, optionValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load beans into the application context.
|
||||
* @param context the context to load beans into
|
||||
* @param sources the sources to load
|
||||
*/
|
||||
protected void load(ApplicationContext context, Object[] sources) {
|
||||
BeanDefinitionLoader loader = createBeanDefinitionLoader(
|
||||
getBeanDefinitionRegistry(context), sources);
|
||||
if (this.beanNameGenerator != null) {
|
||||
loader.setBeanNameGenerator(this.beanNameGenerator);
|
||||
}
|
||||
if (this.resourceLoader != null) {
|
||||
loader.setResourceLoader(this.resourceLoader);
|
||||
}
|
||||
if (this.environment != null) {
|
||||
loader.setEnvironment(this.environment);
|
||||
}
|
||||
loader.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context the application context
|
||||
* @return the BeanDefinitionRegistry if it can be determined
|
||||
*/
|
||||
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
|
||||
if (context instanceof BeanDefinitionRegistry) {
|
||||
return (BeanDefinitionRegistry) context;
|
||||
}
|
||||
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method used to create the {@link BeanDefinitionLoader}.
|
||||
* @param registry the bean definition registry
|
||||
* @param sources the sources to load
|
||||
* @return the {@link BeanDefinitionLoader} that will be used to load beans
|
||||
*/
|
||||
protected BeanDefinitionLoader createBeanDefinitionLoader(
|
||||
BeanDefinitionRegistry registry, Object[] sources) {
|
||||
return new BeanDefinitionLoader(registry, sources);
|
||||
}
|
||||
|
||||
private void runCommandLineRunners(ApplicationContext context, String... args) {
|
||||
List<CommandLineRunner> runners = new ArrayList<CommandLineRunner>(context
|
||||
.getBeansOfType(CommandLineRunner.class).values());
|
||||
AnnotationAwareOrderComparator.sort(runners);
|
||||
for (CommandLineRunner runner : runners) {
|
||||
try {
|
||||
runner.run(args);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the underlying {@link ApplicationContext}.
|
||||
* @param applicationContext the application context to refresh
|
||||
*/
|
||||
protected void refresh(ApplicationContext applicationContext) {
|
||||
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
|
||||
((AbstractApplicationContext) applicationContext).refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this application is running within a web environment. If not specified will
|
||||
* attempt to deduce the environment based on the classpath.
|
||||
* @param webEnvironment if the application is running in a web environment
|
||||
*/
|
||||
public void setWebEnvironment(boolean webEnvironment) {
|
||||
this.webEnvironment = webEnvironment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if the Spring banner should be displayed when the application runs. Defaults
|
||||
* to {@code true}.
|
||||
* @param showBanner if the banner should be shown
|
||||
* @see #printBanner()
|
||||
*/
|
||||
public void setShowBanner(boolean showBanner) {
|
||||
this.showBanner = showBanner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if a {@link CommandLinePropertySource} should be added to the application
|
||||
* context in order to expose arguments. Defaults to {@code true}.
|
||||
* @param addCommandLineProperties if command line arguments should be exposed
|
||||
*/
|
||||
public void setAddCommandLineProperties(boolean addCommandLineProperties) {
|
||||
this.addCommandLineProperties = addCommandLineProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some default command line arguments which can be overridden by those passed
|
||||
* into the run methods.
|
||||
* @param defaultCommandLineArgs the default command line args to set
|
||||
*/
|
||||
public void setDefaultCommandLineArgs(String... defaultCommandLineArgs) {
|
||||
this.defaultCommandLineArgs = defaultCommandLineArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bean name generator that should be used when generating bean names.
|
||||
* @param beanNameGenerator the bean name generator
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the underlying environment that should be used when loading.
|
||||
* @param environment the environment
|
||||
*/
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ResourceLoader} that should be used when loading resources.
|
||||
* @param resourceLoader the resource loader
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of Spring {@link ApplicationContext} that will be created. If not
|
||||
* specified defaults to {@link #DEFAULT_WEB_CONTEXT_CLASS} for web based applications
|
||||
* or {@link AnnotationConfigApplicationContext} for non web based applications.
|
||||
* @param applicationContextClass the context class to set
|
||||
* @see #setApplicationContext(ApplicationContext)
|
||||
*/
|
||||
public void setApplicationContextClass(
|
||||
Class<? extends ApplicationContext> applicationContextClass) {
|
||||
this.applicationContextClass = applicationContextClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a Spring {@link ApplicationContext} that will be used for the application. If
|
||||
* not specified an {@link #DEFAULT_WEB_CONTEXT_CLASS} will be created for web based
|
||||
* applications or an {@link AnnotationConfigApplicationContext} for non web based
|
||||
* applications.
|
||||
* @param applicationContext the spring application context.
|
||||
* @see #setApplicationContextClass(Class)
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ApplicationContextInitializer} that will be applied to the Spring
|
||||
* {@link ApplicationContext}. Any existing initializers will be replaced.
|
||||
* @param initializers the initializers to set
|
||||
*/
|
||||
public void setInitializers(
|
||||
Collection<? extends ApplicationContextInitializer<?>> initializers) {
|
||||
this.initializers = new ArrayList<ApplicationContextInitializer<?>>(initializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link ApplicationContextInitializer}s to be applied to the Spring
|
||||
* {@link ApplicationContext} .
|
||||
* @param initializers the initializers to add
|
||||
*/
|
||||
public void addInitializers(ApplicationContextInitializer<?>... initializers) {
|
||||
this.initializers.addAll(Arrays.asList(initializers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable list of the {@link ApplicationContextInitializer}s that will be
|
||||
* applied to the Spring {@link ApplicationContext}.
|
||||
* @return the initializers
|
||||
*/
|
||||
public List<ApplicationContextInitializer<?>> getInitializers() {
|
||||
return this.initializers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper that can be used to run a {@link SpringApplication} from the
|
||||
* specified source using default settings.
|
||||
* @param source the source to load
|
||||
* @param args the application arguments (usually passed from a Java main method)
|
||||
* @return the running {@link ApplicationContext}
|
||||
*/
|
||||
public static ApplicationContext run(Object source, String... args) {
|
||||
return run(new Object[] { source }, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper that can be used to run a {@link SpringApplication} from the
|
||||
* specified sources using default settings.
|
||||
* @param sources the sources to load
|
||||
* @param args the application arguments (usually passed from a Java main method)
|
||||
* @return the running {@link ApplicationContext}
|
||||
*/
|
||||
public static ApplicationContext run(Object[] sources, String[] args) {
|
||||
return new SpringApplication(sources).run(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper that can be used to exit a {@link SpringApplication} and obtain a
|
||||
* code indicating success (0) or otherwise. Does not throw exceptions but should
|
||||
* print stack traces of any encountered. Applies the specified
|
||||
* {@link ExitCodeGenerator} in addition to any Spring beans that implement
|
||||
* {@link ExitCodeGenerator}. In the case of multiple exit codes the highest value
|
||||
* will be used (or if all values are negative, the lowest value will be used)
|
||||
* @param context the context to close if possible
|
||||
* @param exitCodeGenerators exist code generators
|
||||
* @return the outcome (0 if successful)
|
||||
*/
|
||||
public static int exit(ApplicationContext context,
|
||||
ExitCodeGenerator... exitCodeGenerators) {
|
||||
int exitCode = 0;
|
||||
try {
|
||||
try {
|
||||
List<ExitCodeGenerator> generators = new ArrayList<ExitCodeGenerator>();
|
||||
generators.addAll(Arrays.asList(exitCodeGenerators));
|
||||
generators.addAll(context.getBeansOfType(ExitCodeGenerator.class)
|
||||
.values());
|
||||
exitCode = getExitCode(generators);
|
||||
}
|
||||
finally {
|
||||
close(context);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
exitCode = (exitCode == 0 ? 1 : exitCode);
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private static int getExitCode(List<ExitCodeGenerator> exitCodeGenerators) {
|
||||
int exitCode = 0;
|
||||
for (ExitCodeGenerator exitCodeGenerator : exitCodeGenerators) {
|
||||
try {
|
||||
int value = exitCodeGenerator.getExitCode();
|
||||
if (value > 0 && value > exitCode || value < 0 && value < exitCode) {
|
||||
exitCode = value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
exitCode = (exitCode == 0 ? 1 : exitCode);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private static void close(ApplicationContext context) {
|
||||
if (context instanceof ConfigurableApplicationContext) {
|
||||
ConfigurableApplicationContext closable = (ConfigurableApplicationContext) context;
|
||||
closable.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.introspector.Property;
|
||||
import org.yaml.snakeyaml.introspector.PropertyUtils;
|
||||
import org.yaml.snakeyaml.nodes.NodeId;
|
||||
|
||||
/**
|
||||
* Extended version of snakeyaml's Constructor class to facilitate mapping custom YAML
|
||||
* keys to Javabean property names.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
public class CustomPropertyConstructor extends Constructor {
|
||||
|
||||
private final Map<Class<?>, Map<String, Property>> properties = new HashMap<Class<?>, Map<String, Property>>();
|
||||
|
||||
private final PropertyUtils propertyUtils = new PropertyUtils();
|
||||
|
||||
public CustomPropertyConstructor(Class<?> theRoot) {
|
||||
super(theRoot);
|
||||
this.yamlClassConstructors.put(NodeId.mapping,
|
||||
new CustomPropertyConstructMapping());
|
||||
}
|
||||
|
||||
public CustomPropertyConstructor(Class<?> theRoot,
|
||||
Map<Class<?>, Map<String, String>> propertyAliases) {
|
||||
this(theRoot);
|
||||
|
||||
for (Class<?> key : propertyAliases.keySet()) {
|
||||
Map<String, String> map = propertyAliases.get(key);
|
||||
if (map != null) {
|
||||
for (String alias : map.keySet()) {
|
||||
addPropertyAlias(alias, key, map.get(alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an alias for a Javabean property name on a particular type. The values of YAML
|
||||
* keys with the alias name will be mapped to the Javabean property.
|
||||
* @param alias the alias to map
|
||||
* @param type the type of property
|
||||
* @param name the property name
|
||||
*/
|
||||
protected final void addPropertyAlias(String alias, Class<?> type, String name) {
|
||||
Map<String, Property> typeMap = this.properties.get(type);
|
||||
|
||||
if (typeMap == null) {
|
||||
typeMap = new HashMap<String, Property>();
|
||||
this.properties.put(type, typeMap);
|
||||
}
|
||||
|
||||
try {
|
||||
typeMap.put(alias, this.propertyUtils.getProperty(type, name));
|
||||
}
|
||||
catch (IntrospectionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomPropertyConstructMapping extends ConstructMapping {
|
||||
|
||||
@Override
|
||||
protected Property getProperty(Class<?> type, String name)
|
||||
throws IntrospectionException {
|
||||
Property p = lookupProperty(type, name);
|
||||
|
||||
return p != null ? p : super.getProperty(type, name);
|
||||
}
|
||||
|
||||
private Property lookupProperty(Class<?> type, String name) {
|
||||
Map<String, Property> m = CustomPropertyConstructor.this.properties.get(type);
|
||||
|
||||
if (m != null) {
|
||||
return m.get(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* {@link PropertyEditor} for {@link InetAddress} objects.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class InetAddressEditor extends PropertyEditorSupport implements PropertyEditor {
|
||||
|
||||
@Override
|
||||
public String getAsText() {
|
||||
return ((InetAddress) getValue()).getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
try {
|
||||
setValue(InetAddress.getByName(text));
|
||||
}
|
||||
catch (UnknownHostException ex) {
|
||||
throw new IllegalArgumentException("Cannot locate host", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.validation.Validator;
|
||||
|
||||
/**
|
||||
* Validate some {@link Properties} by binding them to an object of a specified type and
|
||||
* then optionally running a {@link Validator} over it.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertiesConfigurationFactory<T> implements FactoryBean<T>,
|
||||
MessageSourceAware, InitializingBean {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean ignoreUnknownFields = true;
|
||||
|
||||
private boolean ignoreInvalidFields = false;
|
||||
|
||||
private boolean exceptionIfInvalid;
|
||||
|
||||
private Properties properties;
|
||||
|
||||
private PropertySources propertySources;
|
||||
|
||||
private T target;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
private boolean hasBeenBound = false;
|
||||
|
||||
private String targetName;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* @param target the target object to bind too
|
||||
* @see #PropertiesConfigurationFactory(Class)
|
||||
*/
|
||||
public PropertiesConfigurationFactory(T target) {
|
||||
Assert.notNull(target);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new factory for an object of the given type.
|
||||
*
|
||||
* @see #PropertiesConfigurationFactory(Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public PropertiesConfigurationFactory(Class<?> type) {
|
||||
Assert.notNull(type);
|
||||
this.target = (T) BeanUtils.instantiate(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to ignore unknown fields, that is, whether to ignore bind parameters
|
||||
* that do not have corresponding fields in the target object.
|
||||
* <p>
|
||||
* Default is "true". Turn this off to enforce that all bind parameters must have a
|
||||
* matching field in the target object.
|
||||
* @param ignoreUnknownFields if unknown fields should be ignored
|
||||
*/
|
||||
public void setIgnoreUnknownFields(boolean ignoreUnknownFields) {
|
||||
this.ignoreUnknownFields = ignoreUnknownFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to ignore invalid fields, that is, whether to ignore bind parameters
|
||||
* that have corresponding fields in the target object which are not accessible (for
|
||||
* example because of null values in the nested path).
|
||||
* <p>
|
||||
* Default is "false". Turn this on to ignore bind parameters for nested objects in
|
||||
* non-existing parts of the target object graph.
|
||||
* @param ignoreInvalidFields if invalid fields should be ignored
|
||||
*/
|
||||
public void setIgnoreInvalidFields(boolean ignoreInvalidFields) {
|
||||
this.ignoreInvalidFields = ignoreInvalidFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetName the target name to set
|
||||
*/
|
||||
public void setTargetName(String targetName) {
|
||||
this.targetName = targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messageSource the messageSource to set
|
||||
*/
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param properties the properties to set
|
||||
*/
|
||||
public void setProperties(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param propertySources the propertySources to set
|
||||
*/
|
||||
public void setPropertySources(PropertySources propertySources) {
|
||||
this.propertySources = propertySources;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param conversionService the conversionService to set
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validator the validator to set
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public void setExceptionIfInvalid(boolean exceptionIfInvalid) {
|
||||
this.exceptionIfInvalid = exceptionIfInvalid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
bindPropertiesToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
if (this.target == null) {
|
||||
return Object.class;
|
||||
}
|
||||
return this.target.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
if (!this.hasBeenBound) {
|
||||
bindPropertiesToTarget();
|
||||
}
|
||||
return this.target;
|
||||
}
|
||||
|
||||
public void bindPropertiesToTarget() throws BindException {
|
||||
Assert.state(this.properties != null || this.propertySources != null,
|
||||
"Properties or propertySources should not be null");
|
||||
try {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
if (this.properties != null) {
|
||||
this.logger.trace("Properties:\n" + this.properties);
|
||||
}
|
||||
else {
|
||||
this.logger.trace("Property Sources: " + this.propertySources);
|
||||
}
|
||||
}
|
||||
this.hasBeenBound = true;
|
||||
doBindPropertiesToTarget();
|
||||
}
|
||||
catch (BindException ex) {
|
||||
if (this.exceptionIfInvalid) {
|
||||
throw ex;
|
||||
}
|
||||
this.logger.error("Failed to load Properties validation bean. "
|
||||
+ "Your Properties may be invalid.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void doBindPropertiesToTarget() throws BindException {
|
||||
|
||||
RelaxedDataBinder dataBinder = (this.targetName != null ? new RelaxedDataBinder(
|
||||
this.target, this.targetName) : new RelaxedDataBinder(this.target));
|
||||
if (this.validator != null) {
|
||||
dataBinder.setValidator(this.validator);
|
||||
}
|
||||
if (this.conversionService != null) {
|
||||
dataBinder.setConversionService(this.conversionService);
|
||||
}
|
||||
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
|
||||
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
|
||||
customizeBinder(dataBinder);
|
||||
|
||||
PropertyValues propertyValues = (this.properties != null ? new MutablePropertyValues(
|
||||
this.properties)
|
||||
: new PropertySourcesPropertyValues(this.propertySources));
|
||||
dataBinder.bind(propertyValues);
|
||||
|
||||
if (this.validator != null) {
|
||||
validate(dataBinder);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(RelaxedDataBinder dataBinder) throws BindException {
|
||||
dataBinder.validate();
|
||||
BindingResult errors = dataBinder.getBindingResult();
|
||||
if (errors.hasErrors()) {
|
||||
this.logger.error("Properties configuration failed validation");
|
||||
for (ObjectError error : errors.getAllErrors()) {
|
||||
this.logger.error(this.messageSource != null ? this.messageSource
|
||||
.getMessage(error, Locale.getDefault()) + " (" + error + ")"
|
||||
: error);
|
||||
}
|
||||
if (this.exceptionIfInvalid) {
|
||||
BindException summary = new BindException(errors);
|
||||
throw summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dataBinder the data binder that will be used to bind and validate
|
||||
*/
|
||||
protected void customizeBinder(DataBinder dataBinder) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.core.env.PropertySourcesPropertyResolver;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
||||
/**
|
||||
* A {@link PropertyValues} implementation backed by a {@link PropertySources}, bridging
|
||||
* the two abstractions and allowing (for instance) a regular {@link DataBinder} to be
|
||||
* used with the latter.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertySourcesPropertyValues implements PropertyValues {
|
||||
|
||||
private Map<String, PropertyValue> propertyValues = new ConcurrentHashMap<String, PropertyValue>();
|
||||
|
||||
private PropertySources propertySources;
|
||||
|
||||
/**
|
||||
* Create a new PropertyValues from the given PropertySources
|
||||
*
|
||||
* @param propertySources a PropertySources instance
|
||||
*/
|
||||
public PropertySourcesPropertyValues(PropertySources propertySources) {
|
||||
this.propertySources = propertySources;
|
||||
// TODO: maybe lazy initialization?
|
||||
PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
|
||||
propertySources);
|
||||
for (PropertySource<?> source : propertySources) {
|
||||
if (source instanceof EnumerablePropertySource) {
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
if (enumerable.getPropertyNames().length > 0) {
|
||||
for (String propertyName : enumerable.getPropertyNames()) {
|
||||
Object value = resolver.getProperty(propertyName);
|
||||
this.propertyValues.put(propertyName, new PropertyValue(
|
||||
propertyName, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValue[] getPropertyValues() {
|
||||
Collection<PropertyValue> values = this.propertyValues.values();
|
||||
return values.toArray(new PropertyValue[values.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValue getPropertyValue(String propertyName) {
|
||||
PropertyValue propertyValue = this.propertyValues.get(propertyName);
|
||||
if (propertyValue != null) {
|
||||
return propertyValue;
|
||||
}
|
||||
for (PropertySource<?> source : this.propertySources) {
|
||||
Object value = source.getProperty(propertyName);
|
||||
if (value != null) {
|
||||
propertyValue = new PropertyValue(propertyName, value);
|
||||
this.propertyValues.put(propertyName, propertyValue);
|
||||
return propertyValue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertyValues changesSince(PropertyValues old) {
|
||||
MutablePropertyValues changes = new MutablePropertyValues();
|
||||
// for each property value in the new set
|
||||
for (PropertyValue newPv : getPropertyValues()) {
|
||||
// if there wasn't an old one, add it
|
||||
PropertyValue pvOld = old.getPropertyValue(newPv.getName());
|
||||
if (pvOld == null) {
|
||||
changes.addPropertyValue(newPv);
|
||||
}
|
||||
else if (!pvOld.equals(newPv)) {
|
||||
// it's changed
|
||||
changes.addPropertyValue(newPv);
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String propertyName) {
|
||||
return getPropertyValue(propertyName) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.propertyValues.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.InvalidPropertyException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
||||
/**
|
||||
* Binder implementation that allows caller to bind to maps and also allows property names
|
||||
* to match a bit loosely (if underscores or dashes are removed and replaced with camel
|
||||
* case for example).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class RelaxedDataBinder extends DataBinder {
|
||||
|
||||
private String namePrefix;
|
||||
|
||||
/**
|
||||
* @param target the target into which properties are bound
|
||||
*/
|
||||
public RelaxedDataBinder(Object target) {
|
||||
super(wrapTarget(target));
|
||||
}
|
||||
|
||||
private static Object wrapTarget(Object target) {
|
||||
if (target instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) target;
|
||||
target = new MapHolder(map);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target the target into which properties are bound
|
||||
* @param namePrefix An optional prefix to be used when reading properties
|
||||
*/
|
||||
public RelaxedDataBinder(Object target, String namePrefix) {
|
||||
super(wrapTarget(target), (StringUtils.hasLength(namePrefix) ? namePrefix
|
||||
: DEFAULT_OBJECT_NAME));
|
||||
this.namePrefix = (StringUtils.hasLength(namePrefix) ? namePrefix + "." : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBind(MutablePropertyValues propertyValues) {
|
||||
propertyValues = modifyProperties(propertyValues, getTarget());
|
||||
// Harmless additional property editor comes in very handy sometimes...
|
||||
getPropertyEditorRegistry().registerCustomEditor(InetAddress.class,
|
||||
new InetAddressEditor());
|
||||
super.doBind(propertyValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the property values so that period separated property paths are valid for
|
||||
* map keys. Also creates new maps for properties of map type that are null (assuming
|
||||
* all maps are potentially nested). The standard bracket <code>[...]</code>
|
||||
* dereferencing is also accepted.
|
||||
*
|
||||
* @param propertyValues the property values
|
||||
* @param target the target object
|
||||
*/
|
||||
private MutablePropertyValues modifyProperties(MutablePropertyValues propertyValues,
|
||||
Object target) {
|
||||
|
||||
propertyValues = getProperyValuesForNamePrefix(propertyValues);
|
||||
|
||||
if (target instanceof MapHolder) {
|
||||
propertyValues = addMapPrefix(propertyValues);
|
||||
}
|
||||
|
||||
BeanWrapper targetWrapper = new BeanWrapperImpl(target);
|
||||
targetWrapper.setAutoGrowNestedPaths(true);
|
||||
|
||||
List<PropertyValue> list = propertyValues.getPropertyValueList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
modifyProperty(propertyValues, targetWrapper, list.get(i), i);
|
||||
}
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
private MutablePropertyValues addMapPrefix(MutablePropertyValues propertyValues) {
|
||||
MutablePropertyValues rtn = new MutablePropertyValues();
|
||||
for (PropertyValue pv : propertyValues.getPropertyValues()) {
|
||||
rtn.add("map." + pv.getName(), pv.getValue());
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
private MutablePropertyValues getProperyValuesForNamePrefix(
|
||||
MutablePropertyValues propertyValues) {
|
||||
if (this.namePrefix == null) {
|
||||
return propertyValues;
|
||||
}
|
||||
MutablePropertyValues rtn = new MutablePropertyValues();
|
||||
for (PropertyValue pv : propertyValues.getPropertyValues()) {
|
||||
String name = pv.getName();
|
||||
if (name.startsWith(this.namePrefix)) {
|
||||
name = name.substring(this.namePrefix.length());
|
||||
rtn.add(name, pv.getValue());
|
||||
}
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
private void modifyProperty(MutablePropertyValues propertyValues, BeanWrapper target,
|
||||
PropertyValue propertyValue, int index) {
|
||||
|
||||
String name = propertyValue.getName();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Class<?> type = target.getWrappedClass();
|
||||
|
||||
for (String key : StringUtils.delimitedListToStringArray(name, ".")) {
|
||||
if (builder.length() != 0) {
|
||||
builder.append(".");
|
||||
}
|
||||
String oldKey = key;
|
||||
key = getActualPropertyName(target, builder.toString(), oldKey);
|
||||
builder.append(key);
|
||||
String base = builder.toString();
|
||||
if (!oldKey.equals(key)) {
|
||||
propertyValues.setPropertyValueAt(
|
||||
new PropertyValue(base, propertyValue.getValue()), index);
|
||||
}
|
||||
type = target.getPropertyType(base);
|
||||
|
||||
// Any nested properties that are maps, are assumed to be simple nested
|
||||
// maps of maps...
|
||||
if (type != null && Map.class.isAssignableFrom(type)) {
|
||||
Map<String, Object> nested = new LinkedHashMap<String, Object>();
|
||||
if (target.getPropertyValue(base) != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> existing = (Map<String, Object>) target
|
||||
.getPropertyValue(base);
|
||||
nested = existing;
|
||||
}
|
||||
else {
|
||||
target.setPropertyValue(base, nested);
|
||||
}
|
||||
modifyPopertiesForMap(nested, propertyValues, index, base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void modifyPopertiesForMap(Map<String, Object> target,
|
||||
MutablePropertyValues propertyValues, int index, String base) {
|
||||
PropertyValue propertyValue = propertyValues.getPropertyValueList().get(index);
|
||||
String name = propertyValue.getName();
|
||||
String suffix = name.substring(base.length());
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
String[] tree = StringUtils.delimitedListToStringArray(
|
||||
suffix.startsWith(".") ? suffix.substring(1) : suffix, ".");
|
||||
for (int j = 0; j < tree.length - 1; j++) {
|
||||
if (!target.containsKey(tree[j])) {
|
||||
target.put(tree[j], value);
|
||||
}
|
||||
target = value;
|
||||
value = new LinkedHashMap<String, Object>();
|
||||
}
|
||||
String refName = base + suffix.replaceAll("\\.([a-zA-Z0-9]*)", "[$1]");
|
||||
propertyValues.setPropertyValueAt(
|
||||
new PropertyValue(refName, propertyValue.getValue()), index);
|
||||
|
||||
}
|
||||
|
||||
private String getActualPropertyName(BeanWrapper target, String prefix, String name) {
|
||||
for (Variation variation : Variation.values()) {
|
||||
for (Manipulation manipulation : Manipulation.values()) {
|
||||
// Apply all manipulations before attempting variations
|
||||
String candidate = variation.apply(manipulation.apply(name));
|
||||
try {
|
||||
if (target.getPropertyType(prefix + candidate) != null) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
catch (InvalidPropertyException ex) {
|
||||
// swallow and contrinue
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static enum Variation {
|
||||
NONE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
UPPERCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.toUpperCase();
|
||||
}
|
||||
},
|
||||
|
||||
LOWERCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract String apply(String value);
|
||||
}
|
||||
|
||||
static enum Manipulation {
|
||||
NONE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value;
|
||||
}
|
||||
},
|
||||
UNDERSCORE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
return value.replace("-", "_");
|
||||
}
|
||||
},
|
||||
|
||||
CAMELCASE {
|
||||
@Override
|
||||
public String apply(String value) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String field : UNDERSCORE.apply(value).split("_")) {
|
||||
builder.append(builder.length() == 0 ? field : StringUtils
|
||||
.capitalize(field));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
};
|
||||
|
||||
public abstract String apply(String value);
|
||||
}
|
||||
|
||||
static class MapHolder {
|
||||
private Map<String, Object> map;
|
||||
|
||||
public MapHolder(Map<String, Object> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public void setMap(Map<String, Object> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMap() {
|
||||
return this.map;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.ObjectError;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
/**
|
||||
* Validate some YAML by binding it to an object of a specified type and then optionally
|
||||
* running a {@link Validator} over it.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlConfigurationFactory<T> implements FactoryBean<T>, MessageSourceAware,
|
||||
InitializingBean {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Class<?> type;
|
||||
|
||||
private boolean exceptionIfInvalid;
|
||||
|
||||
private String yaml;
|
||||
|
||||
private Resource resource;
|
||||
|
||||
private T configuration;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private MessageSource messageSource;
|
||||
|
||||
private Map<Class<?>, Map<String, String>> propertyAliases = Collections.emptyMap();
|
||||
|
||||
/**
|
||||
* Sets a validation constructor which will be applied to the YAML doc to see whether
|
||||
* it matches the expected Javabean.
|
||||
* @param type the root type
|
||||
*/
|
||||
public YamlConfigurationFactory(Class<?> type) {
|
||||
Assert.notNull(type);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param messageSource the messageSource to set
|
||||
*/
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messageSource = messageSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param propertyAliases the propertyAliases to set
|
||||
*/
|
||||
public void setPropertyAliases(Map<Class<?>, Map<String, String>> propertyAliases) {
|
||||
this.propertyAliases = new HashMap<Class<?>, Map<String, String>>(propertyAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param yaml the yaml to set
|
||||
*/
|
||||
public void setYaml(String yaml) {
|
||||
this.yaml = yaml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource the resource to set
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validator the validator to set
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
public void setExceptionIfInvalid(boolean exceptionIfInvalid) {
|
||||
this.exceptionIfInvalid = exceptionIfInvalid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
if (this.yaml == null) {
|
||||
Assert.state(this.resource != null, "Resource should not be null");
|
||||
this.yaml = StreamUtils.copyToString(this.resource.getInputStream(),
|
||||
Charset.defaultCharset());
|
||||
}
|
||||
|
||||
Assert.state(this.yaml != null, "Yaml document should not be null: "
|
||||
+ "either set it directly or set the resource to load it from");
|
||||
|
||||
try {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Yaml document is\n" + this.yaml);
|
||||
}
|
||||
Constructor constructor = new CustomPropertyConstructor(this.type,
|
||||
this.propertyAliases);
|
||||
this.configuration = (T) (new Yaml(constructor)).load(this.yaml);
|
||||
if (this.validator != null) {
|
||||
validate();
|
||||
}
|
||||
}
|
||||
catch (YAMLException ex) {
|
||||
if (this.exceptionIfInvalid) {
|
||||
throw ex;
|
||||
}
|
||||
this.logger.error("Failed to load YAML validation bean. "
|
||||
+ "Your YAML file may be invalid.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate() throws BindException {
|
||||
BindingResult errors = new BeanPropertyBindingResult(this.configuration,
|
||||
"configuration");
|
||||
this.validator.validate(this.configuration, errors);
|
||||
|
||||
if (errors.hasErrors()) {
|
||||
this.logger.error("YAML configuration failed validation");
|
||||
for (ObjectError error : errors.getAllErrors()) {
|
||||
this.logger.error(this.messageSource != null ? this.messageSource
|
||||
.getMessage(error, Locale.getDefault()) + " (" + error + ")"
|
||||
: error);
|
||||
}
|
||||
if (this.exceptionIfInvalid) {
|
||||
BindException summary = new BindException(errors);
|
||||
throw summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
if (this.configuration == null) {
|
||||
return Object.class;
|
||||
}
|
||||
return this.configuration.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
if (this.configuration == null) {
|
||||
afterPropertiesSet();
|
||||
}
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
/**
|
||||
* Thin wrapper to adapt Jackson 2 {@link ObjectMapper} to {@link JsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see JsonParserFactory
|
||||
*/
|
||||
public class JacksonJsonParser implements JsonParser {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> parseMap(String json) {
|
||||
try {
|
||||
return new ObjectMapper().readValue(json, Map.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Cannot parse JSON", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> parseList(String json) {
|
||||
try {
|
||||
return new ObjectMapper().readValue(json, List.class);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Cannot parse JSON", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parser that can read JSON formatted strings into {@link Map}s or {@link List}s.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see JsonParserFactory
|
||||
* @see SimpleJsonParser
|
||||
* @see JacksonJsonParser
|
||||
* @see YamlJsonParser
|
||||
*/
|
||||
public interface JsonParser {
|
||||
|
||||
/**
|
||||
* Parse the specified JSON string into a Map.
|
||||
* @param json the JSON to parse
|
||||
* @return the parsed JSON as a map
|
||||
*/
|
||||
Map<String, Object> parseMap(String json);
|
||||
|
||||
/**
|
||||
* Parse the specified JSON string into a List.
|
||||
* @param json the JSON to parse
|
||||
* @return the parsed JSON as a list
|
||||
*/
|
||||
List<Object> parseList(String json);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link JsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see JacksonJsonParser
|
||||
* @see YamlJsonParser
|
||||
* @see SimpleJsonParser
|
||||
*/
|
||||
public abstract class JsonParserFactory {
|
||||
|
||||
/**
|
||||
* Static factory for the "best" JSON parser available on the classpath. Tries Jackson
|
||||
* (2), then Snake YAML, and then falls back to the {@link SimpleJsonParser}.
|
||||
*
|
||||
* @return a {@link JsonParser}
|
||||
*/
|
||||
public static JsonParser getJsonParser() {
|
||||
if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", null)) {
|
||||
return new JacksonJsonParser();
|
||||
}
|
||||
if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
|
||||
return new YamlJsonParser();
|
||||
}
|
||||
return new SimpleJsonParser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Really basic JSON parser for when you have nothing else available. Comes with some
|
||||
* limitations with respect to the JSON specification (e.g. only supports String values),
|
||||
* so users will probably prefer to have a library handle things instead (Jackson or Snake
|
||||
* YAML are supported).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see JsonParserFactory
|
||||
*/
|
||||
public class SimpleJsonParser implements JsonParser {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parseMap(String json) {
|
||||
if (json.startsWith("{")) {
|
||||
return parseMapInternal(json);
|
||||
}
|
||||
else if (json.trim().equals("")) {
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> parseList(String json) {
|
||||
if (json.startsWith("[")) {
|
||||
return parseListInternal(json);
|
||||
}
|
||||
else if (json.trim().equals("")) {
|
||||
return new ArrayList<Object>();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<Object> parseListInternal(String json) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
json = trimLeadingCharacter(trimTrailingCharacter(json, ']'), '[');
|
||||
;
|
||||
for (String value : tokenize(json)) {
|
||||
list.add(parseInternal(value));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Object parseInternal(String json) {
|
||||
if (json.startsWith("[")) {
|
||||
return parseListInternal(json);
|
||||
}
|
||||
if (json.startsWith("{")) {
|
||||
return parseMapInternal(json);
|
||||
}
|
||||
if (json.startsWith("\"")) {
|
||||
return trimTrailingCharacter(trimLeadingCharacter(json, '"'), '"');
|
||||
}
|
||||
return json; // TODO: numbers maybe?
|
||||
}
|
||||
|
||||
private static String trimTrailingCharacter(String string, char c) {
|
||||
if (string.length() >= 0 && string.charAt(string.length() - 1) == c) {
|
||||
return string.substring(0, string.length() - 1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
private static String trimLeadingCharacter(String string, char c) {
|
||||
if (string.length() >= 0 && string.charAt(0) == c) {
|
||||
return string.substring(1);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
private Map<String, Object> parseMapInternal(String json) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
json = trimLeadingCharacter(trimTrailingCharacter(json, '}'), '{');
|
||||
for (String pair : tokenize(json)) {
|
||||
String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":"));
|
||||
String key = trimLeadingCharacter(trimTrailingCharacter(values[0], '"'), '"');
|
||||
Object value = null;
|
||||
if (values.length > 0) {
|
||||
String string = trimLeadingCharacter(
|
||||
trimTrailingCharacter(values[1], '"'), '"');
|
||||
if (string.startsWith("{") && string.endsWith("}")) {
|
||||
value = parseInternal(string);
|
||||
}
|
||||
else {
|
||||
value = string;
|
||||
}
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<String> tokenize(String json) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
int index = 0;
|
||||
int inObject = 0;
|
||||
StringBuilder build = new StringBuilder();
|
||||
while (index < json.length()) {
|
||||
char current = json.charAt(index);
|
||||
if (current == '{') {
|
||||
inObject++;
|
||||
}
|
||||
if (current == '}') {
|
||||
inObject--;
|
||||
}
|
||||
if (current == ',' && inObject == 0) {
|
||||
list.add(build.toString());
|
||||
build.setLength(0);
|
||||
}
|
||||
else {
|
||||
build.append(current);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
if (build.length() > 0) {
|
||||
list.add(build.toString());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Thin wrapper to adapt Snake {@link Yaml} to {@link JsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see JsonParserFactory
|
||||
*/
|
||||
public class YamlJsonParser implements JsonParser {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, Object> parseMap(String json) {
|
||||
return new Yaml().loadAs(json, Map.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Object> parseList(String json) {
|
||||
return new Yaml().loadAs(json, List.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.config;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* Factory for Map that reads from a YAML source. YAML is a nice human-readable format for
|
||||
* configuration, and it has some useful hierarchical properties. It's more or less a
|
||||
* superset of JSON, so it has a lot of similar features. If multiple resources are
|
||||
* provided the later ones will override entries in the earlier ones hierarchically - that
|
||||
* is all entries with the same nested key of type Map at any depth are merged. For
|
||||
* example:
|
||||
*
|
||||
* <pre>
|
||||
* foo:
|
||||
* bar:
|
||||
* one: two
|
||||
* three: four
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* plus (later in the list)
|
||||
*
|
||||
* <pre>
|
||||
* foo:
|
||||
* bar:
|
||||
* one: 2
|
||||
* five: six
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* results in an effecive input of
|
||||
*
|
||||
* <pre>
|
||||
* foo:
|
||||
* bar:
|
||||
* one: 2
|
||||
* three: four
|
||||
* five: six
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* Note that the value of "foo" in the first document is not simply replaced with the
|
||||
* value in the second, but its nested values are merged.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlMapFactoryBean extends YamlProcessor implements
|
||||
FactoryBean<Map<String, Object>> {
|
||||
|
||||
private boolean singleton = true;
|
||||
|
||||
private Map<String, Object> instance;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getObject() {
|
||||
if (!this.singleton || this.instance == null) {
|
||||
final Map<String, Object> result = new LinkedHashMap<String, Object>();
|
||||
MatchCallback callback = new MatchCallback() {
|
||||
@Override
|
||||
public void process(Properties properties, Map<String, Object> map) {
|
||||
merge(result, map);
|
||||
}
|
||||
};
|
||||
process(callback);
|
||||
this.instance = result;
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void merge(Map<String, Object> output, Map<String, Object> map) {
|
||||
for (Entry<String, Object> entry : map.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
Object existing = output.get(key);
|
||||
if (value instanceof Map && existing instanceof Map) {
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(
|
||||
(Map) existing);
|
||||
merge(result, (Map) value);
|
||||
output.put(key, result);
|
||||
}
|
||||
else {
|
||||
output.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Map.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if a singleton should be created, or a new object on each request otherwise.
|
||||
* Default is <code>true</code> (a singleton).
|
||||
*/
|
||||
public void setSingleton(boolean singleton) {
|
||||
this.singleton = singleton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return this.singleton;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Base class for Yaml factories.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlProcessor {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private ResolutionMethod resolutionMethod = ResolutionMethod.OVERRIDE;
|
||||
|
||||
private Resource[] resources = new Resource[0];
|
||||
|
||||
private List<DocumentMatcher> documentMatchers = Collections.emptyList();
|
||||
|
||||
private boolean matchDefault = true;
|
||||
|
||||
/**
|
||||
* A map of document matchers allowing callers to selectively use only some of the
|
||||
* documents in a YAML resource. In YAML documents are separated by
|
||||
* <code>---<code> lines, and each document is converted to properties before the match is made. E.g.
|
||||
*
|
||||
* <pre>
|
||||
* environment: dev
|
||||
* url: http://dev.bar.com
|
||||
* name: Developer Setup
|
||||
* ---
|
||||
* environment: prod
|
||||
* url:http://foo.bar.com
|
||||
* name: My Cool App
|
||||
* </pre>
|
||||
*
|
||||
* when mapped with <code>documentMatchers = YamlProcessor.mapMatcher({"environment": "prod"})</code>
|
||||
* would end up as
|
||||
*
|
||||
* <pre>
|
||||
* environment=prod
|
||||
* url=http://foo.bar.com
|
||||
* name=My Cool App
|
||||
* url=http://dev.bar.com
|
||||
* </pre>
|
||||
*
|
||||
* @param matchers a map of keys to value patterns (regular expressions)
|
||||
*/
|
||||
public void setDocumentMatchers(List<? extends DocumentMatcher> matchers) {
|
||||
this.documentMatchers = Collections.unmodifiableList(matchers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag indicating that a document for which all the
|
||||
* {@link #setDocumentMatchers(List) document matchers} abstain will nevertheless
|
||||
* match.
|
||||
*
|
||||
* @param matchDefault the flag to set (default true)
|
||||
*/
|
||||
public void setMatchDefault(boolean matchDefault) {
|
||||
this.matchDefault = matchDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to use for resolving resources. Each resource will be converted to a Map, so
|
||||
* this property is used to decide which map entries to keep in the final output from
|
||||
* this factory. Possible values:
|
||||
* <ul>
|
||||
* <li><code>OVERRIDE</code> for replacing values from earlier in the list</li>
|
||||
* <li><code>FIRST_FOUND</code> if you want to take the first resource in the list
|
||||
* that exists and use just that.</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @param resolutionMethod the resolution method to set. Defaults to OVERRIDE.
|
||||
*/
|
||||
public void setResolutionMethod(ResolutionMethod resolutionMethod) {
|
||||
this.resolutionMethod = resolutionMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resources the resources to set
|
||||
*/
|
||||
public void setResources(Resource[] resources) {
|
||||
this.resources = (resources == null ? null : resources.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity for subclasses to process the Yaml parsed from the supplied
|
||||
* resources. Each resource is parsed in turn and the documents inside checked against
|
||||
* the {@link #setDocumentMatchers(List) matchers}. If a document matches it is passed
|
||||
* into the callback, along with its representation as Properties. Depending on the
|
||||
* {@link #setResolutionMethod(ResolutionMethod)} not all of the documents will be
|
||||
* parsed.
|
||||
*
|
||||
* @param callback a callback to delegate to once matching documents are found
|
||||
*/
|
||||
protected void process(MatchCallback callback) {
|
||||
Yaml yaml = new Yaml();
|
||||
for (Resource resource : this.resources) {
|
||||
boolean found = process(callback, yaml, resource);
|
||||
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND && found) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
|
||||
int count = 0;
|
||||
try {
|
||||
this.logger.info("Loading from YAML: " + resource);
|
||||
for (Object object : yaml.loadAll(resource.getInputStream())) {
|
||||
if (object != null && process(asMap(object), callback)) {
|
||||
count++;
|
||||
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.logger.info("Loaded " + count + " document" + (count > 1 ? "s" : "")
|
||||
+ " from YAML resource: " + resource);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
handleProcessError(resource, ex);
|
||||
}
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
private void handleProcessError(Resource resource, IOException ex) {
|
||||
if (this.resolutionMethod != ResolutionMethod.FIRST_FOUND
|
||||
&& this.resolutionMethod != ResolutionMethod.OVERRIDE_AND_IGNORE) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn("Could not load map from " + resource + ": "
|
||||
+ ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> asMap(Object object) {
|
||||
return (Map<String, Object>) object;
|
||||
}
|
||||
|
||||
private boolean process(Map<String, Object> map, MatchCallback callback) {
|
||||
Properties properties = new Properties();
|
||||
assignProperties(properties, map, null);
|
||||
if (this.documentMatchers.isEmpty()) {
|
||||
this.logger.debug("Merging document (no matchers set)" + map);
|
||||
callback.process(properties, map);
|
||||
}
|
||||
else {
|
||||
boolean valueFound = false;
|
||||
MatchStatus result = MatchStatus.ABSTAIN;
|
||||
for (DocumentMatcher matcher : this.documentMatchers) {
|
||||
MatchStatus match = matcher.matches(properties);
|
||||
result = match.ordinal() < result.ordinal() ? match : result;
|
||||
if (match == MatchStatus.FOUND) {
|
||||
this.logger.debug("Matched document with document matcher: "
|
||||
+ properties);
|
||||
callback.process(properties, map);
|
||||
valueFound = true;
|
||||
// No need to check for more matches
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == MatchStatus.ABSTAIN && this.matchDefault) {
|
||||
this.logger.debug("Matched document with default matcher: " + map);
|
||||
callback.process(properties, map);
|
||||
}
|
||||
else if (!valueFound) {
|
||||
this.logger.debug("Unmatched document");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void assignProperties(Properties properties, Map<String, Object> input,
|
||||
String path) {
|
||||
for (Entry<String, Object> entry : input.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (StringUtils.hasText(path)) {
|
||||
if (key.startsWith("[")) {
|
||||
key = path + key;
|
||||
}
|
||||
else {
|
||||
key = path + "." + key;
|
||||
}
|
||||
}
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof String) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
else if (value instanceof Map) {
|
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
assignProperties(properties, map, key);
|
||||
}
|
||||
else if (value instanceof Collection) {
|
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> collection = (Collection<Object>) value;
|
||||
properties.put(key,
|
||||
StringUtils.collectionToCommaDelimitedString(collection));
|
||||
int count = 0;
|
||||
for (Object object : collection) {
|
||||
assignProperties(properties,
|
||||
Collections.singletonMap("[" + (count++) + "]", object), key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
properties.put(key, value == null ? "" : value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface MatchCallback {
|
||||
void process(Properties properties, Map<String, Object> map);
|
||||
}
|
||||
|
||||
public interface DocumentMatcher {
|
||||
MatchStatus matches(Properties properties);
|
||||
}
|
||||
|
||||
public static enum ResolutionMethod {
|
||||
OVERRIDE, OVERRIDE_AND_IGNORE, FIRST_FOUND
|
||||
}
|
||||
|
||||
public static enum MatchStatus {
|
||||
FOUND, NOT_FOUND, ABSTAIN
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a document containing a given key and where the value of that key is an
|
||||
* array containing one of the given values, or where one of the values matches one of
|
||||
* the given values (interpreted as regexes).
|
||||
*/
|
||||
public static class ArrayDocumentMatcher implements DocumentMatcher {
|
||||
|
||||
private String key;
|
||||
|
||||
private String[] patterns;
|
||||
|
||||
public ArrayDocumentMatcher(final String key, final String... patterns) {
|
||||
this.key = key;
|
||||
this.patterns = patterns;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
if (!properties.containsKey(this.key)) {
|
||||
return MatchStatus.ABSTAIN;
|
||||
}
|
||||
Set<String> values = StringUtils.commaDelimitedListToSet(properties
|
||||
.getProperty(this.key));
|
||||
for (String pattern : this.patterns) {
|
||||
for (String value : values) {
|
||||
if (value.matches(pattern)) {
|
||||
return MatchStatus.FOUND;
|
||||
}
|
||||
}
|
||||
}
|
||||
return MatchStatus.NOT_FOUND;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.config;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
||||
/**
|
||||
* Factory for Java Properties that reads from a YAML source. YAML is a nice
|
||||
* human-readable format for configuration, and it has some useful hierarchical
|
||||
* properties. It's more or less a superset of JSON, so it has a lot of similar features.
|
||||
* The Properties created by this factory have nested paths for hierarchical objects, so
|
||||
* for instance this YAML
|
||||
*
|
||||
* <pre>
|
||||
* environments:
|
||||
* dev:
|
||||
* url: http://dev.bar.com
|
||||
* name: Developer Setup
|
||||
* prod:
|
||||
* url: http://foo.bar.com
|
||||
* name: My Cool App
|
||||
* </pre>
|
||||
*
|
||||
* is transformed into these Properties:
|
||||
*
|
||||
* <pre>
|
||||
* environments.dev.url=http://dev.bar.com
|
||||
* environments.dev.name=Developer Setup
|
||||
* environments.prod.url=http://foo.bar.com
|
||||
* environments.prod.name=My Cool App
|
||||
* </pre>
|
||||
*
|
||||
* Lists are represented as comma-separated values (useful for simple String values) and
|
||||
* also as property keys with <code>[]</code> dereferencers, for example this YAML:
|
||||
*
|
||||
* <pre>
|
||||
* servers:
|
||||
* - dev.bar.com
|
||||
* - foo.bar.com
|
||||
* </pre>
|
||||
*
|
||||
* becomes java Properties like this:
|
||||
*
|
||||
* <pre>
|
||||
* servers=dev.bar.com,foo.bar.com
|
||||
* servers[0]=dev.bar.com
|
||||
* servers[1]=foo.bar.com
|
||||
* </pre>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlPropertiesFactoryBean extends YamlProcessor implements
|
||||
FactoryBean<Properties> {
|
||||
|
||||
private boolean singleton = true;
|
||||
|
||||
private Properties instance;
|
||||
|
||||
@Override
|
||||
public Properties getObject() {
|
||||
if (!this.singleton || this.instance == null) {
|
||||
final Properties result = new Properties();
|
||||
MatchCallback callback = new MatchCallback() {
|
||||
@Override
|
||||
public void process(Properties properties, Map<String, Object> map) {
|
||||
result.putAll(properties);
|
||||
}
|
||||
};
|
||||
process(callback);
|
||||
this.instance = result;
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return Properties.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if a singleton should be created, or a new object on each request otherwise.
|
||||
* Default is <code>true</code> (a singleton).
|
||||
*/
|
||||
public void setSingleton(boolean singleton) {
|
||||
this.singleton = singleton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return this.singleton;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
|
||||
/**
|
||||
* Base for {@link OnBeanCondition} and {@link OnMissingBeanCondition}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
abstract class AbstractOnBeanCondition implements ConfigurationCondition {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected abstract Class<?> annotationClass();
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return ConfigurationPhase.REGISTER_BEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
annotationClass().getName(), true);
|
||||
final List<String> beanClasses = collect(attributes, "value");
|
||||
final List<String> beanNames = collect(attributes, "name");
|
||||
|
||||
if (beanClasses.size() == 0) {
|
||||
if (metadata instanceof MethodMetadata
|
||||
&& metadata.isAnnotated(Bean.class.getName())) {
|
||||
try {
|
||||
final MethodMetadata methodMetadata = (MethodMetadata) metadata;
|
||||
// We should be safe to load at this point since we are in the
|
||||
// REGISTER_BEAN phase
|
||||
Class<?> configClass = ClassUtils.forName(
|
||||
methodMetadata.getDeclaringClassName(),
|
||||
context.getClassLoader());
|
||||
ReflectionUtils.doWithMethods(configClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
if (methodMetadata.getMethodName().equals(method.getName())) {
|
||||
beanClasses.add(method.getReturnType().getName());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.isTrue(beanClasses.size() > 0 || beanNames.size() > 0,
|
||||
"@" + ClassUtils.getShortName(annotationClass())
|
||||
+ " annotations must specify at least one bean");
|
||||
|
||||
return matches(context, metadata, beanClasses, beanNames);
|
||||
}
|
||||
|
||||
protected boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata,
|
||||
List<String> beanClasses, List<String> beanNames) throws LinkageError {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(this.logger, metadata);
|
||||
|
||||
Boolean considerHierarchy = (Boolean) metadata.getAnnotationAttributes(
|
||||
annotationClass().getName()).get("considerHierarchy");
|
||||
considerHierarchy = (considerHierarchy == null ? false : considerHierarchy);
|
||||
|
||||
List<String> beanClassesFound = new ArrayList<String>();
|
||||
List<String> beanNamesFound = new ArrayList<String>();
|
||||
|
||||
for (String beanClass : beanClasses) {
|
||||
try {
|
||||
// eagerInit set to false to prevent early instantiation (some
|
||||
// factory beans will not be able to determine their object type at this
|
||||
// stage, so those are not eligible for matching this condition)
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
Class<?> type = ClassUtils.forName(beanClass, context.getClassLoader());
|
||||
String[] beans = (considerHierarchy ? BeanFactoryUtils
|
||||
.beanNamesForTypeIncludingAncestors(beanFactory, type, false,
|
||||
false) : beanFactory.getBeanNamesForType(type, false,
|
||||
false));
|
||||
if (beans.length != 0) {
|
||||
beanClassesFound.add(beanClass);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
}
|
||||
for (String beanName : beanNames) {
|
||||
if (considerHierarchy ? context.getBeanFactory().containsBean(beanName)
|
||||
: context.getBeanFactory().containsLocalBean(beanName)) {
|
||||
beanNamesFound.add(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
boolean result = evaluate(beanClassesFound, beanNamesFound);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
logFoundResults(checking, "class", beanClasses, beanClassesFound);
|
||||
logFoundResults(checking, "name", beanNames, beanClassesFound);
|
||||
this.logger.debug(checking + "Match result is: " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void logFoundResults(String prefix, String type, List<?> candidates,
|
||||
List<?> found) {
|
||||
if (!candidates.isEmpty()) {
|
||||
this.logger.debug(prefix + "Looking for beans with " + type + ": "
|
||||
+ candidates);
|
||||
if (found.isEmpty()) {
|
||||
this.logger.debug(prefix + "Found no beans");
|
||||
}
|
||||
else {
|
||||
this.logger.debug(prefix + "Found beans with " + type + ": " + found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean evaluate(List<String> beanClassesFound, List<String> beanNamesFound) {
|
||||
return !beanClassesFound.isEmpty() || !beanNamesFound.isEmpty();
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private List<String> collect(MultiValueMap<String, Object> attributes, String key) {
|
||||
List<String> collected = new ArrayList<String>();
|
||||
List<String[]> valueList = (List) attributes.get(key);
|
||||
for (String[] valueArray : valueList) {
|
||||
for (String value : valueArray) {
|
||||
collected.add(value);
|
||||
}
|
||||
}
|
||||
return collected;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* General utilities for constructing {@code @Conditional} log messages.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class ConditionLogUtils {
|
||||
|
||||
public static String getPrefix(Log logger, AnnotatedTypeMetadata metadata) {
|
||||
String prefix = "";
|
||||
if (logger.isDebugEnabled()) {
|
||||
prefix = metadata instanceof ClassMetadata ? "Processing "
|
||||
+ ((ClassMetadata) metadata).getClassName() + ". "
|
||||
: (metadata instanceof MethodMetadata ? "Processing "
|
||||
+ getMethodName((MethodMetadata) metadata) + ". " : "");
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
private static String getMethodName(MethodMetadata metadata) {
|
||||
return metadata.getDeclaringClassName() + "#" + metadata.getMethodName();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean classes and/or names are
|
||||
* already contained in the {@link BeanFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnBeanCondition.class)
|
||||
public @interface ConditionalOnBean {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition matches when any of
|
||||
* the classes specified is contained in the {@link ApplicationContext}.
|
||||
* @return the class types of beans to check
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The names of beans to check. The condition matches when any of the bean names
|
||||
* specified is contained in the {@link ApplicationContext}.
|
||||
* @return the name of beans to check
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* If the application context hierarchy (parent contexts) should be considered.
|
||||
*/
|
||||
boolean considerHierarchy() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified classes are on the classpath.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnClassCondition.class)
|
||||
public @interface ConditionalOnClass {
|
||||
|
||||
/**
|
||||
* The classes that must be present. Since this annotation parsed by loading class
|
||||
* bytecode it is safe to specify classes here that may ultimately not be on the
|
||||
* classpath.
|
||||
* @return the classes that must be present
|
||||
*/
|
||||
public Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The classes names that must be present. When possible {@link #value()} should be
|
||||
* used in preference to this property.
|
||||
* @return the class names that must be present.
|
||||
*/
|
||||
public String[] name() default {};
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* Configuration annotation for a conditional element that depends on the value of a SpEL
|
||||
* expression.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Conditional(OnExpressionCondition.class)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface ConditionalOnExpression {
|
||||
|
||||
/**
|
||||
* The SpEL expression.
|
||||
*/
|
||||
String value() default "true";
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean classes and/or names are
|
||||
* not already contained in the {@link BeanFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnMissingBeanCondition.class)
|
||||
public @interface ConditionalOnMissingBean {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition matches when each
|
||||
* class specified is missing in the {@link ApplicationContext}.
|
||||
* @return the class types of beans to check
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The names of beans to check. The condition matches when each bean name specified is
|
||||
* missing in the {@link ApplicationContext}.
|
||||
* @return the name of beans to check
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* If the application context hierarchy (parent contexts) should be considered.
|
||||
*/
|
||||
boolean considerHierarchy() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified classes are on the classpath.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnMissingClassCondition.class)
|
||||
public @interface ConditionalOnMissingClass {
|
||||
|
||||
/**
|
||||
* The classes names that must be absent.
|
||||
* @return the class names that must be absent.
|
||||
*/
|
||||
public String[] value() default {};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the application context is a not a web
|
||||
* application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnNotWebApplicationCondition.class)
|
||||
public @interface ConditionalOnNotWebApplication {
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified resources are on the
|
||||
* classpath.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnResourceCondition.class)
|
||||
public @interface ConditionalOnResource {
|
||||
|
||||
/**
|
||||
* The resources that must be present.
|
||||
* @return the resource paths that must be present.
|
||||
*/
|
||||
public String[] resources() default {};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the application context is a web application
|
||||
* context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnWebApplicationCondition.class)
|
||||
public @interface ConditionalOnWebApplication {
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks that specific beans are present.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnBean
|
||||
*/
|
||||
class OnBeanCondition extends AbstractOnBeanCondition {
|
||||
|
||||
@Override
|
||||
protected Class<?> annotationClass() {
|
||||
return ConditionalOnBean.class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for the specific classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnClass
|
||||
*/
|
||||
class OnClassCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnClassCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnClass.class.getName(), true);
|
||||
if (attributes != null) {
|
||||
List<String> classNames = new ArrayList<String>();
|
||||
collectClassNames(classNames, attributes.get("value"));
|
||||
collectClassNames(classNames, attributes.get("name"));
|
||||
Assert.isTrue(classNames.size() > 0,
|
||||
"@ConditionalOnClass annotations must specify at least one class value");
|
||||
for (String className : classNames) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Looking for class: " + className);
|
||||
}
|
||||
if (!ClassUtils.isPresent(className, context.getClassLoader())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Class not found: " + className
|
||||
+ " (search terminated with matches=false)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Match result is: true");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void collectClassNames(List<String> classNames, List<Object> values) {
|
||||
for (Object value : values) {
|
||||
for (Object valueItem : (Object[]) value) {
|
||||
classNames.add(valueItem instanceof Class<?> ? ((Class<?>) valueItem)
|
||||
.getName() : valueItem.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
|
||||
/**
|
||||
* A Condition that evaluates a SpEL expression.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnExpression
|
||||
*/
|
||||
public class OnExpressionCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnExpressionCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
String value = (String) metadata.getAnnotationAttributes(
|
||||
ConditionalOnExpression.class.getName()).get("value");
|
||||
if (!value.startsWith("#{")) {
|
||||
// For convenience allow user to provide bare expression with no #{} wrapper
|
||||
value = "#{" + value + "}";
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
StringBuilder builder = new StringBuilder(checking)
|
||||
.append("Evaluating expression");
|
||||
if (metadata instanceof ClassMetadata) {
|
||||
builder.append(" on " + ((ClassMetadata) metadata).getClassName());
|
||||
}
|
||||
builder.append(": " + value);
|
||||
logger.debug(builder.toString());
|
||||
}
|
||||
// Explicitly allow environment placeholders inside the expression
|
||||
value = context.getEnvironment().resolvePlaceholders(value);
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
|
||||
BeanExpressionContext expressionContext = (beanFactory != null) ? new BeanExpressionContext(
|
||||
beanFactory, null) : null;
|
||||
if (resolver == null) {
|
||||
resolver = new StandardBeanExpressionResolver();
|
||||
}
|
||||
boolean result = (Boolean) resolver.evaluate(value, expressionContext);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Finished matching and result is matches=" + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks that specific beans are missing.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnMissingBean
|
||||
*/
|
||||
class OnMissingBeanCondition extends AbstractOnBeanCondition {
|
||||
|
||||
@Override
|
||||
protected Class<?> annotationClass() {
|
||||
return ConditionalOnMissingBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean evaluate(List<String> beanClassesFound, List<String> beanNamesFound) {
|
||||
return !super.evaluate(beanClassesFound, beanNamesFound);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for the specific classes.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnMissingClass
|
||||
*/
|
||||
class OnMissingClassCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnMissingClassCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnMissingClass.class.getName(), true);
|
||||
if (attributes != null) {
|
||||
List<String> classNames = new ArrayList<String>();
|
||||
collectClassNames(classNames, attributes.get("value"));
|
||||
Assert.isTrue(classNames.size() > 0,
|
||||
"@ConditionalOnMissingClass annotations must specify at least one class value");
|
||||
for (String className : classNames) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Looking for class: " + className);
|
||||
}
|
||||
if (ClassUtils.isPresent(className, context.getClassLoader())) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Found class: " + className
|
||||
+ " (search terminated with matches=false)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Match result is: true");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void collectClassNames(List<String> classNames, List<Object> values) {
|
||||
for (Object value : values) {
|
||||
for (Object valueItem : (Object[]) value) {
|
||||
classNames.add(valueItem instanceof Class<?> ? ((Class<?>) valueItem)
|
||||
.getName() : valueItem.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME merge with OnClassCondition
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a web application context and returns false if one is
|
||||
* found.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnNotWebApplication
|
||||
*/
|
||||
class OnNotWebApplicationCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnNotWebApplicationCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.springframework.web.context.support.GenericWebApplicationContext",
|
||||
null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Web application classes not found");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
boolean result = !StringUtils.arrayToCommaDelimitedString(
|
||||
context.getBeanFactory().getRegisteredScopeNames()).contains("session");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Web application context found: " + !result);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for specific resources.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnResource
|
||||
*/
|
||||
class OnResourceCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnResourceCondition.class);
|
||||
|
||||
private ResourceLoader defaultResourceLoader = new DefaultResourceLoader();
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnResource.class.getName(), true);
|
||||
ResourceLoader loader = context.getResourceLoader() == null ? this.defaultResourceLoader
|
||||
: context.getResourceLoader();
|
||||
if (attributes != null) {
|
||||
List<String> locations = new ArrayList<String>();
|
||||
collectValues(locations, attributes.get("resources"));
|
||||
Assert.isTrue(locations.size() > 0,
|
||||
"@ConditionalOnResource annotations must specify at least one resource location");
|
||||
for (String location : locations) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Checking for resource: " + location);
|
||||
}
|
||||
if (!loader.getResource(location).exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Resource not found: " + location
|
||||
+ " (search terminated with matches=false)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Match result is: true");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void collectValues(List<String> names, List<Object> values) {
|
||||
for (Object value : values) {
|
||||
for (Object item : (Object[]) value) {
|
||||
names.add((String) item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.support.StandardServletEnvironment;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a web application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnWebApplication
|
||||
*/
|
||||
class OnWebApplicationCondition implements Condition {
|
||||
|
||||
private static Log logger = LogFactory.getLog(OnWebApplicationCondition.class);
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
|
||||
String checking = ConditionLogUtils.getPrefix(logger, metadata);
|
||||
|
||||
if (!ClassUtils.isPresent(
|
||||
"org.springframework.web.context.support.GenericWebApplicationContext",
|
||||
null)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Web application classes not found");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
boolean result = StringUtils.arrayToCommaDelimitedString(
|
||||
context.getBeanFactory().getRegisteredScopeNames()).contains("session")
|
||||
|| context.getEnvironment() instanceof StandardServletEnvironment;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(checking + "Web application context found: " + result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// FIXME merge with OnNotWeb...
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.CodeSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link EmbeddedServletContainerFactory} implementations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class AbstractEmbeddedServletContainerFactory implements
|
||||
ConfigurableEmbeddedServletContainerFactory {
|
||||
|
||||
private static final String[] COMMON_DOC_ROOTS = { "src/main/webapp", "public",
|
||||
"static" };
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private String contextPath = "";
|
||||
|
||||
private boolean registerDefaultServlet = true;
|
||||
|
||||
private boolean registerJspServlet = true;
|
||||
|
||||
private String jspServletClassName = "org.apache.jasper.servlet.JspServlet";
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
private List<ServletContextInitializer> initializers = new ArrayList<ServletContextInitializer>();
|
||||
|
||||
private File documentRoot;
|
||||
|
||||
private Set<ErrorPage> errorPages = new LinkedHashSet<ErrorPage>();
|
||||
|
||||
private InetAddress address;
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractEmbeddedServletContainerFactory} instance.
|
||||
*/
|
||||
public AbstractEmbeddedServletContainerFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractEmbeddedServletContainerFactory} instance with the
|
||||
* specified port.
|
||||
* @param port the port number for the embedded servlet container
|
||||
*/
|
||||
public AbstractEmbeddedServletContainerFactory(int port) {
|
||||
checkPort(port);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AbstractEmbeddedServletContainerFactory} instance with the
|
||||
* specified context path and port.
|
||||
* @param contextPath the context path for the embedded servlet container
|
||||
* @param port the port number for the embedded servlet container
|
||||
*/
|
||||
public AbstractEmbeddedServletContainerFactory(String contextPath, int port) {
|
||||
checkContextPath(contextPath);
|
||||
checkPort(port);
|
||||
this.contextPath = contextPath;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context path for the embedded servlet container. The context should start
|
||||
* with a "/" character but not end with a "/" character. The default context path can
|
||||
* be specified using an empty string.
|
||||
* @param contextPath the contextPath to set
|
||||
* @see #getContextPath
|
||||
*/
|
||||
@Override
|
||||
public void setContextPath(String contextPath) {
|
||||
checkContextPath(contextPath);
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
private void checkContextPath(String contextPath) {
|
||||
Assert.notNull(contextPath, "ContextPath must not be null");
|
||||
if (contextPath.length() > 0) {
|
||||
if ("/".equals(contextPath)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Root ContextPath must be specified using an empty string");
|
||||
}
|
||||
if (!contextPath.startsWith("/") || contextPath.endsWith("/")) {
|
||||
throw new IllegalArgumentException(
|
||||
"ContextPath must start with '/ and not end with '/'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context path for the embedded servlet container. The path will start
|
||||
* with "/" and not end with "/". The root context is represented by an empty string.
|
||||
*/
|
||||
@Override
|
||||
public String getContextPath() {
|
||||
return this.contextPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port that the embedded servlet container should listen on. If not
|
||||
* specified port '8080' will be used. Use port 0 to switch off the server completely.
|
||||
*
|
||||
* @param port the port to set
|
||||
*/
|
||||
@Override
|
||||
public void setPort(int port) {
|
||||
checkPort(port);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
private void checkPort(int port) {
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("Port must be between 1 and 65535");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port that the embedded servlet container should listen on.
|
||||
*/
|
||||
@Override
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you need the server to bind to a specific network address, provide one here.
|
||||
*
|
||||
* @param address the address to set (defaults to null)
|
||||
*/
|
||||
@Override
|
||||
public void setAddress(InetAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the address the embedded container binds to
|
||||
*/
|
||||
@Override
|
||||
public InetAddress getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link ServletContextInitializer} that should be applied in addition to
|
||||
* {@link #getEmbeddedServletContainer(ServletContextInitializer...)} parameters. This
|
||||
* method will replace any previously set or added initializers.
|
||||
* @param initializers the initializers to set
|
||||
* @see #addInitializers
|
||||
* @see #getInitializers
|
||||
*/
|
||||
@Override
|
||||
public void setInitializers(List<? extends ServletContextInitializer> initializers) {
|
||||
Assert.notNull(initializers, "Initializers must not be null");
|
||||
this.initializers = new ArrayList<ServletContextInitializer>(initializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link ServletContextInitializer}s to those that should be applied in addition
|
||||
* to {@link #getEmbeddedServletContainer(ServletContextInitializer...)} parameters.
|
||||
* @param initializers the initializers to add
|
||||
* @see #setInitializers
|
||||
* @see #getInitializers
|
||||
*/
|
||||
@Override
|
||||
public void addInitializers(ServletContextInitializer... initializers) {
|
||||
Assert.notNull(initializers, "Initializers must not be null");
|
||||
this.initializers.addAll(Arrays.asList(initializers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable list of {@link ServletContextInitializer} that should be applied
|
||||
* in addition to {@link #getEmbeddedServletContainer(ServletContextInitializer...)}
|
||||
* parameters.
|
||||
* @return the initializers
|
||||
*/
|
||||
@Override
|
||||
public List<ServletContextInitializer> getInitializers() {
|
||||
return this.initializers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the document root folder which will be used by the web context to serve static
|
||||
* files.
|
||||
* @param documentRoot the document root or {@code null} if not required
|
||||
*/
|
||||
@Override
|
||||
public void setDocumentRoot(File documentRoot) {
|
||||
this.documentRoot = documentRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the document root which will be used by the web context to serve static
|
||||
* files.
|
||||
*/
|
||||
@Override
|
||||
public File getDocumentRoot() {
|
||||
return this.documentRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the error pages that will be used when handling exceptions.
|
||||
* @param errorPages the error pages
|
||||
*/
|
||||
@Override
|
||||
public void setErrorPages(Set<ErrorPage> errorPages) {
|
||||
Assert.notNull(errorPages, "ErrorPages must not be null");
|
||||
this.errorPages = new LinkedHashSet<ErrorPage>(errorPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds error pages that will be used when handling exceptions.
|
||||
* @param errorPages the error pages
|
||||
*/
|
||||
@Override
|
||||
public void addErrorPages(ErrorPage... errorPages) {
|
||||
Assert.notNull(this.initializers, "ErrorPages must not be null");
|
||||
this.errorPages.addAll(Arrays.asList(errorPages));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable set of {@link ErrorPage}s that will be used when handling
|
||||
* exceptions.
|
||||
*/
|
||||
@Override
|
||||
public Set<ErrorPage> getErrorPages() {
|
||||
return this.errorPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the DefaultServlet should be registered. Defaults to {@code true} so that
|
||||
* files from the {@link #setDocumentRoot(File) document root} will be served.
|
||||
* @param registerDefaultServlet if the default servlet should be registered
|
||||
*/
|
||||
@Override
|
||||
public void setRegisterDefaultServlet(boolean registerDefaultServlet) {
|
||||
this.registerDefaultServlet = registerDefaultServlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that the JSP servlet should be registered if available on the
|
||||
* classpath.
|
||||
*
|
||||
* @return true if the JSP servlet is to be registered
|
||||
*/
|
||||
@Override
|
||||
public boolean isRegisterJspServlet() {
|
||||
return this.registerJspServlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the JspServlet should be registered if it is on the classpath. Defaults to
|
||||
* {@code true} so that files from the {@link #setDocumentRoot(File) document root}
|
||||
* will be served.
|
||||
* @param registerJspServlet if the JSP servlet should be registered
|
||||
*/
|
||||
@Override
|
||||
public void setRegisterJspServlet(boolean registerJspServlet) {
|
||||
this.registerJspServlet = registerJspServlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate that the default servlet should be registered.
|
||||
*
|
||||
* @return true if the default servlet is to be registered
|
||||
*/
|
||||
@Override
|
||||
public boolean isRegisterDefaultServlet() {
|
||||
return this.registerDefaultServlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* The class name for the jsp servlet if used. If
|
||||
* {@link #setRegisterJspServlet(boolean) <code>registerJspServlet</code>} is true
|
||||
* <b>and</b> this class is on the classpath then it will be registered. Since both
|
||||
* Tomcat and Jetty use Jasper for their JSP implementation the default is
|
||||
* <code>org.apache.jasper.servlet.JspServlet</code>.
|
||||
*
|
||||
* @param jspServletClassName the class name for the JSP servlet if used
|
||||
*/
|
||||
@Override
|
||||
public void setJspServletClassName(String jspServletClassName) {
|
||||
this.jspServletClassName = jspServletClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the JSP servlet class name
|
||||
*/
|
||||
protected String getJspServletClassName() {
|
||||
return this.jspServletClassName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that can be used by subclasses wishing to combine the specified
|
||||
* {@link ServletContextInitializer} parameters with those defined in this instance.
|
||||
* @param initializers the initializers to merge
|
||||
* @return a complete set of merged initializers (with the specified parameters
|
||||
* appearing first)
|
||||
*/
|
||||
protected final ServletContextInitializer[] mergeInitializers(
|
||||
ServletContextInitializer... initializers) {
|
||||
List<ServletContextInitializer> mergedInitializers = new ArrayList<ServletContextInitializer>();
|
||||
mergedInitializers.addAll(Arrays.asList(initializers));
|
||||
mergedInitializers.addAll(getInitializers());
|
||||
return mergedInitializers
|
||||
.toArray(new ServletContextInitializer[mergedInitializers.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute document root when it points to a valid folder, logging a
|
||||
* warning and returning {@code null} otherwise.
|
||||
*/
|
||||
protected final File getValidDocumentRoot() {
|
||||
File file = getDocumentRoot();
|
||||
file = file != null ? file : getWarFileDocumentRoot();
|
||||
file = file != null ? file : getCommonDocumentRoot();
|
||||
if (file == null && this.logger.isWarnEnabled()) {
|
||||
this.logger.warn("None of the document roots "
|
||||
+ Arrays.asList(COMMON_DOC_ROOTS)
|
||||
+ " point to a directory and will be ignored.");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private File getWarFileDocumentRoot() {
|
||||
File warFile = getCodeSourceArchive();
|
||||
if (warFile.exists() && !warFile.isDirectory()
|
||||
&& warFile.getName().toLowerCase().endsWith(".war")) {
|
||||
return warFile.getAbsoluteFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getCommonDocumentRoot() {
|
||||
for (String commonDocRoot : COMMON_DOC_ROOTS) {
|
||||
File root = new File(commonDocRoot);
|
||||
if (root != null && root.exists() && root.isDirectory()) {
|
||||
return root.getAbsoluteFile();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private File getCodeSourceArchive() {
|
||||
try {
|
||||
CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
|
||||
URL location = (codeSource == null ? null : codeSource.getLocation());
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
String path = location.getPath();
|
||||
URLConnection connection = location.openConnection();
|
||||
if (connection instanceof JarURLConnection) {
|
||||
path = ((JarURLConnection) connection).getJarFile().getName();
|
||||
}
|
||||
if (path.indexOf("!/") != -1) {
|
||||
path = path.substring(0, path.indexOf("!/"));
|
||||
}
|
||||
return new File(path);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.AnnotationScopeMetadataResolver;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ScopeMetadataResolver;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedWebApplicationContext} that accepts annotated classes as input - in
|
||||
* particular {@link org.springframework.context.annotation.Configuration
|
||||
* <code>@Configuration</code>}-annotated classes, but also plain
|
||||
* {@link org.springframework.stereotype.Component <code>@Component</code>} classes and
|
||||
* JSR-330 compliant classes using {@code javax.inject} annotations. Allows for
|
||||
* registering classes one by one (specifying class names as config location) as well as
|
||||
* for classpath scanning (specifying base packages as config location).
|
||||
*
|
||||
* <p>
|
||||
* Note: In case of multiple {@code @Configuration} classes, later {@code @Bean}
|
||||
* definitions will override ones defined in earlier loaded files. This can be leveraged
|
||||
* to deliberately override certain bean definitions via an extra Configuration class.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #register(Class...)
|
||||
* @see #scan(String...)
|
||||
* @see EmbeddedWebApplicationContext
|
||||
* @see AnnotationConfigWebApplicationContext
|
||||
*/
|
||||
public class AnnotationConfigEmbeddedWebApplicationContext extends
|
||||
EmbeddedWebApplicationContext {
|
||||
|
||||
private final AnnotatedBeanDefinitionReader reader;
|
||||
|
||||
private final ClassPathBeanDefinitionScanner scanner;
|
||||
|
||||
private Class<?>[] annotatedClasses;
|
||||
|
||||
private String[] basePackages;
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigEmbeddedWebApplicationContext} that needs to be
|
||||
* populated through {@link #register} calls and then manually {@linkplain #refresh
|
||||
* refreshed}.
|
||||
*/
|
||||
public AnnotationConfigEmbeddedWebApplicationContext() {
|
||||
this.reader = new AnnotatedBeanDefinitionReader(this);
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigEmbeddedWebApplicationContext}, deriving bean
|
||||
* definitions from the given annotated classes and automatically refreshing the
|
||||
* context.
|
||||
* @param annotatedClasses one or more annotated classes, e.g. {@link Configuration
|
||||
* <code>@Configuration</code>} classes
|
||||
*/
|
||||
public AnnotationConfigEmbeddedWebApplicationContext(Class<?>... annotatedClasses) {
|
||||
this();
|
||||
register(annotatedClasses);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link AnnotationConfigEmbeddedWebApplicationContext}, scanning for
|
||||
* bean definitions in the given packages and automatically refreshing the context.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
*/
|
||||
public AnnotationConfigEmbeddedWebApplicationContext(String... basePackages) {
|
||||
this();
|
||||
scan(basePackages);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader} and
|
||||
* {@link ClassPathBeanDefinitionScanner} members.
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
super.setEnvironment(environment);
|
||||
this.reader.setEnvironment(environment);
|
||||
this.scanner.setEnvironment(environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom {@link BeanNameGenerator} for use with
|
||||
* {@link AnnotatedBeanDefinitionReader} and/or {@link ClassPathBeanDefinitionScanner}
|
||||
* , if any.
|
||||
* <p>
|
||||
* Default is
|
||||
* {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
|
||||
* <p>
|
||||
* Any call to this method must occur prior to calls to {@link #register(Class...)}
|
||||
* and/or {@link #scan(String...)}.
|
||||
* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
|
||||
* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.reader.setBeanNameGenerator(beanNameGenerator);
|
||||
this.scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
this.getBeanFactory().registerSingleton(
|
||||
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
|
||||
beanNameGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ScopeMetadataResolver} to use for detected bean classes.
|
||||
* <p>
|
||||
* The default is an {@link AnnotationScopeMetadataResolver}.
|
||||
* <p>
|
||||
* Any call to this method must occur prior to calls to {@link #register(Class...)}
|
||||
* and/or {@link #scan(String...)}.
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.reader.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register one or more annotated classes to be processed. Note that
|
||||
* {@link #refresh()} must be called in order for the context to fully process the new
|
||||
* class.
|
||||
* <p>
|
||||
* Calls to {@link #register} are idempotent; adding the same annotated class more
|
||||
* than once has no additional effect.
|
||||
* @param annotatedClasses one or more annotated classes, e.g. {@link Configuration
|
||||
* <code>@Configuration</code>} classes
|
||||
* @see #scan(String...)
|
||||
* @see #refresh()
|
||||
*/
|
||||
public final void register(Class<?>... annotatedClasses) {
|
||||
this.annotatedClasses = annotatedClasses;
|
||||
Assert.notEmpty(annotatedClasses,
|
||||
"At least one annotated class must be specified");
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a scan within the specified base packages. Note that {@link #refresh()}
|
||||
* must be called in order for the context to fully process the new class.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @see #register(Class...)
|
||||
* @see #refresh()
|
||||
*/
|
||||
public final void scan(String... basePackages) {
|
||||
this.basePackages = basePackages;
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareRefresh() {
|
||||
this.scanner.clearCache();
|
||||
super.prepareRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
super.postProcessBeanFactory(beanFactory);
|
||||
if (this.basePackages != null && this.basePackages.length > 0) {
|
||||
this.scanner.scan(this.basePackages);
|
||||
}
|
||||
if (this.annotatedClasses != null && this.annotatedClasses.length > 0) {
|
||||
this.reader.register(this.annotatedClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void refresh() throws BeansException, IllegalStateException {
|
||||
super.refresh();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple interface that represents customizations to an
|
||||
* {@link EmbeddedServletContainerFactory}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see EmbeddedServletContainerFactory
|
||||
*/
|
||||
public interface ConfigurableEmbeddedServletContainerFactory extends
|
||||
EmbeddedServletContainerFactory {
|
||||
|
||||
void setContextPath(String contextPath);
|
||||
|
||||
String getContextPath();
|
||||
|
||||
void setPort(int port);
|
||||
|
||||
int getPort();
|
||||
|
||||
void setAddress(InetAddress address);
|
||||
|
||||
InetAddress getAddress();
|
||||
|
||||
void setInitializers(List<? extends ServletContextInitializer> initializers);
|
||||
|
||||
void setJspServletClassName(String jspServletClassName);
|
||||
|
||||
boolean isRegisterDefaultServlet();
|
||||
|
||||
void setRegisterJspServlet(boolean registerJspServlet);
|
||||
|
||||
boolean isRegisterJspServlet();
|
||||
|
||||
void setRegisterDefaultServlet(boolean registerDefaultServlet);
|
||||
|
||||
Set<ErrorPage> getErrorPages();
|
||||
|
||||
void addErrorPages(ErrorPage... errorPages);
|
||||
|
||||
void setErrorPages(Set<ErrorPage> errorPages);
|
||||
|
||||
File getDocumentRoot();
|
||||
|
||||
void setDocumentRoot(File documentRoot);
|
||||
|
||||
List<ServletContextInitializer> getInitializers();
|
||||
|
||||
void addInitializers(ServletContextInitializer... initializers);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
/**
|
||||
* Simple interface that represents a fully configured embedded servlet container (for
|
||||
* example Tomcat or Jetty). Allows the container to be {@link #stop() stopped}.
|
||||
*
|
||||
* <p>
|
||||
* Instances of this class are usually obtained via a
|
||||
* {@link EmbeddedServletContainerFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see EmbeddedServletContainerFactory
|
||||
*/
|
||||
public interface EmbeddedServletContainer {
|
||||
|
||||
/**
|
||||
* An empty {@link EmbeddedServletContainer} that does nothing.
|
||||
*/
|
||||
public static final EmbeddedServletContainer NONE = new EmbeddedServletContainer() {
|
||||
|
||||
@Override
|
||||
public void stop() throws EmbeddedServletContainerException {
|
||||
// Do nothing
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the embedded servlet container. Calling this method on an already stopped
|
||||
* container has no effect.
|
||||
* @throws EmbeddedServletContainerException of the container cannot be stopped
|
||||
*/
|
||||
void stop() throws EmbeddedServletContainerException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.embedded;
|
||||
|
||||
/**
|
||||
* Strategy interface for customizing auto-configured embedded servlet containers. Any
|
||||
* beans of this type will get a callback with the container factory before the container
|
||||
* itself is started, so you can set the port, address, error pages etc. Beware: will be
|
||||
* called from a BeanPostProcessor (so very early in the ApplicationContext lifecycle), so
|
||||
* it might be safer to lookup dependencies lazily in the enclosing BeanFactory rather
|
||||
* than injecting them with <code>@Autowired</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see EmbeddedServletContainerCustomizerBeanPostProcessor
|
||||
*/
|
||||
public interface EmbeddedServletContainerCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the specified {@link ConfigurableEmbeddedServletContainerFactory}.
|
||||
* @param factory the factory to customize
|
||||
*/
|
||||
void customize(ConfigurableEmbeddedServletContainerFactory factory);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.embedded;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} that apply all {@link EmbeddedServletContainerCustomizer}s
|
||||
* from the bean factory to {@link ConfigurableEmbeddedServletContainerFactory} beans.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class EmbeddedServletContainerCustomizerBeanPostProcessor implements
|
||||
BeanPostProcessor, ApplicationContextAware {
|
||||
|
||||
// FIXME should we register this by default, Javadoc in
|
||||
// EmbeddedServletContainerCustomizer suggests so
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private List<EmbeddedServletContainerCustomizer> customizers;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof ConfigurableEmbeddedServletContainerFactory) {
|
||||
postProcessBeforeInitialization((ConfigurableEmbeddedServletContainerFactory) bean);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void postProcessBeforeInitialization(
|
||||
ConfigurableEmbeddedServletContainerFactory bean) {
|
||||
for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
|
||||
customizer.customize(bean);
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
|
||||
if (this.customizers == null) {
|
||||
// Look up does not include the parent context
|
||||
this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
|
||||
this.applicationContext.getBeansOfType(
|
||||
EmbeddedServletContainerCustomizer.class).values());
|
||||
Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
|
||||
this.customizers = Collections.unmodifiableList(this.customizers);
|
||||
}
|
||||
return this.customizers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
/**
|
||||
* Exceptions thrown by an embedded servlet container.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class EmbeddedServletContainerException extends RuntimeException {
|
||||
|
||||
public EmbeddedServletContainerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import org.springframework.zero.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
|
||||
/**
|
||||
* Factory interface that can be used to create {@link EmbeddedServletContainer}s.
|
||||
* Implementations are encouraged to extends
|
||||
* {@link AbstractEmbeddedServletContainerFactory} when possible.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see EmbeddedServletContainer
|
||||
* @see AbstractEmbeddedServletContainerFactory
|
||||
* @see JettyEmbeddedServletContainerFactory
|
||||
* @see TomcatEmbeddedServletContainerFactory
|
||||
*/
|
||||
public interface EmbeddedServletContainerFactory {
|
||||
|
||||
/**
|
||||
* Gets a new fully configured and started {@link EmbeddedServletContainer}
|
||||
* instance,blocking until the point that clients can connect.
|
||||
* @param initializers {@link ServletContextInitializer}s that should be applied as
|
||||
* the container starts
|
||||
* @return a fully configured and started {@link EmbeddedServletContainer}
|
||||
* @see EmbeddedServletContainer#stop()
|
||||
*/
|
||||
EmbeddedServletContainer getEmbeddedServletContainer(
|
||||
ServletContextInitializer... initializers);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.ContextLoader;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.GenericWebApplicationContext;
|
||||
import org.springframework.web.context.support.ServletContextAwareProcessor;
|
||||
import org.springframework.web.context.support.ServletContextResource;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
|
||||
/**
|
||||
* A {@link WebApplicationContext} that can be used to bootstrap itself from a contained
|
||||
* {@link EmbeddedServletContainerFactory} bean.
|
||||
*
|
||||
* <p>
|
||||
* This context will create, initialize and run an {@link EmbeddedServletContainer} by
|
||||
* searching for a single {@link EmbeddedServletContainerFactory} bean within the
|
||||
* {@link ApplicationContext} itself. The {@link EmbeddedServletContainerFactory} is free
|
||||
* to use standard Spring concepts (such as dependency injection, lifecycle callbacks and
|
||||
* property placeholder variables).
|
||||
*
|
||||
* <p>
|
||||
* In addition, any {@link Servlet} or {@link Filter} beans defined in the context will be
|
||||
* automatically registered with the embedded Servlet container. In the case of a single
|
||||
* Servlet bean, the '/*' mapping will be used. If multiple Servlet beans are found then
|
||||
* the lowercase bean name will be used as a mapping prefix. Filter beans will be mapped
|
||||
* to all URLs ('/*').
|
||||
*
|
||||
* <p>
|
||||
* For more advanced configuration, the context can instead define beans that implement
|
||||
* the {@link ServletContextInitializer} interface (most often
|
||||
* {@link ServletRegistrationBean}s and/or {@link FilterRegistrationBean}s). To prevent
|
||||
* double registration, the use of {@link ServletContextInitializer} beans will disable
|
||||
* automatic Servlet and Filter bean registration.
|
||||
*
|
||||
* <p>
|
||||
* Although this context can be used directly, most developers should consider using the
|
||||
* {@link AnnotationConfigEmbeddedWebApplicationContext} or
|
||||
* {@link XmlEmbeddedWebApplicationContext} variants.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see AnnotationConfigEmbeddedWebApplicationContext
|
||||
* @see XmlEmbeddedWebApplicationContext
|
||||
* @see EmbeddedServletContainerFactory
|
||||
*/
|
||||
public class EmbeddedWebApplicationContext extends GenericWebApplicationContext {
|
||||
|
||||
/**
|
||||
* Constant value for the DispatcherServlet bean name. A Servlet bean with this name
|
||||
* is deemed to be the "main" servlet and is automatically given a mapping of "/" by
|
||||
* default. To change the default behaviour you can use a
|
||||
* {@link ServletRegistrationBean} or a different bean name.
|
||||
*/
|
||||
public static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
|
||||
|
||||
private EmbeddedServletContainer embeddedServletContainer;
|
||||
|
||||
private ServletConfig servletConfig;
|
||||
|
||||
private String namespace;
|
||||
|
||||
/**
|
||||
* Register ServletContextAwareProcessor.
|
||||
* @see ServletContextAwareProcessor
|
||||
*/
|
||||
@Override
|
||||
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
beanFactory
|
||||
.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(
|
||||
this));
|
||||
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRefresh() {
|
||||
super.onRefresh();
|
||||
registerShutdownHook();
|
||||
createAndStartEmbeddedServletContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() {
|
||||
super.doClose();
|
||||
stopAndReleaseEmbeddedServletContainer();
|
||||
}
|
||||
|
||||
private synchronized void createAndStartEmbeddedServletContainer() {
|
||||
if (this.embeddedServletContainer == null && getServletContext() == null) {
|
||||
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
|
||||
this.embeddedServletContainer = containerFactory
|
||||
.getEmbeddedServletContainer(getSelfInitializer());
|
||||
}
|
||||
else if (getServletContext() != null) {
|
||||
try {
|
||||
getSelfInitializer().onStartup(getServletContext());
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new ApplicationContextException(
|
||||
"Cannot initialize servlet context", ex);
|
||||
}
|
||||
}
|
||||
WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory(),
|
||||
getServletContext());
|
||||
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
|
||||
getServletContext());
|
||||
initPropertySources();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EmbeddedServletContainerFactory} that should be used to create
|
||||
* the embedded servlet container. By default this method searches for a suitable bean
|
||||
* in the context itself.
|
||||
* @return a {@link EmbeddedServletContainerFactory} (never {@code null})
|
||||
*/
|
||||
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
|
||||
// Use bean names so that we don't consider the hierarchy
|
||||
String[] beanNames = getBeanFactory().getBeanNamesForType(
|
||||
EmbeddedServletContainerFactory.class);
|
||||
if (beanNames.length == 0) {
|
||||
throw new ApplicationContextException(
|
||||
"Unable to start EmbeddedWebApplicationContext due to missing "
|
||||
+ "EmbeddedServletContainerFactory bean.");
|
||||
}
|
||||
if (beanNames.length > 1) {
|
||||
throw new ApplicationContextException(
|
||||
"Unable to start EmbeddedWebApplicationContext due to multiple "
|
||||
+ "EmbeddedServletContainerFactory beans : "
|
||||
+ StringUtils.arrayToCommaDelimitedString(beanNames));
|
||||
}
|
||||
return getBeanFactory().getBean(beanNames[0],
|
||||
EmbeddedServletContainerFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ServletContextInitializer} that will be used to complete the
|
||||
* setup of this {@link WebApplicationContext}.
|
||||
* @see #prepareEmbeddedWebApplicationContext(ServletContext)
|
||||
*/
|
||||
private ServletContextInitializer getSelfInitializer() {
|
||||
return new ServletContextInitializer() {
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
prepareEmbeddedWebApplicationContext(servletContext);
|
||||
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
|
||||
beans.onStartup(servletContext);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link ServletContextInitializer}s that should be used with the embedded
|
||||
* Servlet context. By default this method will first attempt to find
|
||||
* {@link ServletContextInitializer} beans, if none are found it will instead search
|
||||
* for {@link Servlet} and {@link Filter} beans.
|
||||
*/
|
||||
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
|
||||
|
||||
Set<ServletContextInitializer> initializers = new LinkedHashSet<ServletContextInitializer>();
|
||||
Set<Object> targets = new HashSet<Object>();
|
||||
|
||||
for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(ServletContextInitializer.class)) {
|
||||
ServletContextInitializer initializer = initializerBean.getValue();
|
||||
if (initializer instanceof RegistrationBean) {
|
||||
targets.add(((RegistrationBean) initializer).getRegistrationTarget());
|
||||
}
|
||||
initializers.add(initializer);
|
||||
}
|
||||
|
||||
List<Entry<String, Servlet>> servletBeans = getOrderedBeansOfType(Servlet.class);
|
||||
for (Entry<String, Servlet> servletBean : servletBeans) {
|
||||
final String name = servletBean.getKey();
|
||||
Servlet servlet = servletBean.getValue();
|
||||
if (targets.contains(servlet)) {
|
||||
continue;
|
||||
}
|
||||
String url = (servletBeans.size() == 1 ? "/" : "/" + name + "/*");
|
||||
if (name.equals(DISPATCHER_SERVLET_NAME)) {
|
||||
url = "/"; // always map the main dispatcherServlet to "/"
|
||||
}
|
||||
ServletRegistrationBean registration = new ServletRegistrationBean(servlet,
|
||||
url);
|
||||
registration.setName(name);
|
||||
registration.setMultipartConfig(getMultipartConfig());
|
||||
initializers.add(registration);
|
||||
}
|
||||
|
||||
for (Entry<String, Filter> filterBean : getOrderedBeansOfType(Filter.class)) {
|
||||
String name = filterBean.getKey();
|
||||
Filter filter = filterBean.getValue();
|
||||
if (targets.contains(filter)) {
|
||||
continue;
|
||||
}
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
|
||||
registration.setName(name);
|
||||
initializers.add(registration);
|
||||
}
|
||||
|
||||
return initializers;
|
||||
}
|
||||
|
||||
private MultipartConfigElement getMultipartConfig() {
|
||||
List<Entry<String, MultipartConfigElement>> beans = getOrderedBeansOfType(MultipartConfigElement.class);
|
||||
if (beans.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return beans.get(0).getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@link WebApplicationContext} with the given fully loaded
|
||||
* {@link ServletContext}. This method is usually called from
|
||||
* {@link ServletContextInitializer#onStartup(ServletContext)} and is similar to the
|
||||
* functionality usually provided by a {@link ContextLoaderListener}.
|
||||
* @param servletContext the operational servlet context
|
||||
*/
|
||||
protected void prepareEmbeddedWebApplicationContext(ServletContext servletContext) {
|
||||
if (servletContext
|
||||
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot initialize context because there is already a root application context present - "
|
||||
+ "check whether you have multiple ServletContextInitializer!");
|
||||
}
|
||||
Log logger = LogFactory.getLog(ContextLoader.class);
|
||||
servletContext.log("Initializing Spring embedded WebApplicationContext");
|
||||
try {
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["
|
||||
+ WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
|
||||
+ "]");
|
||||
}
|
||||
setServletContext(servletContext);
|
||||
if (logger.isInfoEnabled()) {
|
||||
long elapsedTime = System.currentTimeMillis() - getStartupDate();
|
||||
logger.info("Root WebApplicationContext: initialization completed in "
|
||||
+ elapsedTime + " ms");
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
logger.error("Context initialization failed", ex);
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
|
||||
throw ex;
|
||||
}
|
||||
catch (Error err) {
|
||||
logger.error("Context initialization failed", err);
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<Entry<String, T>> getOrderedBeansOfType(Class<T> type) {
|
||||
List<Entry<String, T>> beans = new ArrayList<Entry<String, T>>();
|
||||
Comparator<Entry<String, T>> comparator = new Comparator<Entry<String, T>>() {
|
||||
@Override
|
||||
public int compare(Entry<String, T> o1, Entry<String, T> o2) {
|
||||
return AnnotationAwareOrderComparator.INSTANCE.compare(o1.getValue(),
|
||||
o2.getValue());
|
||||
}
|
||||
};
|
||||
beans.addAll(getBeanFactory().getBeansOfType(type, true, true).entrySet());
|
||||
Collections.sort(beans, comparator);
|
||||
return beans;
|
||||
}
|
||||
|
||||
private synchronized void stopAndReleaseEmbeddedServletContainer() {
|
||||
if (this.embeddedServletContainer != null) {
|
||||
try {
|
||||
this.embeddedServletContainer.stop();
|
||||
this.embeddedServletContainer = null;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Resource getResourceByPath(String path) {
|
||||
if (getServletContext() == null) {
|
||||
return new ClassPathContextResource(path, getClassLoader());
|
||||
}
|
||||
return new ServletContextResource(getServletContext(), path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNamespace(String namespace) {
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletConfig(ServletConfig servletConfig) {
|
||||
this.servletConfig = servletConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletConfig getServletConfig() {
|
||||
return this.servletConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link EmbeddedServletContainer} that was created by the context or
|
||||
* {@code null} if the container has not yet been created.
|
||||
*/
|
||||
public EmbeddedServletContainer getEmbeddedServletContainer() {
|
||||
return this.embeddedServletContainer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Simple container-independent abstraction for servlet error pages. Roughly equivalent to
|
||||
* the {@literal <error-page>} element traditionally found in web.xml.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ErrorPage {
|
||||
|
||||
private String path;
|
||||
|
||||
private Class<? extends Throwable> exception = null;
|
||||
|
||||
private HttpStatus status = null;
|
||||
|
||||
public ErrorPage(String path) {
|
||||
super();
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public ErrorPage(HttpStatus status, String path) {
|
||||
super();
|
||||
this.status = status;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public ErrorPage(Class<? extends Throwable> exception, String path) {
|
||||
super();
|
||||
this.exception = exception;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to render (usually implemented as a forward), starting with "/". A custom
|
||||
* controller or servlet path can be used, or if the container supports it, a template
|
||||
* path (e.g. "/error.jsp").
|
||||
*
|
||||
* @return the path that will be rendered for this error
|
||||
*/
|
||||
public String getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the exception type (or null for a page that matches by status)
|
||||
*/
|
||||
public Class<? extends Throwable> getException() {
|
||||
return this.exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTTP status value that this error page matches.
|
||||
*
|
||||
* @return the status
|
||||
*/
|
||||
public HttpStatus getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTTP status value that this error page matches.
|
||||
*
|
||||
* @return the status value (or 0 for a page that matches any status)
|
||||
*/
|
||||
public int getStatusCode() {
|
||||
return this.status == null ? 0 : this.status.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* The exception type name.
|
||||
*
|
||||
* @return the exception type name (or null if there is none)
|
||||
*/
|
||||
public String getExceptionName() {
|
||||
return this.exception == null ? null : this.exception.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return is this error page a global one (matches all unmatched status and exception
|
||||
* types)?
|
||||
*/
|
||||
public boolean isGlobal() {
|
||||
return this.status == null && this.exception == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ObjectUtils.nullSafeHashCode(getExceptionName());
|
||||
result = prime * result + ObjectUtils.nullSafeHashCode(this.path);
|
||||
result = prime * result + this.getStatusCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ErrorPage other = (ErrorPage) obj;
|
||||
boolean rtn = true;
|
||||
rtn &= ObjectUtils.nullSafeEquals(getExceptionName(), other.getExceptionName());
|
||||
rtn &= ObjectUtils.nullSafeEquals(this.path, other.path);
|
||||
rtn &= this.status == other.status;
|
||||
return rtn;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterRegistration;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ServletContextInitializer} to register {@link Filter}s in a Servlet 3.0+
|
||||
* container. Similar to the {@link ServletContext#addFilter(String, Filter) registration}
|
||||
* features provided by {@link ServletContext} but with a Spring Bean friendly design.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #setFilter(Filter) Filter} must be specified before calling
|
||||
* {@link #onStartup(ServletContext)}. Registrations can be associated with
|
||||
* {@link #setUrlPatterns URL patterns} and/or servlets (either by
|
||||
* {@link #setServletNames name} or via a {@link #setServletRegistrationBeans
|
||||
* ServletRegistrationBean}s. When no URL pattern or servlets are specified the filter
|
||||
* will be associated to '/*'. The filter name will be deduced if not specified.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ServletContextInitializer
|
||||
* @see ServletContext#addFilter(String, Filter)
|
||||
*/
|
||||
public class FilterRegistrationBean extends RegistrationBean {
|
||||
|
||||
static final EnumSet<DispatcherType> ASYNC_DISPATCHER_TYPES = EnumSet.of(
|
||||
DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST,
|
||||
DispatcherType.ASYNC);
|
||||
|
||||
static final EnumSet<DispatcherType> NON_ASYNC_DISPATCHER_TYPES = EnumSet.of(
|
||||
DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
|
||||
|
||||
private static final String[] DEFAULT_URL_MAPPINGS = { "/*" };
|
||||
|
||||
private Filter filter;
|
||||
|
||||
private Set<ServletRegistrationBean> servletRegistrationBeans = new LinkedHashSet<ServletRegistrationBean>();
|
||||
|
||||
private Set<String> servletNames = new LinkedHashSet<String>();
|
||||
|
||||
private Set<String> urlPatterns = new LinkedHashSet<String>();
|
||||
|
||||
private EnumSet<DispatcherType> dispatcherTypes;
|
||||
|
||||
private boolean matchAfter = false;
|
||||
|
||||
/**
|
||||
* Create a new {@link FilterRegistrationBean} instance.
|
||||
*/
|
||||
public FilterRegistrationBean() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link FilterRegistrationBean} instance to be registered with the
|
||||
* specified {@link ServletRegistrationBean}s.
|
||||
* @param filter the filter to register
|
||||
* @param servletRegistrationBeans associate {@link ServletRegistrationBean}s
|
||||
*/
|
||||
public FilterRegistrationBean(Filter filter,
|
||||
ServletRegistrationBean... servletRegistrationBeans) {
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
Assert.notNull(servletRegistrationBeans,
|
||||
"ServletRegistrationBeans must not be null");
|
||||
this.filter = filter;
|
||||
for (ServletRegistrationBean servletRegistrationBean : servletRegistrationBeans) {
|
||||
this.servletRegistrationBeans.add(servletRegistrationBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the filter to be registered.
|
||||
*/
|
||||
public void setFilter(Filter filter) {
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link ServletRegistrationBean}s that the filter will be registered against.
|
||||
* @param servletRegistrationBeans the Servlet registration beans
|
||||
*/
|
||||
public void setServletRegistrationBeans(
|
||||
Collection<? extends ServletRegistrationBean> servletRegistrationBeans) {
|
||||
Assert.notNull(servletRegistrationBeans,
|
||||
"ServletRegistrationBeans must not be null");
|
||||
this.servletRegistrationBeans = new LinkedHashSet<ServletRegistrationBean>(
|
||||
servletRegistrationBeans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable collection of the {@link ServletRegistrationBean} that the filter
|
||||
* will be registered against. {@link ServletRegistrationBean}s.
|
||||
* @return the Servlet registration beans
|
||||
* @see #setServletNames
|
||||
* @see #setUrlPatterns
|
||||
*/
|
||||
public Collection<ServletRegistrationBean> getServletRegistrationBeans() {
|
||||
return this.servletRegistrationBeans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link ServletRegistrationBean}s for the filter.
|
||||
* @param servletRegistrationBeans the servlet registration beans to add
|
||||
* @see #setServletRegistrationBeans
|
||||
*/
|
||||
public void addServletRegistrationBeans(
|
||||
ServletRegistrationBean... servletRegistrationBeans) {
|
||||
Assert.notNull(servletRegistrationBeans,
|
||||
"ServletRegistrationBeans must not be null");
|
||||
for (ServletRegistrationBean servletRegistrationBean : servletRegistrationBeans) {
|
||||
this.servletRegistrationBeans.add(servletRegistrationBean);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set servlet names that the filter will be registered against. This will replace any
|
||||
* previously specified servlet names.
|
||||
* @param servletNames the servlet names
|
||||
* @see #setServletRegistrationBeans
|
||||
* @see #setUrlPatterns
|
||||
*/
|
||||
public void setServletNames(Collection<String> servletNames) {
|
||||
Assert.notNull(servletNames, "ServletNames must not be null");
|
||||
this.servletNames = new LinkedHashSet<String>(servletNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable collection of servlet names that the filter will be registered
|
||||
* against.
|
||||
* @return the servlet names
|
||||
*/
|
||||
public Collection<String> getServletNames() {
|
||||
return this.servletNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add servlet names for the filter.
|
||||
* @param servletNames the servlet names to add
|
||||
*/
|
||||
public void addServletNames(String... servletNames) {
|
||||
Assert.notNull(servletNames, "ServletNames must not be null");
|
||||
this.servletNames.addAll(Arrays.asList(servletNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL patterns that the filter will be registered against. This will replace
|
||||
* any previously specified URL patterns.
|
||||
* @param urlPatterns the URL patterns
|
||||
* @see #setServletRegistrationBeans
|
||||
* @see #setServletNames
|
||||
*/
|
||||
public void setUrlPatterns(Collection<String> urlPatterns) {
|
||||
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
|
||||
this.urlPatterns = new LinkedHashSet<String>(urlPatterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable collection of URL patterns that the filter will be registered
|
||||
* against.
|
||||
* @return the URL patterns
|
||||
*/
|
||||
public Collection<String> getUrlPatterns() {
|
||||
return this.urlPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add URL patterns that the filter will be registered against.
|
||||
* @param urlPatterns the URL patterns
|
||||
*/
|
||||
public void addUrlPatterns(String... urlPatterns) {
|
||||
Assert.notNull(urlPatterns, "UrlPatterns must not be null");
|
||||
for (String urlPattern : urlPatterns) {
|
||||
this.urlPatterns.add(urlPattern);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the filter mappings should be matched after any declared filter mappings of
|
||||
* the ServletContext. Defaults to {@code false} indicating the filters are supposed
|
||||
* to be matched before any declared filter mappings of the ServletContext.
|
||||
*/
|
||||
public void setMatchAfter(boolean matchAfter) {
|
||||
this.matchAfter = matchAfter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if filter mappings should be matched after any declared Filter mappings of
|
||||
* the ServletContext.
|
||||
*/
|
||||
public boolean isMatchAfter() {
|
||||
return this.matchAfter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
Assert.notNull(this.filter, "Filter must not be null");
|
||||
configure(servletContext.addFilter(getName(), this.filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRegistrationTarget() {
|
||||
return this.filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure registration settings. Subclasses can override this method to perform
|
||||
* additional configuration if required.
|
||||
*/
|
||||
protected void configure(FilterRegistration.Dynamic registration) {
|
||||
super.configure(registration);
|
||||
EnumSet<DispatcherType> dispatcherTypes = this.dispatcherTypes;
|
||||
if (dispatcherTypes == null) {
|
||||
dispatcherTypes = (isAsyncSupported() ? ASYNC_DISPATCHER_TYPES
|
||||
: NON_ASYNC_DISPATCHER_TYPES);
|
||||
}
|
||||
|
||||
Set<String> servletNames = new LinkedHashSet<String>();
|
||||
for (ServletRegistrationBean servletRegistrationBean : this.servletRegistrationBeans) {
|
||||
servletNames.add(servletRegistrationBean.getServletName());
|
||||
}
|
||||
servletNames.addAll(this.servletNames);
|
||||
|
||||
if (servletNames.isEmpty() && this.urlPatterns.isEmpty()) {
|
||||
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
|
||||
DEFAULT_URL_MAPPINGS);
|
||||
}
|
||||
else {
|
||||
if (servletNames.size() > 0) {
|
||||
registration.addMappingForServletNames(dispatcherTypes, this.matchAfter,
|
||||
servletNames.toArray(new String[servletNames.size()]));
|
||||
}
|
||||
if (this.urlPatterns.size() > 0) {
|
||||
registration.addMappingForUrlPatterns(dispatcherTypes, this.matchAfter,
|
||||
this.urlPatterns.toArray(new String[this.urlPatterns.size()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Registration;
|
||||
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class for {@link ServletRegistrationBean servlet} and
|
||||
* {@link FilterRegistrationBean filter} registration beans.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ServletRegistrationBean
|
||||
* @see FilterRegistrationBean
|
||||
*/
|
||||
public abstract class RegistrationBean implements ServletContextInitializer {
|
||||
|
||||
private String name;
|
||||
|
||||
private boolean asyncSupported = true;
|
||||
|
||||
private Map<String, String> initParameters = new LinkedHashMap<String, String>();
|
||||
|
||||
/**
|
||||
* Set the name of this registration. If not specified the bean name will be used.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
Assert.hasLength(name, "Name must not be empty");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return getOrDeduceName(getRegistrationTarget());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if asynchronous operations are support for this registration. If not specified
|
||||
* defaults to {@code true}.
|
||||
*/
|
||||
public void setAsyncSupported(boolean asyncSupported) {
|
||||
this.asyncSupported = asyncSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if asynchronous operations are support for this registration.
|
||||
*/
|
||||
public boolean isAsyncSupported() {
|
||||
return this.asyncSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set init-parameters for this registration. Calling this method will replace any
|
||||
* existing init-parameters.
|
||||
* @see #getInitParameters
|
||||
* @see #addInitParameter
|
||||
*/
|
||||
public void setInitParameters(Map<String, String> initParameters) {
|
||||
Assert.notNull(initParameters, "InitParameters must not be null");
|
||||
this.initParameters = new LinkedHashMap<String, String>(initParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable Map of the registration init-parameters.
|
||||
*/
|
||||
public Map<String, String> getInitParameters() {
|
||||
return this.initParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single init-parameter, replacing any existing parameter with the same name.
|
||||
* @param name the init-parameter name
|
||||
* @param value the init-parameter value
|
||||
*/
|
||||
public void addInitParameter(String name, String value) {
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
this.initParameters.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The target of the registration (e.g. a Servlet or a Filter) that can be used to
|
||||
* guess its name if none is supplied explicitly.
|
||||
*
|
||||
* @return the target of this registration
|
||||
*/
|
||||
public abstract Object getRegistrationTarget();
|
||||
|
||||
/**
|
||||
* Deduces the name for this registration. Will return user specified name or fallback
|
||||
* to convention based naming.
|
||||
* @param value the object used for convention based names
|
||||
*/
|
||||
private String getOrDeduceName(Object value) {
|
||||
return (this.name != null ? this.name : Conventions.getVariableName(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure registration base settings.
|
||||
*/
|
||||
protected void configure(Registration.Dynamic registration) {
|
||||
registration.setAsyncSupported(this.asyncSupported);
|
||||
if (this.initParameters.size() > 0) {
|
||||
registration.setInitParameters(this.initParameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.springframework.web.SpringServletContainerInitializer;
|
||||
import org.springframework.web.WebApplicationInitializer;
|
||||
|
||||
/**
|
||||
* Interface used to configure a Servlet 3.0+ {@link ServletContext context}
|
||||
* programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this
|
||||
* interface (and do not implement {@link WebApplicationInitializer}) will <b>not</b> be
|
||||
* detected by {@link SpringServletContainerInitializer} and hence will not be
|
||||
* automatically bootstrapped by the Servlet container.
|
||||
*
|
||||
* <p>
|
||||
* This interface is primarily designed to allow {@link ServletContextInitializer}s to be
|
||||
* managed by Spring and not the Servlet container.
|
||||
*
|
||||
* <p>
|
||||
* For configuration examples see {@link WebApplicationInitializer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see WebApplicationInitializer
|
||||
*/
|
||||
public interface ServletContextInitializer {
|
||||
|
||||
/**
|
||||
* Configure the given {@link ServletContext} with any servlets, filters, listeners
|
||||
* context-params and attributes necessary for initialization.
|
||||
* @param servletContext the {@code ServletContext} to initialize
|
||||
* @throws ServletException if any call against the given {@code ServletContext}
|
||||
* throws a {@code ServletException}
|
||||
*/
|
||||
void onStartup(ServletContext servletContext) throws ServletException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.MultipartConfigElement;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRegistration;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ServletContextInitializer} to register {@link Servlet}s in a Servlet 3.0+
|
||||
* container. Similar to the {@link ServletContext#addServlet(String, Servlet)
|
||||
* registration} features provided by {@link ServletContext} but with a Spring Bean
|
||||
* friendly design.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #setServlet(Servlet) servlet} must be specified before calling
|
||||
* {@link #onStartup}. URL mapping can be configured used {@link #setUrlMappings} or
|
||||
* omitted when mapping to '/*'. The servlet name will be deduced if not specified.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ServletContextInitializer
|
||||
* @see ServletContext#addServlet(String, Servlet)
|
||||
*/
|
||||
public class ServletRegistrationBean extends RegistrationBean {
|
||||
|
||||
private static final String[] DEFAULT_MAPPINGS = { "/*" };
|
||||
|
||||
private Servlet servlet;
|
||||
|
||||
private Set<String> urlMappings = new LinkedHashSet<String>();
|
||||
|
||||
private int loadOnStartup = 1;
|
||||
|
||||
private MultipartConfigElement multipartConfig;
|
||||
|
||||
/**
|
||||
* Create a new {@link ServletRegistrationBean} instance.
|
||||
*/
|
||||
public ServletRegistrationBean() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ServletRegistrationBean} instance with the specified
|
||||
* {@link Servlet} and URL mappings.
|
||||
* @param servlet the servlet being mapped
|
||||
* @param urlMappings the URLs being mapped
|
||||
*/
|
||||
public ServletRegistrationBean(Servlet servlet, String... urlMappings) {
|
||||
Assert.notNull(servlet, "Servlet must not be null");
|
||||
Assert.notNull(urlMappings, "UrlMappings must not be null");
|
||||
this.servlet = servlet;
|
||||
this.urlMappings.addAll(Arrays.asList(urlMappings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the servlet to be registered.
|
||||
*/
|
||||
public void setServlet(Servlet servlet) {
|
||||
Assert.notNull(servlet, "Servlet must not be null");
|
||||
this.servlet = servlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL mappings for the servlet. If not specified the mapping will default to
|
||||
* '/'. This will replace any previously specified mappings.
|
||||
* @param urlMappings the mappings to set
|
||||
* @see #addUrlMappings(String...)
|
||||
*/
|
||||
public void setUrlMappings(Collection<String> urlMappings) {
|
||||
Assert.notNull(urlMappings, "UrlMappings must not be null");
|
||||
this.urlMappings = new LinkedHashSet<String>(urlMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a mutable collection of the URL mappings for the servlet.
|
||||
* @return the urlMappings
|
||||
*/
|
||||
public Collection<String> getUrlMappings() {
|
||||
return this.urlMappings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add URL mappings for the servlet.
|
||||
* @param urlMappings the mappings to add
|
||||
* @see #setUrlMappings(Collection)
|
||||
*/
|
||||
public void addUrlMappings(String... urlMappings) {
|
||||
Assert.notNull(urlMappings, "UrlMappings must not be null");
|
||||
this.urlMappings.addAll(Arrays.asList(urlMappings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>loadOnStartup</code> priority. See
|
||||
* {@link ServletRegistration.Dynamic#setLoadOnStartup} for details.
|
||||
*/
|
||||
public void setLoadOnStartup(int loadOnStartup) {
|
||||
this.loadOnStartup = loadOnStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the the {@link MultipartConfigElement multi-part configuration}.
|
||||
* @param multipartConfig the muti-part configuration to set or {@code null}
|
||||
*/
|
||||
public void setMultipartConfig(MultipartConfigElement multipartConfig) {
|
||||
this.multipartConfig = multipartConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MultipartConfigElement multi-part configuration} to be applied
|
||||
* or {@code null}.
|
||||
*/
|
||||
public MultipartConfigElement getMultipartConfig() {
|
||||
return this.multipartConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the servlet name that will be registered.
|
||||
*/
|
||||
public String getServletName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRegistrationTarget() {
|
||||
return this.servlet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
Assert.notNull(this.servlet, "Servlet must not be null");
|
||||
configure(servletContext.addServlet(getServletName(), this.servlet));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure registration settings. Subclasses can override this method to perform
|
||||
* additional configuration if required.
|
||||
*/
|
||||
protected void configure(ServletRegistration.Dynamic registration) {
|
||||
super.configure(registration);
|
||||
String[] urlMapping = this.urlMappings
|
||||
.toArray(new String[this.urlMappings.size()]);
|
||||
if (urlMapping.length == 0) {
|
||||
urlMapping = DEFAULT_MAPPINGS;
|
||||
}
|
||||
registration.addMapping(urlMapping);
|
||||
registration.setLoadOnStartup(this.loadOnStartup);
|
||||
if (this.multipartConfig != null) {
|
||||
registration.setMultipartConfig(this.multipartConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||
import org.springframework.web.context.support.ServletContextAwareProcessor;
|
||||
|
||||
/**
|
||||
* Variant of {@link ServletContextAwareProcessor} for use with a
|
||||
* {@link ConfigurableWebApplicationContext}. Can be used when registering the processor
|
||||
* can occur before the {@link ServletContext} or {@link ServletConfig} have been
|
||||
* initialized.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class WebApplicationContextServletContextAwareProcessor extends
|
||||
ServletContextAwareProcessor {
|
||||
|
||||
private final ConfigurableWebApplicationContext webApplicationContext;
|
||||
|
||||
public WebApplicationContextServletContextAwareProcessor(
|
||||
ConfigurableWebApplicationContext webApplicationContext) {
|
||||
Assert.notNull(webApplicationContext, "WebApplicationContext must not be null");
|
||||
this.webApplicationContext = webApplicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServletContext getServletContext() {
|
||||
ServletContext servletContext = this.webApplicationContext.getServletContext();
|
||||
return (servletContext != null ? servletContext : super.getServletContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServletConfig getServletConfig() {
|
||||
ServletConfig servletConfig = this.webApplicationContext.getServletConfig();
|
||||
return (servletConfig != null ? servletConfig : super.getServletConfig());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.web.context.support.XmlWebApplicationContext;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedWebApplicationContext} which takes its configuration from XML documents,
|
||||
* understood by an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
|
||||
*
|
||||
* <p>
|
||||
* Note: In case of multiple config locations, later bean definitions will override ones
|
||||
* defined in earlier loaded files. This can be leveraged to deliberately override certain
|
||||
* bean definitions via an extra XML file.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see #setNamespace
|
||||
* @see #setConfigLocations
|
||||
* @see EmbeddedWebApplicationContext
|
||||
* @see XmlWebApplicationContext
|
||||
*/
|
||||
public class XmlEmbeddedWebApplicationContext extends EmbeddedWebApplicationContext {
|
||||
|
||||
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
|
||||
|
||||
/**
|
||||
* Create a new {@link XmlEmbeddedWebApplicationContext} that needs to be
|
||||
* {@linkplain #load loaded} and then manually {@link #refresh refreshed}.
|
||||
*/
|
||||
public XmlEmbeddedWebApplicationContext() {
|
||||
this.reader.setEnvironment(this.getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link XmlEmbeddedWebApplicationContext}, loading bean definitions
|
||||
* from the given resources and automatically refreshing the context.
|
||||
* @param resources the resources to load from
|
||||
*/
|
||||
public XmlEmbeddedWebApplicationContext(Resource... resources) {
|
||||
load(resources);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link XmlEmbeddedWebApplicationContext}, loading bean definitions
|
||||
* from the given resource locations and automatically refreshing the context.
|
||||
* @param resourceLocations the resources to load from
|
||||
*/
|
||||
public XmlEmbeddedWebApplicationContext(String... resourceLocations) {
|
||||
load(resourceLocations);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link XmlEmbeddedWebApplicationContext}, loading bean definitions
|
||||
* from the given resource locations and automatically refreshing the context.
|
||||
* @param relativeClass class whose package will be used as a prefix when loading each
|
||||
* specified resource name
|
||||
* @param resourceNames relatively-qualified names of resources to load
|
||||
*/
|
||||
public XmlEmbeddedWebApplicationContext(Class<?> relativeClass,
|
||||
String... resourceNames) {
|
||||
load(relativeClass, resourceNames);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use XML validation. Default is {@code true}.
|
||||
*/
|
||||
public void setValidating(boolean validating) {
|
||||
this.reader.setValidating(validating);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Delegates the given environment to underlying {@link XmlBeanDefinitionReader}.
|
||||
* Should be called before any call to {@link #load}.
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
super.setEnvironment(environment);
|
||||
this.reader.setEnvironment(this.getEnvironment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given XML resources.
|
||||
* @param resources one or more resources to load from
|
||||
*/
|
||||
public final void load(Resource... resources) {
|
||||
this.reader.loadBeanDefinitions(resources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given XML resources.
|
||||
* @param resourceLocations one or more resource locations to load from
|
||||
*/
|
||||
public final void load(String... resourceLocations) {
|
||||
this.reader.loadBeanDefinitions(resourceLocations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load bean definitions from the given XML resources.
|
||||
* @param relativeClass class whose package will be used as a prefix when loading each
|
||||
* specified resource name
|
||||
* @param resourceNames relatively-qualified names of resources to load
|
||||
*/
|
||||
public final void load(Class<?> relativeClass, String... resourceNames) {
|
||||
Resource[] resources = new Resource[resourceNames.length];
|
||||
for (int i = 0; i < resourceNames.length; i++) {
|
||||
resources[i] = new ClassPathResource(resourceNames[i], relativeClass);
|
||||
}
|
||||
this.reader.loadBeanDefinitions(resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void refresh() throws BeansException, IllegalStateException {
|
||||
super.refresh();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.jetty;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerException;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedServletContainer} that can be used to control an embedded Jetty server.
|
||||
* Usually this class should be created using the
|
||||
* {@link JettyEmbeddedServletContainerFactory} and not directly.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see JettyEmbeddedServletContainerFactory
|
||||
*/
|
||||
public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||
|
||||
private final Server server;
|
||||
|
||||
/**
|
||||
* Create a new {@link JettyEmbeddedServletContainer} instance.
|
||||
* @param server the underlying Jetty server
|
||||
*/
|
||||
public JettyEmbeddedServletContainer(Server server) {
|
||||
Assert.notNull(server, "Jetty Server must not be null");
|
||||
this.server = server;
|
||||
start();
|
||||
}
|
||||
|
||||
private synchronized void start() {
|
||||
try {
|
||||
this.server.start();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to start embedded Jetty servlet container", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void stop() {
|
||||
try {
|
||||
this.server.stop();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
// No drama
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to stop embedded Jetty servlet container", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access to the underlying Jetty Server.
|
||||
*/
|
||||
public Server getServer() {
|
||||
return this.server;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.jetty;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.zero.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.ErrorPage;
|
||||
import org.springframework.zero.context.embedded.ServletContextInitializer;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedServletContainerFactory} that can be used to create
|
||||
* {@link JettyEmbeddedServletContainer}s. Can be initialized using Spring's
|
||||
* {@link ServletContextInitializer}s or Jetty {@link Configuration}s.
|
||||
*
|
||||
* <p>
|
||||
* Unless explicitly configured otherwise this factory will created containers that
|
||||
* listens for HTTP requests on port 8080.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see #setPort(int)
|
||||
* @see #setConfigurations(Collection)
|
||||
* @see JettyEmbeddedServletContainer
|
||||
*/
|
||||
public class JettyEmbeddedServletContainerFactory extends
|
||||
AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
|
||||
|
||||
private List<Configuration> configurations = new ArrayList<Configuration>();
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private WebAppContext context = new WebAppContext();
|
||||
|
||||
/**
|
||||
* Create a new {@link JettyEmbeddedServletContainerFactory} instance.
|
||||
*/
|
||||
public JettyEmbeddedServletContainerFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JettyEmbeddedServletContainerFactory} that listens for requests
|
||||
* using the specified port.
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public JettyEmbeddedServletContainerFactory(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link JettyEmbeddedServletContainerFactory} with the specified
|
||||
* context path and port.
|
||||
* @param contextPath root the context path
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public JettyEmbeddedServletContainerFactory(String contextPath, int port) {
|
||||
super(contextPath, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddedServletContainer getEmbeddedServletContainer(
|
||||
ServletContextInitializer... initializers) {
|
||||
if (getPort() == 0) {
|
||||
return EmbeddedServletContainer.NONE;
|
||||
}
|
||||
Server server = new Server(new InetSocketAddress(getAddress(), getPort()));
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
this.context.setClassLoader(this.resourceLoader.getClassLoader());
|
||||
}
|
||||
String contextPath = getContextPath();
|
||||
this.context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath
|
||||
: "/");
|
||||
configureDocumentRoot(this.context);
|
||||
if (isRegisterDefaultServlet()) {
|
||||
addDefaultServlet(this.context);
|
||||
}
|
||||
if (isRegisterJspServlet()
|
||||
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
|
||||
.getClassLoader())) {
|
||||
addJspServlet(this.context);
|
||||
}
|
||||
|
||||
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
|
||||
Configuration[] configurations = getWebAppContextConfigurations(this.context,
|
||||
initializersToUse);
|
||||
this.context.setConfigurations(configurations);
|
||||
postProcessWebAppContext(this.context);
|
||||
|
||||
server.setHandler(this.context);
|
||||
return getJettyEmbeddedServletContainer(server);
|
||||
}
|
||||
|
||||
private void configureDocumentRoot(WebAppContext handler) {
|
||||
File root = getValidDocumentRoot();
|
||||
if (root != null) {
|
||||
try {
|
||||
handler.setBaseResource(Resource.newResource(root));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDefaultServlet(WebAppContext context) {
|
||||
ServletHolder holder = new ServletHolder();
|
||||
holder.setName("default");
|
||||
holder.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
|
||||
holder.setInitParameter("dirAllowed", "false");
|
||||
holder.setInitOrder(1);
|
||||
context.getServletHandler().addServletWithMapping(holder, "/");
|
||||
context.getServletHandler().getServletMapping("/").setDefault(true);
|
||||
}
|
||||
|
||||
private void addJspServlet(WebAppContext context) {
|
||||
ServletHolder holder = new ServletHolder();
|
||||
holder.setName("jsp");
|
||||
holder.setClassName(getJspServletClassName());
|
||||
holder.setInitParameter("fork", "false");
|
||||
holder.setInitOrder(3);
|
||||
context.getServletHandler().addServlet(holder);
|
||||
ServletMapping mapping = new ServletMapping();
|
||||
mapping.setServletName("jsp");
|
||||
mapping.setPathSpecs(new String[] { "*.jsp", "*.jspx" });
|
||||
context.getServletHandler().addServletMapping(mapping);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Jetty {@link Configuration}s that should be applied to the server.
|
||||
* @param webAppContext the Jetty {@link WebAppContext}
|
||||
* @param initializers the {@link ServletContextInitializer}s to apply
|
||||
* @return configurations to apply
|
||||
*/
|
||||
protected Configuration[] getWebAppContextConfigurations(WebAppContext webAppContext,
|
||||
ServletContextInitializer... initializers) {
|
||||
List<Configuration> configurations = new ArrayList<Configuration>();
|
||||
configurations.add(getServletContextInitializerConfiguration(webAppContext,
|
||||
initializers));
|
||||
configurations.addAll(getConfigurations());
|
||||
configurations.add(getErrorPageConfiguration());
|
||||
return configurations.toArray(new Configuration[configurations.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a configuration object that adds error handlers
|
||||
*
|
||||
* @return a configuration object for adding error pages
|
||||
*/
|
||||
private Configuration getErrorPageConfiguration() {
|
||||
return new AbstractConfiguration() {
|
||||
@Override
|
||||
public void configure(WebAppContext context) throws Exception {
|
||||
ErrorHandler errorHandler = context.getErrorHandler();
|
||||
addJettyErrorPages(errorHandler, getErrorPages());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Jetty {@link Configuration} that will invoke the specified
|
||||
* {@link ServletContextInitializer}s. By default this method will return a
|
||||
* {@link ServletContextInitializerConfiguration}.
|
||||
* @param webAppContext the Jetty {@link WebAppContext}
|
||||
* @param initializers the {@link ServletContextInitializer}s to apply
|
||||
* @return the {@link Configuration} instance
|
||||
*/
|
||||
protected Configuration getServletContextInitializerConfiguration(
|
||||
WebAppContext webAppContext, ServletContextInitializer... initializers) {
|
||||
return new ServletContextInitializerConfiguration(webAppContext, initializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process the Jetty {@link WebAppContext} before it used with the Jetty Server.
|
||||
* Subclasses can override this method to apply additional processing to the
|
||||
* {@link WebAppContext}.
|
||||
* @param webAppContext the Jetty {@link WebAppContext}
|
||||
*/
|
||||
protected void postProcessWebAppContext(WebAppContext webAppContext) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method called to create the {@link JettyEmbeddedServletContainer}.
|
||||
* Subclasses can override this method to return a different
|
||||
* {@link JettyEmbeddedServletContainer} or apply additional processing to the Jetty
|
||||
* server.
|
||||
* @param server the Jetty server.
|
||||
* @return a new {@link JettyEmbeddedServletContainer} instance
|
||||
*/
|
||||
protected JettyEmbeddedServletContainer getJettyEmbeddedServletContainer(Server server) {
|
||||
return new JettyEmbeddedServletContainer(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Jetty {@link Configuration}s that will be applied to the {@link WebAppContext}
|
||||
* before the server is created. Calling this method will replace any existing
|
||||
* configurations.
|
||||
* @param configurations the Jetty configurations to apply
|
||||
*/
|
||||
public void setConfigurations(Collection<? extends Configuration> configurations) {
|
||||
Assert.notNull(configurations, "Configurations must not be null");
|
||||
this.configurations = new ArrayList<Configuration>(configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable collection of Jetty {@link Configuration}s that will be applied
|
||||
* to the {@link WebAppContext} before the server is created.
|
||||
* @return the Jetty {@link Configuration}s
|
||||
*/
|
||||
public Collection<Configuration> getConfigurations() {
|
||||
return this.configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link Configuration}s that will be applied to the {@link WebAppContext} before
|
||||
* the server is started.
|
||||
*
|
||||
* @param configurations the configurations to add
|
||||
*/
|
||||
public void addConfigurations(Configuration... configurations) {
|
||||
Assert.notNull(configurations, "Configurations must not be null");
|
||||
this.configurations.addAll(Arrays.asList(configurations));
|
||||
}
|
||||
|
||||
private void addJettyErrorPages(ErrorHandler errorHandler,
|
||||
Collection<ErrorPage> errorPages) {
|
||||
if (errorHandler instanceof ErrorPageErrorHandler) {
|
||||
ErrorPageErrorHandler handler = (ErrorPageErrorHandler) errorHandler;
|
||||
for (ErrorPage errorPage : errorPages) {
|
||||
if (errorPage.isGlobal()) {
|
||||
handler.addErrorPage(ErrorPageErrorHandler.GLOBAL_ERROR_PAGE,
|
||||
errorPage.getPath());
|
||||
}
|
||||
else {
|
||||
if (errorPage.getExceptionName() != null) {
|
||||
handler.addErrorPage(errorPage.getExceptionName(),
|
||||
errorPage.getPath());
|
||||
}
|
||||
else {
|
||||
handler.addErrorPage(errorPage.getStatusCode(),
|
||||
errorPage.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.jetty;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.zero.context.embedded.ServletContextInitializer;
|
||||
|
||||
/**
|
||||
* Jetty {@link Configuration} that calls {@link ServletContextInitializer}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ServletContextInitializerConfiguration extends AbstractConfiguration {
|
||||
|
||||
private ContextHandler contextHandler;
|
||||
|
||||
private ServletContextInitializer[] initializers;
|
||||
|
||||
/**
|
||||
* Create a new {@link ServletContextInitializerConfiguration}.
|
||||
* @param contextHandler the Jetty ContextHandler
|
||||
* @param initializers the initializers that should be invoked
|
||||
*/
|
||||
public ServletContextInitializerConfiguration(ContextHandler contextHandler,
|
||||
ServletContextInitializer... initializers) {
|
||||
Assert.notNull(contextHandler, "Jetty ContextHandler must not be null");
|
||||
Assert.notNull(initializers, "Initializers must not be null");
|
||||
this.contextHandler = contextHandler;
|
||||
this.initializers = initializers;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebAppContext context) throws Exception {
|
||||
context.addBean(new InitializerListener(), true);
|
||||
}
|
||||
|
||||
private class InitializerListener extends AbstractLifeCycle {
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception {
|
||||
ServletContext servletContext = ServletContextInitializerConfiguration.this.contextHandler
|
||||
.getServletContext();
|
||||
for (ServletContextInitializer initializer : ServletContextInitializerConfiguration.this.initializers) {
|
||||
initializer.onStartup(servletContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for Jetty {@link org.springframework.zero.context.embedded.EmbeddedServletContainer EmbeddedServletContainers}.
|
||||
*/
|
||||
package org.springframework.zero.context.embedded.jetty;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for embedded servlet containers.
|
||||
* @see org.springframework.zero.context.embedded.EmbeddedServletContainerFactory
|
||||
* @see org.springframework.zero.context.embedded.EmbeddedWebApplicationContext
|
||||
*/
|
||||
package org.springframework.zero.context.embedded;
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.embedded.properties;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.zero.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* {@link ConfigurationProperties properties} for a web server (e.g. port and path
|
||||
* settings). Will be used to customize an {@link EmbeddedServletContainerFactory} when an
|
||||
* {@link EmbeddedServletContainerCustomizerBeanPostProcessor} is active.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties(name = "server", ignoreUnknownFields = false)
|
||||
public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
private InetAddress address;
|
||||
|
||||
@NotNull
|
||||
private String contextPath = "";
|
||||
|
||||
private Tomcat tomcat = new Tomcat();
|
||||
|
||||
public Tomcat getTomcat() {
|
||||
return this.tomcat;
|
||||
}
|
||||
|
||||
public String getContextPath() {
|
||||
return this.contextPath;
|
||||
}
|
||||
|
||||
public void setContextPath(String contextPath) {
|
||||
this.contextPath = contextPath;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public InetAddress getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
public void setAddress(InetAddress address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
|
||||
factory.setPort(getPort());
|
||||
factory.setAddress(getAddress());
|
||||
factory.setContextPath(getContextPath());
|
||||
if (factory instanceof TomcatEmbeddedServletContainerFactory) {
|
||||
getTomcat().customizeTomcat((TomcatEmbeddedServletContainerFactory) factory);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Tomcat {
|
||||
|
||||
private String accessLogPattern;
|
||||
|
||||
private String protocolHeader = "x-forwarded-proto";
|
||||
|
||||
private String remoteIpHeader = "x-forwarded-for";
|
||||
|
||||
private File basedir;
|
||||
|
||||
public File getBasedir() {
|
||||
return this.basedir;
|
||||
}
|
||||
|
||||
public void setBasedir(File basedir) {
|
||||
this.basedir = basedir;
|
||||
}
|
||||
|
||||
public String getAccessLogPattern() {
|
||||
return this.accessLogPattern;
|
||||
}
|
||||
|
||||
public void setAccessLogPattern(String accessLogPattern) {
|
||||
this.accessLogPattern = accessLogPattern;
|
||||
}
|
||||
|
||||
public String getProtocolHeader() {
|
||||
return this.protocolHeader;
|
||||
}
|
||||
|
||||
public void setProtocolHeader(String protocolHeader) {
|
||||
this.protocolHeader = protocolHeader;
|
||||
}
|
||||
|
||||
public String getRemoteIpHeader() {
|
||||
return this.remoteIpHeader;
|
||||
}
|
||||
|
||||
public void setRemoteIpHeader(String remoteIpHeader) {
|
||||
this.remoteIpHeader = remoteIpHeader;
|
||||
}
|
||||
|
||||
void customizeTomcat(TomcatEmbeddedServletContainerFactory factory) {
|
||||
if (getBasedir() != null) {
|
||||
factory.setBaseDirectory(getBasedir());
|
||||
}
|
||||
|
||||
String remoteIpHeader = getRemoteIpHeader();
|
||||
String protocolHeader = getProtocolHeader();
|
||||
if (StringUtils.hasText(remoteIpHeader)
|
||||
|| StringUtils.hasText(protocolHeader)) {
|
||||
RemoteIpValve valve = new RemoteIpValve();
|
||||
valve.setRemoteIpHeader(remoteIpHeader);
|
||||
valve.setProtocolHeader(protocolHeader);
|
||||
factory.addContextValves(valve);
|
||||
}
|
||||
|
||||
String accessLogPattern = getAccessLogPattern();
|
||||
if (accessLogPattern != null) {
|
||||
AccessLogValve valve = new AccessLogValve();
|
||||
valve.setPattern(accessLogPattern);
|
||||
valve.setSuffix(".log");
|
||||
factory.addContextValves(valve);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.tomcat;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleEvent;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.zero.context.embedded.ServletContextInitializer;
|
||||
|
||||
/**
|
||||
* Tomcat {@link LifecycleListener} that calls {@link ServletContextInitializer}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ServletContextInitializerLifecycleListener implements LifecycleListener {
|
||||
|
||||
private ServletContextInitializer[] initializers;
|
||||
|
||||
/**
|
||||
* Create a new {@link ServletContextInitializerLifecycleListener} instance with the
|
||||
* specified initializers.
|
||||
* @param initializers the initializers to call
|
||||
*/
|
||||
public ServletContextInitializerLifecycleListener(
|
||||
ServletContextInitializer... initializers) {
|
||||
this.initializers = initializers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifecycleEvent(LifecycleEvent event) {
|
||||
if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType())) {
|
||||
Assert.isInstanceOf(StandardContext.class, event.getSource());
|
||||
StandardContext standardContext = (StandardContext) event.getSource();
|
||||
for (ServletContextInitializer initializer : this.initializers) {
|
||||
try {
|
||||
initializer.onStartup(standardContext.getServletContext());
|
||||
}
|
||||
catch (ServletException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.tomcat;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerException;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedServletContainer} that can be used to control an embedded Tomcat server.
|
||||
* Usually this class should be created using the
|
||||
* {@link TomcatEmbeddedServletContainerFactory} and not directly.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see TomcatEmbeddedServletContainerFactory
|
||||
*/
|
||||
public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||
|
||||
private final Tomcat tomcat;
|
||||
|
||||
/**
|
||||
* Create a new {@link TomcatEmbeddedServletContainer} instance.
|
||||
* @param tomcat the underlying Tomcat server
|
||||
*/
|
||||
public TomcatEmbeddedServletContainer(Tomcat tomcat) {
|
||||
Assert.notNull(tomcat, "Tomcat Server must not be null");
|
||||
this.tomcat = tomcat;
|
||||
start();
|
||||
}
|
||||
|
||||
private synchronized void start() throws EmbeddedServletContainerException {
|
||||
try {
|
||||
this.tomcat.start();
|
||||
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
|
||||
// blocking non-daemon to stop immediate shutdown
|
||||
Thread awaitThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
TomcatEmbeddedServletContainer.this.tomcat.getServer().await();
|
||||
};
|
||||
};
|
||||
awaitThread.setDaemon(false);
|
||||
awaitThread.start();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to start embdedded Tomcat", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void stop() throws EmbeddedServletContainerException {
|
||||
try {
|
||||
try {
|
||||
this.tomcat.stop();
|
||||
}
|
||||
catch (LifecycleException ex) {
|
||||
// swallow and continue
|
||||
}
|
||||
this.tomcat.destroy();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to stop embdedded Tomcat", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns access to the underlying Tomcat server.
|
||||
*/
|
||||
public Tomcat getTomcat() {
|
||||
return this.tomcat;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* Copyright 2002-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.zero.context.embedded.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Host;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.Server;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.core.StandardEngine;
|
||||
import org.apache.catalina.core.StandardHost;
|
||||
import org.apache.catalina.core.StandardService;
|
||||
import org.apache.catalina.loader.WebappLoader;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.Tomcat.FixContextListener;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.zero.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerException;
|
||||
import org.springframework.zero.context.embedded.EmbeddedServletContainerFactory;
|
||||
import org.springframework.zero.context.embedded.ErrorPage;
|
||||
import org.springframework.zero.context.embedded.ServletContextInitializer;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedServletContainerFactory} that can be used to create
|
||||
* {@link TomcatEmbeddedServletContainer}s. Can be initialized using Spring's
|
||||
* {@link ServletContextInitializer}s or Tomcat {@link LifecycleListener}s.
|
||||
*
|
||||
* <p>
|
||||
* Unless explicitly configured otherwise this factory will created containers that
|
||||
* listens for HTTP requests on port 8080.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see #setPort(int)
|
||||
* @see #setContextLifecycleListeners(Collection)
|
||||
* @see TomcatEmbeddedServletContainer
|
||||
*/
|
||||
public class TomcatEmbeddedServletContainerFactory extends
|
||||
AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
|
||||
|
||||
private File baseDirectory;
|
||||
|
||||
private List<Valve> contextValves = new ArrayList<Valve>();
|
||||
|
||||
private List<LifecycleListener> contextLifecycleListeners = new ArrayList<LifecycleListener>();
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private Connector connector;
|
||||
|
||||
private Tomcat tomcat = new Tomcat();
|
||||
|
||||
/**
|
||||
* Create a new {@link TomcatEmbeddedServletContainerFactory} instance.
|
||||
*/
|
||||
public TomcatEmbeddedServletContainerFactory() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TomcatEmbeddedServletContainerFactory} that listens for
|
||||
* requests using the specified port.
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public TomcatEmbeddedServletContainerFactory(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link TomcatEmbeddedServletContainerFactory} with the specified
|
||||
* context path and port.
|
||||
* @param contextPath root the context path
|
||||
* @param port the port to listen on
|
||||
*/
|
||||
public TomcatEmbeddedServletContainerFactory(String contextPath, int port) {
|
||||
super(contextPath, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddedServletContainer getEmbeddedServletContainer(
|
||||
ServletContextInitializer... initializers) {
|
||||
if (getPort() == 0) {
|
||||
return EmbeddedServletContainer.NONE;
|
||||
}
|
||||
File baseDir = (this.baseDirectory != null ? this.baseDirectory
|
||||
: createTempDir("tomcat"));
|
||||
this.tomcat.setBaseDir(baseDir.getAbsolutePath());
|
||||
if (this.connector != null) {
|
||||
this.connector.setPort(getPort());
|
||||
this.tomcat.getService().addConnector(this.connector);
|
||||
this.tomcat.setConnector(this.connector);
|
||||
}
|
||||
else {
|
||||
Connector connector = new Connector(
|
||||
"org.apache.coyote.http11.Http11NioProtocol");
|
||||
customizeConnector(connector);
|
||||
this.tomcat.getService().addConnector(connector);
|
||||
this.tomcat.setConnector(connector);
|
||||
}
|
||||
this.tomcat.getHost().setAutoDeploy(false);
|
||||
this.tomcat.getEngine().setBackgroundProcessorDelay(-1);
|
||||
|
||||
prepareContext(this.tomcat.getHost(), initializers);
|
||||
return getTomcatEmbeddedServletContainer(this.tomcat);
|
||||
}
|
||||
|
||||
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
|
||||
File docBase = getValidDocumentRoot();
|
||||
docBase = (docBase != null ? docBase : createTempDir("tomcat-docbase"));
|
||||
Context context = new StandardContext();
|
||||
context.setName(getContextPath());
|
||||
context.setPath(getContextPath());
|
||||
context.setDocBase(docBase.getAbsolutePath());
|
||||
context.addLifecycleListener(new FixContextListener());
|
||||
context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader
|
||||
.getClassLoader() : ClassUtils.getDefaultClassLoader());
|
||||
WebappLoader loader = new WebappLoader(context.getParentClassLoader());
|
||||
loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
|
||||
context.setLoader(loader);
|
||||
|
||||
if (isRegisterDefaultServlet()) {
|
||||
addDefaultServlet(context);
|
||||
}
|
||||
if (isRegisterJspServlet()
|
||||
&& ClassUtils.isPresent(getJspServletClassName(), getClass()
|
||||
.getClassLoader())) {
|
||||
addJspServlet(context);
|
||||
}
|
||||
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
|
||||
configureContext(context, initializersToUse);
|
||||
host.addChild(context);
|
||||
postProcessContext(context);
|
||||
}
|
||||
|
||||
private void addDefaultServlet(Context context) {
|
||||
Wrapper defaultServlet = context.createWrapper();
|
||||
defaultServlet.setName("default");
|
||||
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
|
||||
defaultServlet.addInitParameter("debug", "0");
|
||||
defaultServlet.addInitParameter("listings", "false");
|
||||
defaultServlet.setLoadOnStartup(1);
|
||||
// Otherwise the default location of a Spring DispatcherServlet cannot be set
|
||||
defaultServlet.setOverridable(true);
|
||||
context.addChild(defaultServlet);
|
||||
context.addServletMapping("/", "default");
|
||||
}
|
||||
|
||||
private void addJspServlet(Context context) {
|
||||
Wrapper jspServlet = context.createWrapper();
|
||||
jspServlet.setName("jsp");
|
||||
jspServlet.setServletClass(getJspServletClassName());
|
||||
jspServlet.addInitParameter("fork", "false");
|
||||
jspServlet.setLoadOnStartup(3);
|
||||
context.addChild(jspServlet);
|
||||
context.addServletMapping("*.jsp", "jsp");
|
||||
context.addServletMapping("*.jspx", "jsp");
|
||||
}
|
||||
|
||||
// Needs to be protected so it can be used by subclasses
|
||||
protected void customizeConnector(Connector connector) {
|
||||
connector.setPort(getPort());
|
||||
if (connector.getProtocolHandler() instanceof AbstractProtocol
|
||||
&& getAddress() != null) {
|
||||
((AbstractProtocol) connector.getProtocolHandler()).setAddress(getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the Tomcat {@link Context}.
|
||||
* @param context the Tomcat context
|
||||
* @param initializers initializers to apply
|
||||
*/
|
||||
protected void configureContext(Context context,
|
||||
ServletContextInitializer[] initializers) {
|
||||
context.addLifecycleListener(new ServletContextInitializerLifecycleListener(
|
||||
initializers));
|
||||
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
|
||||
context.addLifecycleListener(lifecycleListener);
|
||||
}
|
||||
for (Valve valve : this.contextValves) {
|
||||
context.getPipeline().addValve(valve);
|
||||
}
|
||||
for (ErrorPage errorPage : getErrorPages()) {
|
||||
org.apache.catalina.deploy.ErrorPage tomcatPage = new org.apache.catalina.deploy.ErrorPage();
|
||||
tomcatPage.setLocation(errorPage.getPath());
|
||||
tomcatPage.setExceptionType(errorPage.getExceptionName());
|
||||
tomcatPage.setErrorCode(errorPage.getStatusCode());
|
||||
context.addErrorPage(tomcatPage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post process the Tomcat {@link Context} before it used with the Tomcat Server.
|
||||
* Subclasses can override this method to apply additional processing to the
|
||||
* {@link Context}.
|
||||
* @param context the Tomcat {@link Context}
|
||||
*/
|
||||
protected void postProcessContext(Context context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method called to create the {@link TomcatEmbeddedServletContainer}.
|
||||
* Subclasses can override this method to return a different
|
||||
* {@link TomcatEmbeddedServletContainer} or apply additional processing to the Tomcat
|
||||
* server.
|
||||
* @param tomcat the Tomcat server.
|
||||
* @return a new {@link TomcatEmbeddedServletContainer} instance
|
||||
*/
|
||||
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
|
||||
Tomcat tomcat) {
|
||||
return new TomcatEmbeddedServletContainer(tomcat);
|
||||
}
|
||||
|
||||
private File createTempDir(String prefix) {
|
||||
try {
|
||||
File tempFolder = File.createTempFile(prefix + ".", "." + getPort());
|
||||
tempFolder.delete();
|
||||
tempFolder.mkdir();
|
||||
tempFolder.deleteOnExit();
|
||||
return tempFolder;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new EmbeddedServletContainerException(
|
||||
"Unable to create Tomcat tempdir", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Tomcat base directory. If not specified a temporary directory will be used.
|
||||
* @param baseDirectory the tomcat base directory
|
||||
*/
|
||||
public void setBaseDirectory(File baseDirectory) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tomcat connector to use (e.g. if you want to change the protocol handler).
|
||||
*
|
||||
* @param connector the connector to set
|
||||
*/
|
||||
public void setConnector(Connector connector) {
|
||||
this.connector = connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link Valve}s that should be applied to the Tomcat {@link Context}. Calling
|
||||
* this method will replace any existing listeners.
|
||||
* @param contextValves the valves to set
|
||||
*/
|
||||
public void setContextValves(Collection<? extends Valve> contextValves) {
|
||||
Assert.notNull(contextValves, "Valves must not be null");
|
||||
this.contextValves = new ArrayList<Valve>(contextValves);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable collection of the {@link Valve}s that will be applied to the
|
||||
* Tomcat {@link Context}.
|
||||
* @return the contextValves the valves that will be applied
|
||||
*/
|
||||
public Collection<Valve> getValves() {
|
||||
return this.contextValves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link Valve}s that should be applied to the Tomcat {@link Context}.
|
||||
* @param contextValves the valves to add
|
||||
*/
|
||||
public void addContextValves(Valve... contextValves) {
|
||||
Assert.notNull(contextValves, "Valves must not be null");
|
||||
this.contextValves.addAll(Arrays.asList(contextValves));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@link LifecycleListener}s that should be applied to the Tomcat {@link Context}
|
||||
* . Calling this method will replace any existing listeners.
|
||||
* @param contextLifecycleListeners the listeners to set
|
||||
*/
|
||||
public void setContextLifecycleListeners(
|
||||
Collection<? extends LifecycleListener> contextLifecycleListeners) {
|
||||
Assert.notNull(contextLifecycleListeners,
|
||||
"ContextLifecycleListeners must not be null");
|
||||
this.contextLifecycleListeners = new ArrayList<LifecycleListener>(
|
||||
contextLifecycleListeners);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mutable collection of the {@link LifecycleListener}s that will be applied
|
||||
* to the Tomcat {@link Context} .
|
||||
* @return the contextLifecycleListeners the listeners that will be applied
|
||||
*/
|
||||
public Collection<LifecycleListener> getContextLifecycleListeners() {
|
||||
return this.contextLifecycleListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link LifecycleListener}s that should be applied to the Tomcat {@link Context}
|
||||
* .
|
||||
* @param contextLifecycleListeners the listeners to add
|
||||
*/
|
||||
public void addContextLifecycleListeners(
|
||||
LifecycleListener... contextLifecycleListeners) {
|
||||
Assert.notNull(contextLifecycleListeners,
|
||||
"ContextLifecycleListeners must not be null");
|
||||
this.contextLifecycleListeners.addAll(Arrays.asList(contextLifecycleListeners));
|
||||
}
|
||||
|
||||
// FIXME JavaDoc
|
||||
// FIXME Is this still needed?
|
||||
public TomcatEmbeddedServletContainerFactory getChildContextFactory(final String name) {
|
||||
|
||||
final Server server = this.tomcat.getServer();
|
||||
|
||||
return new TomcatEmbeddedServletContainerFactory() {
|
||||
|
||||
@Override
|
||||
public EmbeddedServletContainer getEmbeddedServletContainer(
|
||||
ServletContextInitializer... initializers) {
|
||||
|
||||
if (getPort() == 0) {
|
||||
return EmbeddedServletContainer.NONE;
|
||||
}
|
||||
StandardService service = new StandardService();
|
||||
service.setName(name);
|
||||
Connector connector = new Connector(
|
||||
"org.apache.coyote.http11.Http11NioProtocol");
|
||||
customizeConnector(connector);
|
||||
service.addConnector(connector);
|
||||
StandardEngine engine = new StandardEngine();
|
||||
engine.setName(name);
|
||||
engine.setDefaultHost("localhost");
|
||||
service.setContainer(engine);
|
||||
server.addService(service);
|
||||
StandardHost host = new StandardHost();
|
||||
host.setName("localhost");
|
||||
engine.addChild(host);
|
||||
prepareContext(host, initializers);
|
||||
|
||||
return new EmbeddedServletContainer() {
|
||||
@Override
|
||||
public void stop() throws EmbeddedServletContainerException {
|
||||
// noop
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.embedded.tomcat;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
import org.apache.catalina.loader.WebappClassLoader;
|
||||
|
||||
/**
|
||||
* Extension of Tomcat's {@link WebappClassLoader} that does not consider the
|
||||
* {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to to
|
||||
* ensure that any custom context classloader is always used (as is the case with some
|
||||
* executable archives).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class TomcatEmbeddedWebappClassLoader extends WebappClassLoader {
|
||||
|
||||
public TomcatEmbeddedWebappClassLoader() {
|
||||
super();
|
||||
this.system = new EmbeddedSystemClassLoader();
|
||||
}
|
||||
|
||||
public TomcatEmbeddedWebappClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
this.system = new EmbeddedSystemClassLoader();
|
||||
}
|
||||
|
||||
private static class EmbeddedSystemClassLoader extends URLClassLoader {
|
||||
|
||||
public EmbeddedSystemClassLoader() {
|
||||
super(new URL[] {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
throw new ClassNotFoundException(
|
||||
"System ClassLoader disabled for embedded context, unable to load "
|
||||
+ name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2002-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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for Tomcat {@link org.springframework.zero.context.embedded.EmbeddedServletContainer EmbeddedServletContainers}.
|
||||
*/
|
||||
package org.springframework.zero.context.embedded.tomcat;
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright 2010-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.context.initializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.zero.config.YamlProcessor.ArrayDocumentMatcher;
|
||||
import org.springframework.zero.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.zero.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.zero.config.YamlPropertiesFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextInitializer} that configures the context environment by
|
||||
* loading properties from well known file locations. By default properties will be loaded
|
||||
* from 'application.properties' and/or 'application.yml' files in the following
|
||||
* locations:
|
||||
* <ul>
|
||||
* <li>classpath:</li>
|
||||
* <li>file:./</li>
|
||||
* <li>classpath:config/</li>
|
||||
* <li>file:./config/:</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Alternative locations and names can be specified using
|
||||
* {@link #setSearchLocations(String[])} and {@link #setName(String)}.
|
||||
*
|
||||
* <p>
|
||||
* Additional files will also be loaded based on active profiles. For example if a 'web'
|
||||
* profile is active 'application-web.properties' and 'application-web.yml' will be
|
||||
* considered.
|
||||
*
|
||||
* <p>
|
||||
* The 'spring.config.name' property can be used to specify an alternative name to load or
|
||||
* alternatively the 'spring.config.location' property can be used to specify an exact
|
||||
* resource location.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ConfigFileApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private static final Loader[] LOADERS = { new PropertiesLoader(), new YamlLoader() };
|
||||
|
||||
private static final String LOCATION_VARIABLE = "${spring.config.location}";
|
||||
|
||||
private String[] searchLocations = new String[] { "classpath:", "file:./",
|
||||
"classpath:config/", "file:./config/" };
|
||||
|
||||
private String name = "${spring.config.name:application}";
|
||||
|
||||
private int order = Integer.MIN_VALUE + 10;
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
List<String> candidates = getCandidateLocations();
|
||||
|
||||
// Initial load allows profiles to be activated
|
||||
for (String candidate : candidates) {
|
||||
load(applicationContext, candidate, null);
|
||||
}
|
||||
|
||||
// Second load for specific profiles
|
||||
for (String profile : applicationContext.getEnvironment().getActiveProfiles()) {
|
||||
for (String candidate : candidates) {
|
||||
load(applicationContext, candidate, profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getCandidateLocations() {
|
||||
List<String> candidates = new ArrayList<String>();
|
||||
for (String searchLocation : this.searchLocations) {
|
||||
for (Loader loader : LOADERS) {
|
||||
for (String extension : loader.getExtensions()) {
|
||||
String location = searchLocation + this.name + extension;
|
||||
candidates.add(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
candidates.add(LOCATION_VARIABLE);
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void load(ConfigurableApplicationContext applicationContext, String location,
|
||||
String profile) {
|
||||
location = applicationContext.getEnvironment().resolvePlaceholders(location);
|
||||
String suffix = "." + StringUtils.getFilenameExtension(location);
|
||||
if (StringUtils.hasLength(profile)) {
|
||||
location = location.replace(suffix, "-" + profile + suffix);
|
||||
}
|
||||
for (Loader loader : LOADERS) {
|
||||
if (loader.getExtensions().contains(suffix.toLowerCase())) {
|
||||
Resource resource = applicationContext.getResource(location);
|
||||
if (resource != null && resource.exists()) {
|
||||
loader.load(resource, applicationContext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the file that should be loaded (excluding any file extension).
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the search locations that will be considered.
|
||||
*/
|
||||
public void setSearchLocations(String[] searchLocations) {
|
||||
this.searchLocations = (searchLocations == null ? null : searchLocations.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy interface used to load a {@link PropertySource}.
|
||||
*/
|
||||
private static interface Loader {
|
||||
|
||||
/**
|
||||
* @return The supported extensions (including '.' and in lowercase)
|
||||
*/
|
||||
public Set<String> getExtensions();
|
||||
|
||||
/**
|
||||
* Load the resource into the destination application context.
|
||||
*/
|
||||
void load(Resource resource, ConfigurableApplicationContext applicationContext);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to load '.properties' files.
|
||||
*/
|
||||
private static class PropertiesLoader implements Loader {
|
||||
|
||||
@Override
|
||||
public Set<String> getExtensions() {
|
||||
return Collections.singleton(".properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Resource resource,
|
||||
ConfigurableApplicationContext applicationContext) {
|
||||
try {
|
||||
Properties properties = loadProperties(resource, applicationContext);
|
||||
MutablePropertySources propertySources = applicationContext
|
||||
.getEnvironment().getPropertySources();
|
||||
if (propertySources
|
||||
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.addAfter(
|
||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
new PropertiesPropertySource(resource.getDescription(),
|
||||
properties));
|
||||
|
||||
}
|
||||
else {
|
||||
propertySources.addFirst(new PropertiesPropertySource(resource
|
||||
.getDescription(), properties));
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Could not load properties file from "
|
||||
+ resource, ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected Properties loadProperties(Resource resource,
|
||||
ConfigurableApplicationContext applicationContext) throws IOException {
|
||||
return PropertiesLoaderUtils.loadProperties(resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy to load '.yml' files.
|
||||
*/
|
||||
private static class YamlLoader extends PropertiesLoader {
|
||||
|
||||
@Override
|
||||
public Set<String> getExtensions() {
|
||||
return Collections.singleton(".yml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Properties loadProperties(final Resource resource,
|
||||
final ConfigurableApplicationContext applicationContext)
|
||||
throws IOException {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
List<DocumentMatcher> matchers = new ArrayList<DocumentMatcher>();
|
||||
matchers.add(new DocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
String[] profiles = applicationContext.getEnvironment()
|
||||
.getActiveProfiles();
|
||||
if (profiles.length == 0) {
|
||||
profiles = new String[] { "default" };
|
||||
}
|
||||
return new ArrayDocumentMatcher("spring.profiles", profiles)
|
||||
.matches(properties);
|
||||
|
||||
}
|
||||
});
|
||||
matchers.add(new DocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
if (!properties.containsKey("spring.profiles")) {
|
||||
Set<String> profiles = StringUtils
|
||||
.commaDelimitedListToSet(properties.getProperty(
|
||||
"spring.profiles.active", ""));
|
||||
for (String profile : profiles) {
|
||||
// allow document with no profile to set the active one
|
||||
applicationContext.getEnvironment().addActiveProfile(profile);
|
||||
}
|
||||
// matches default profile
|
||||
return MatchStatus.FOUND;
|
||||
}
|
||||
else {
|
||||
return MatchStatus.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
});
|
||||
factory.setMatchDefault(false);
|
||||
factory.setDocumentMatchers(matchers);
|
||||
factory.setResources(new Resource[] { resource });
|
||||
return factory.getObject();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2010-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.context.initializer;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextInitializer} that set the Spring
|
||||
* {@link ApplicationContext#getId() ApplicationContext ID}. The following environment
|
||||
* properties will be consulted to create the ID:
|
||||
* <ul>
|
||||
* <li>spring.application.name</li>
|
||||
* <li>vcap.application.name</li>
|
||||
* <li>spring.config.name</li>
|
||||
* </ul>
|
||||
* If no property is set the ID 'application' will be used.
|
||||
*
|
||||
* <p>
|
||||
* In addition the following environment properties will be consulted to append a relevant
|
||||
* port or index:
|
||||
*
|
||||
* <ul>
|
||||
* <li>spring.application.index</li>
|
||||
* <li>vcap.application.instance_index</li>
|
||||
* <li>PORT</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ContextIdApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private String name = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";
|
||||
|
||||
private int index = -1;
|
||||
|
||||
private int order = Integer.MAX_VALUE - 10;
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
applicationContext.setId(getApplicationId(applicationContext.getEnvironment()));
|
||||
}
|
||||
|
||||
private String getApplicationId(ConfigurableEnvironment environment) {
|
||||
String name = environment.resolvePlaceholders(this.name);
|
||||
int index = environment.getProperty("PORT", Integer.class, this.index);
|
||||
index = environment.getProperty("vcap.application.instance_index", Integer.class,
|
||||
index);
|
||||
index = environment.getProperty("spring.application.index", Integer.class, index);
|
||||
if (index >= 0) {
|
||||
name = name + ":" + index;
|
||||
}
|
||||
else {
|
||||
// FIXME do we want this
|
||||
String profiles = StringUtils.arrayToCommaDelimitedString(environment
|
||||
.getActiveProfiles());
|
||||
if (StringUtils.hasText(profiles)) {
|
||||
name = name + ":" + profiles;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.initializer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextInitializer} that delegates to other initializers that are
|
||||
* specified under a {@literal context.initializer.classes} environment property.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class EnvironmentDelegateApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
// NOTE: Similar to org.springframework.web.context.ContextLoader
|
||||
|
||||
private static final String PROPERTY_NAME = "context.initializer.classes";
|
||||
private int order = 0;
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
ConfigurableEnvironment env = applicationContext.getEnvironment();
|
||||
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(env);
|
||||
|
||||
if (initializerClasses.size() == 0) {
|
||||
// no ApplicationContextInitializers have been declared -> nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> contextClass = applicationContext.getClass();
|
||||
ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances = new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();
|
||||
|
||||
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
|
||||
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(
|
||||
initializerClass, ApplicationContextInitializer.class);
|
||||
Assert.isAssignable(
|
||||
initializerContextClass,
|
||||
contextClass,
|
||||
String.format(
|
||||
"Could not add context initializer [%s] as its generic parameter [%s] "
|
||||
+ "is not assignable from the type of application context used by this "
|
||||
+ "context loader [%s]: ",
|
||||
initializerClass.getName(),
|
||||
initializerContextClass.getName(), contextClass.getName()));
|
||||
initializerInstances.add(BeanUtils.instantiateClass(initializerClass));
|
||||
}
|
||||
|
||||
Collections.sort(initializerInstances, new AnnotationAwareOrderComparator());
|
||||
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
|
||||
initializer.initialize(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> determineContextInitializerClasses(
|
||||
ConfigurableEnvironment env) {
|
||||
String classNames = env.getProperty(PROPERTY_NAME);
|
||||
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes = new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();
|
||||
if (StringUtils.hasLength(classNames)) {
|
||||
for (String className : StringUtils.tokenizeToStringArray(classNames, ",")) {
|
||||
try {
|
||||
Class<?> clazz = ClassUtils.forName(className,
|
||||
ClassUtils.getDefaultClassLoader());
|
||||
Assert.isAssignable(ApplicationContextInitializer.class, clazz,
|
||||
"class [" + className
|
||||
+ "] must implement ApplicationContextInitializer");
|
||||
classes.add((Class<ApplicationContextInitializer<ConfigurableApplicationContext>>) clazz);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new ApplicationContextException(
|
||||
"Failed to load context initializer class [" + className
|
||||
+ "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.initializer;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.Log4jConfigurer;
|
||||
import org.springframework.zero.logging.JavaLoggerConfigurer;
|
||||
import org.springframework.zero.logging.LogbackConfigurer;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationContextInitializer} that configures a logging framework depending
|
||||
* on what it finds on the classpath and in the {@link Environment}. If the environment
|
||||
* contains a property <code>logging.config</code> then that will be used to initialize
|
||||
* the logging system, otherwise a default location is used. The classpath is probed for
|
||||
* log4j and logback and if those are present they will be reconfigured, otherwise vanilla
|
||||
* <code>java.util.logging</code> will be used. </p>
|
||||
*
|
||||
* <p>
|
||||
* The default config locations are <code>classpath:log4j.properties</code> or
|
||||
* <code>classpath:log4j.xml</code> for log4j; <code>classpath:logback.xml</code> for
|
||||
* logback; and <code>classpath:logging.properties</code> for
|
||||
* <code>java.util.logging</code>. If the correct one of those files is not found then
|
||||
* some sensible defaults are adopted from files of the same name but in the package
|
||||
* containing {@link LoggingApplicationContextInitializer}.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Some system properties may be set as side effects, and these can be useful if the
|
||||
* logging configuration supports placeholders (i.e. log4j or logback):
|
||||
* <ul>
|
||||
* <li><code>LOG_FILE</code> is set to the value of <code>logging.file</code> if found in
|
||||
* the environment</li>
|
||||
* <li><code>LOG_PATH</code> is set to the value of <code>logging.path</code> if found in
|
||||
* the environment</li>
|
||||
* <li><code>PID</code> is set to the value of the current process ID if it can be
|
||||
* determined</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class LoggingApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private static final Map<String, String> ENVIRONMENT_SYSTEM_PROPERTY_MAPPING;
|
||||
static {
|
||||
ENVIRONMENT_SYSTEM_PROPERTY_MAPPING = new HashMap<String, String>();
|
||||
ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("logging.file", "LOG_FILE");
|
||||
ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("logging.path", "LOG_PATH");
|
||||
ENVIRONMENT_SYSTEM_PROPERTY_MAPPING.put("PID", "PID");
|
||||
}
|
||||
|
||||
private int order = Integer.MIN_VALUE + 11;
|
||||
|
||||
/**
|
||||
* Initialize the logging system according to preferences expressed through the
|
||||
* {@link Environment} and the classpath.
|
||||
*/
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
|
||||
for (Map.Entry<String, String> mapping : ENVIRONMENT_SYSTEM_PROPERTY_MAPPING
|
||||
.entrySet()) {
|
||||
if (environment.containsProperty(mapping.getKey())) {
|
||||
System.setProperty(mapping.getValue(),
|
||||
environment.getProperty(mapping.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
if (System.getProperty("PID") == null) {
|
||||
System.setProperty("PID", getPid());
|
||||
}
|
||||
|
||||
LoggingSystem system = LoggingSystem.get(applicationContext.getClassLoader());
|
||||
system.init(applicationContext);
|
||||
}
|
||||
|
||||
private String getPid() {
|
||||
String name = ManagementFactory.getRuntimeMXBean().getName();
|
||||
if (name != null) {
|
||||
return name.split("@")[0];
|
||||
}
|
||||
return "????";
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
private static enum LoggingSystem {
|
||||
|
||||
/**
|
||||
* Log4J
|
||||
*/
|
||||
LOG4J("org.apache.log4j.PropertyConfigurator", "log4j.xml", "log4j.properties") {
|
||||
|
||||
@Override
|
||||
protected void doInit(ApplicationContext applicationContext,
|
||||
String configLocation) throws Exception {
|
||||
Log4jConfigurer.initLogging(configLocation);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Logback
|
||||
*/
|
||||
LOGBACK("ch.qos.logback.core.Appender", "logback.xml") {
|
||||
|
||||
@Override
|
||||
protected void doInit(ApplicationContext applicationContext,
|
||||
String configLocation) throws Exception {
|
||||
LogbackConfigurer.initLogging(configLocation);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Java Util Logging
|
||||
*/
|
||||
JAVA(null, "logging.properties") {
|
||||
|
||||
@Override
|
||||
protected void doInit(ApplicationContext applicationContext,
|
||||
String configLocation) throws Exception {
|
||||
JavaLoggerConfigurer.initLogging(configLocation);
|
||||
}
|
||||
};
|
||||
|
||||
private final String className;
|
||||
|
||||
private final String[] paths;
|
||||
|
||||
private LoggingSystem(String className, String... paths) {
|
||||
this.className = className;
|
||||
this.paths = paths;
|
||||
}
|
||||
|
||||
public void init(ApplicationContext applicationContext) {
|
||||
String configLocation = getConfigLocation(applicationContext);
|
||||
try {
|
||||
doInit(applicationContext, configLocation);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Cannot initialize logging from "
|
||||
+ configLocation, ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void doInit(ApplicationContext applicationContext,
|
||||
String configLocation) throws Exception;
|
||||
|
||||
private String getConfigLocation(ApplicationContext applicationContext) {
|
||||
Environment environment = applicationContext.getEnvironment();
|
||||
ClassLoader classLoader = applicationContext.getClassLoader();
|
||||
|
||||
// User specified config
|
||||
if (environment.containsProperty("logging.config")) {
|
||||
return environment.getProperty("logging.config");
|
||||
}
|
||||
|
||||
// Common patterns
|
||||
for (String path : this.paths) {
|
||||
ClassPathResource resource = new ClassPathResource(path, classLoader);
|
||||
if (resource.exists()) {
|
||||
return "classpath:" + path;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to the default
|
||||
String defaultPath = ClassUtils.getPackageName(JavaLoggerConfigurer.class);
|
||||
defaultPath = defaultPath.replace(".", "/");
|
||||
defaultPath = defaultPath + "/" + this.paths[this.paths.length - 1];
|
||||
return "classpath:" + defaultPath;
|
||||
}
|
||||
|
||||
public static LoggingSystem get(ClassLoader classLoader) {
|
||||
for (LoggingSystem loggingSystem : values()) {
|
||||
String className = loggingSystem.className;
|
||||
if (className == null || ClassUtils.isPresent(className, classLoader)) {
|
||||
return loggingSystem;
|
||||
}
|
||||
}
|
||||
return JAVA;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2010-2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.zero.context.initializer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CommandLinePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.zero.config.JsonParser;
|
||||
import org.springframework.zero.config.JsonParserFactory;
|
||||
|
||||
/**
|
||||
* An {@link ApplicationContextInitializer} that knows where to find VCAP (a.k.a. Cloud
|
||||
* Foundry) meta data in the existing environment. It parses out the VCAP_APPLICATION and
|
||||
* VCAP_SERVICES meta data and dumps it in a form that is easily consumed by
|
||||
* {@link Environment} users. If the app is running in Cloud Foundry then both meta data
|
||||
* items are JSON objects encoded in OS environment variables. VCAP_APPLICATION is a
|
||||
* shallow hash with basic information about the application (name, instance id, instance
|
||||
* index, etc.), and VCAP_SERVICES is a hash of lists where the keys are service labels
|
||||
* and the values are lists of hashes of service instance meta data. Examples are:
|
||||
*
|
||||
* <pre>
|
||||
* VCAP_APPLICATION: {"instance_id":"2ce0ac627a6c8e47e936d829a3a47b5b","instance_index":0,
|
||||
* "version":"0138c4a6-2a73-416b-aca0-572c09f7ca53","name":"foo",
|
||||
* "uris":["foo.cfapps.io"], ...}
|
||||
* VCAP_SERVICES: {"rds-mysql-1.0":[{"name":"mysql","label":"rds-mysql-1.0","plan":"10mb",
|
||||
* "credentials":{"name":"d04fb13d27d964c62b267bbba1cffb9da","hostname":"mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com",
|
||||
* "host":"mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com","port":3306,"user":"urpRuqTf8Cpe6",
|
||||
* "username":"urpRuqTf8Cpe6","password":"pxLsGVpsC9A5S"}
|
||||
* }]}
|
||||
* </pre>
|
||||
*
|
||||
* These objects are flattened into properties. The VCAP_APPLICATION object goes straight
|
||||
* to <code>vcap.application.*</code> in a fairly obvious way, and the VCAP_SERVICES
|
||||
* object is unwrapped so that it is a hash of objects with key equal to the service
|
||||
* instance name (e.g. "mysql" in the example above), and value equal to that instances
|
||||
* properties, and then flattened in the smae way. E.g.
|
||||
*
|
||||
* <pre>
|
||||
* vcap.application.instance_id: 2ce0ac627a6c8e47e936d829a3a47b5b
|
||||
* vcap.application.version: 0138c4a6-2a73-416b-aca0-572c09f7ca53
|
||||
* vcap.application.name: foo
|
||||
* vcap.application.uris[0]: foo.cfapps.io
|
||||
*
|
||||
* vcap.services.mysql.name: mysql
|
||||
* vcap.services.mysql.label: rds-mysql-1.0
|
||||
* vcap.services.mysql.credentials.name: d04fb13d27d964c62b267bbba1cffb9da
|
||||
* vcap.services.mysql.credentials.port: 3306
|
||||
* vcap.services.mysql.credentials.host: mysql-service-public.clqg2e2w3ecf.us-east-1.rds.amazonaws.com
|
||||
* vcap.services.mysql.credentials.username: urpRuqTf8Cpe6
|
||||
* vcap.services.mysql.credentials.password: pxLsGVpsC9A5S
|
||||
* ...
|
||||
* </pre>
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class VcapApplicationContextInitializer implements
|
||||
ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
private static final String VCAP_APPLICATION = "VCAP_APPLICATION";
|
||||
|
||||
private static final String VCAP_SERVICES = "VCAP_SERVICES";
|
||||
|
||||
private int order = Integer.MIN_VALUE + 11;
|
||||
|
||||
private JsonParser parser = JsonParserFactory.getJsonParser();
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
if (!environment.containsProperty(VCAP_APPLICATION)
|
||||
&& !environment.containsProperty(VCAP_SERVICES)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Properties properties = new Properties();
|
||||
addWithPrefix(properties, getPropertiesFromApplication(environment),
|
||||
"vcap.application.");
|
||||
addWithPrefix(properties, getPropertiesFromServices(environment),
|
||||
"vcap.services.");
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
if (propertySources
|
||||
.contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) {
|
||||
propertySources.addAfter(
|
||||
CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME,
|
||||
new PropertiesPropertySource("vcap", properties));
|
||||
|
||||
}
|
||||
else {
|
||||
propertySources.addFirst(new PropertiesPropertySource("vcap", properties));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addWithPrefix(Properties properties, Properties other, String prefix) {
|
||||
for (String key : other.stringPropertyNames()) {
|
||||
String prefixed = prefix + key;
|
||||
properties.setProperty(prefixed, other.getProperty(key));
|
||||
}
|
||||
}
|
||||
|
||||
private Properties getPropertiesFromApplication(Environment environment) {
|
||||
Map<String, Object> map = this.parser.parseMap(environment.getProperty(
|
||||
VCAP_APPLICATION, "{}"));
|
||||
Properties properties = new Properties();
|
||||
properties.putAll(map);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private Properties getPropertiesFromServices(Environment environment) {
|
||||
Map<String, Object> map = this.parser.parseMap(environment.getProperty(
|
||||
VCAP_SERVICES, "{}"));
|
||||
Properties properties = new Properties();
|
||||
for (Object services : map.values()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> list = (List<Object>) services;
|
||||
for (Object object : list) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> service = (Map<String, Object>) object;
|
||||
String key = (String) service.get("name");
|
||||
if (key == null) {
|
||||
key = (String) service.get("label");
|
||||
}
|
||||
flatten(properties, service, key);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void flatten(Properties properties, Map<String, Object> input, String path) {
|
||||
for (Entry<String, Object> entry : input.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (StringUtils.hasText(path)) {
|
||||
if (key.startsWith("[")) {
|
||||
key = path + key;
|
||||
}
|
||||
else {
|
||||
key = path + "." + key;
|
||||
}
|
||||
}
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof String) {
|
||||
properties.put(key, value);
|
||||
}
|
||||
else if (value instanceof Map) {
|
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) value;
|
||||
flatten(properties, map, key);
|
||||
}
|
||||
else if (value instanceof Collection) {
|
||||
// Need a compound key
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Object> collection = (Collection<Object>) value;
|
||||
properties.put(key,
|
||||
StringUtils.collectionToCommaDelimitedString(collection));
|
||||
int count = 0;
|
||||
for (Object object : collection) {
|
||||
flatten(properties,
|
||||
Collections.singletonMap("[" + (count++) + "]", object), key);
|
||||
}
|
||||
}
|
||||
else {
|
||||
properties.put(key, value == null ? "" : value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation for externalized configuration. Add this to a class definition if you want
|
||||
* to bind and validate some external Properties (e.g. from a .properties file).
|
||||
*
|
||||
* @see ConfigurationPropertiesBindingPostProcessor
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface ConfigurationProperties {
|
||||
|
||||
/**
|
||||
* The (optional) name of the object to be bound. Properties to bind can have a name
|
||||
* prefix to select the properties that are valid to this object. Synonym for
|
||||
* {@link #name()}.
|
||||
*
|
||||
* @return the name prefix of the properties to bind
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* The (optional) name of the object to be bound. Properties to bind can have a name
|
||||
* prefix to select the properties that are valid to this object. Synonym for
|
||||
* {@link #value()}.
|
||||
*
|
||||
* @return the name prefix of the properties to bind
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Flag to indicate that when binding to this object invalid fields should be ignored.
|
||||
* Invalid means invalid according to the binder that is used, and usually this means
|
||||
* fields of the wrong type (or that cannot be coerced into the correct type).
|
||||
*
|
||||
* @return the flag value (default false)
|
||||
*/
|
||||
boolean ignoreInvalidFields() default false;
|
||||
|
||||
/**
|
||||
* Flag to indicate that when binding to this object unknown fields should be ignored.
|
||||
* An unknown field could be a sign of a mistake in the Properties.
|
||||
*
|
||||
* @return the flag value (default true)
|
||||
*/
|
||||
boolean ignoreUnknownFields() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.zero.context.condition.ConditionalOnClass;
|
||||
import org.springframework.zero.context.condition.ConditionalOnMissingBean;
|
||||
|
||||
/**
|
||||
* Configuration for binding externalized application properties to
|
||||
* {@link ConfigurationProperties} beans.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
public class ConfigurationPropertiesBindingConfiguration {
|
||||
|
||||
public final static String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";
|
||||
|
||||
@Autowired(required = false)
|
||||
private PropertySourcesPlaceholderConfigurer configurer;
|
||||
|
||||
@Autowired(required = false)
|
||||
private Environment environment;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier(ConfigurableApplicationContext.CONVERSION_SERVICE_BEAN_NAME)
|
||||
private ConversionService conversionService;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier(VALIDATOR_BEAN_NAME)
|
||||
private Validator validator;
|
||||
|
||||
@ConditionalOnMissingBean(name = VALIDATOR_BEAN_NAME)
|
||||
@ConditionalOnClass(name = "javax.validation.Validator")
|
||||
protected static class ValidatorConfiguration {
|
||||
@Bean
|
||||
protected Validator configurationPropertiesValidator() {
|
||||
return new LocalValidatorFactoryBean();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lifecycle hook that binds application properties to any bean whose type is
|
||||
* decorated with {@link ConfigurationProperties} annotation.
|
||||
*
|
||||
* @return a bean post processor to bind application properties
|
||||
*/
|
||||
@Bean
|
||||
public ConfigurationPropertiesBindingPostProcessor propertySourcesBinder() {
|
||||
PropertySources propertySources;
|
||||
|
||||
if (this.configurer != null) {
|
||||
propertySources = extractPropertySources(this.configurer);
|
||||
}
|
||||
else if (this.environment instanceof ConfigurableEnvironment) {
|
||||
propertySources = flattenPropertySources(((ConfigurableEnvironment) this.environment)
|
||||
.getPropertySources());
|
||||
}
|
||||
else {
|
||||
// empty, so not very useful, but fulfils the contract
|
||||
propertySources = new MutablePropertySources();
|
||||
}
|
||||
|
||||
ConfigurationPropertiesBindingPostProcessor processor = new ConfigurationPropertiesBindingPostProcessor();
|
||||
processor.setValidator(this.validator);
|
||||
processor.setConversionService(this.conversionService);
|
||||
processor.setPropertySources(propertySources);
|
||||
return processor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten out a tree of property sources.
|
||||
*
|
||||
* @param propertySources some PropertySources, possibly containing environment
|
||||
* properties
|
||||
* @return another PropertySources containing the same properties
|
||||
*/
|
||||
private PropertySources flattenPropertySources(PropertySources propertySources) {
|
||||
MutablePropertySources result = new MutablePropertySources();
|
||||
for (PropertySource<?> propertySource : propertySources) {
|
||||
flattenPropertySources(propertySource, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to allow recursive flattening of property sources.
|
||||
*
|
||||
* @param propertySource a property source to flatten
|
||||
* @param result the cumulative result
|
||||
*/
|
||||
private void flattenPropertySources(PropertySource<?> propertySource,
|
||||
MutablePropertySources result) {
|
||||
Object source = propertySource.getSource();
|
||||
if (source instanceof ConfigurableEnvironment) {
|
||||
ConfigurableEnvironment environment = (ConfigurableEnvironment) source;
|
||||
for (PropertySource<?> childSource : environment.getPropertySources()) {
|
||||
flattenPropertySources(childSource, result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.addLast(propertySource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to extract PropertySources from an existing (and already
|
||||
* initialized) PropertySourcesPlaceholderConfigurer. As long as this method is
|
||||
* executed late enough in the context lifecycle it will come back with data. We can
|
||||
* rely on the fact that PropertySourcesPlaceholderConfigurer is a
|
||||
* BeanFactoryPostProcessor and is therefore initialized early.
|
||||
*
|
||||
* @param configurer a PropertySourcesPlaceholderConfigurer
|
||||
* @return some PropertySources
|
||||
*/
|
||||
private PropertySources extractPropertySources(
|
||||
PropertySourcesPlaceholderConfigurer configurer) {
|
||||
PropertySources propertySources = configurer.getAppliedPropertySources();
|
||||
// Flatten the sources into a single list so they can be iterated
|
||||
return flattenPropertySources(propertySources);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.zero.bind.PropertiesConfigurationFactory;
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to bind {@link PropertySources} to beans annotated with
|
||||
* {@link ConfigurationProperties}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor,
|
||||
BeanFactoryAware {
|
||||
|
||||
private PropertySources propertySources;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
private DefaultConversionService defaultConversionService = new DefaultConversionService();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
/**
|
||||
* @param propertySources
|
||||
*/
|
||||
public void setPropertySources(PropertySources propertySources) {
|
||||
this.propertySources = propertySources;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validator the validator to set
|
||||
*/
|
||||
public void setValidator(Validator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param conversionService the conversionService to set
|
||||
*/
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
|
||||
bean.getClass(), ConfigurationProperties.class);
|
||||
if (annotation != null || bean instanceof ConfigurationPropertiesHolder) {
|
||||
postProcessAfterInitialization(bean, beanName, annotation);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void postProcessAfterInitialization(Object bean, String beanName,
|
||||
ConfigurationProperties annotation) {
|
||||
Object target = (bean instanceof ConfigurationPropertiesHolder ? ((ConfigurationPropertiesHolder) bean)
|
||||
.getTarget() : bean);
|
||||
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
|
||||
target);
|
||||
factory.setPropertySources(this.propertySources);
|
||||
factory.setValidator(this.validator);
|
||||
// If no explicit conversion service is provided we add one so that (at least)
|
||||
// comma-separated arrays of convertibles can be bound automatically
|
||||
factory.setConversionService(this.conversionService == null ? getDefaultConversionService()
|
||||
: this.conversionService);
|
||||
if (annotation != null) {
|
||||
factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
|
||||
factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
|
||||
String targetName = (StringUtils.hasLength(annotation.value()) ? annotation
|
||||
.value() : annotation.name());
|
||||
if (StringUtils.hasLength(targetName)) {
|
||||
factory.setTargetName(targetName);
|
||||
}
|
||||
}
|
||||
try {
|
||||
factory.bindPropertiesToTarget();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new BeanCreationException(beanName, "Could not bind properties", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private ConversionService getDefaultConversionService() {
|
||||
if (!this.initialized && this.beanFactory instanceof ListableBeanFactory) {
|
||||
for (Converter<?, ?> converter : ((ListableBeanFactory) this.beanFactory)
|
||||
.getBeansOfType(Converter.class).values()) {
|
||||
this.defaultConversionService.addConverter(converter);
|
||||
}
|
||||
}
|
||||
return this.defaultConversionService;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
/**
|
||||
* Properties holder registered by {@link EnableConfigurationPropertiesImportSelector} to
|
||||
* be picked up by {@link ConfigurationPropertiesBindingPostProcessor}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class ConfigurationPropertiesHolder {
|
||||
|
||||
private Object target;
|
||||
|
||||
public ConfigurationPropertiesHolder(Object target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Enable support for {@link ConfigurationProperties} annotated beans.
|
||||
* {@link ConfigurationProperties} beans can be registered in the standard way (for
|
||||
* example using {@link Bean @Bean} methods) or, for convenience, can be specified
|
||||
* directly on this annotation.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(EnableConfigurationPropertiesImportSelector.class)
|
||||
public @interface EnableConfigurationProperties {
|
||||
|
||||
/**
|
||||
* Convenient way to quickly register {@link ConfigurationProperties} beans with
|
||||
* Spring. Standard Spring Beans will also be scanned regardless of this value.
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.properties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Import selector that sets up binding of external properties to configuration classes
|
||||
* (see {@link ConfigurationProperties}). It either registers a
|
||||
* {@link ConfigurationProperties} bean or not, depending on whether the enclosing
|
||||
* {@link EnableConfigurationProperties} explicitly declares one. If none is declared then
|
||||
* a bean post processor will still kick in for any beans annotated as external
|
||||
* configuration. If one is declared then it a bean definition is registered with id equal
|
||||
* to the class name (thus an application context usually only contains one
|
||||
* {@link ConfigurationProperties} bean of each unique type).
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata metadata) {
|
||||
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
|
||||
EnableConfigurationProperties.class.getName(), false);
|
||||
Object[] type = (Object[]) attributes.getFirst("value");
|
||||
if (type == null || type.length == 0) {
|
||||
return new String[] { ConfigurationPropertiesBindingConfiguration.class
|
||||
.getName() };
|
||||
}
|
||||
return new String[] { ConfigurationPropertiesBeanRegistrar.class.getName(),
|
||||
ConfigurationPropertiesBindingConfiguration.class.getName() };
|
||||
}
|
||||
|
||||
public static class ConfigurationPropertiesBeanRegistrar implements
|
||||
ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
MultiValueMap<String, Object> attributes = metadata
|
||||
.getAllAnnotationAttributes(
|
||||
EnableConfigurationProperties.class.getName(), false);
|
||||
List<Class<?>> types = collectClasses(attributes.get("value"));
|
||||
for (Class<?> type : types) {
|
||||
String name = type.getName();
|
||||
if (!registry.containsBeanDefinition(name)) {
|
||||
registerBeanDefinition(registry, type, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Class<?>> collectClasses(List<Object> list) {
|
||||
ArrayList<Class<?>> result = new ArrayList<Class<?>>();
|
||||
for (Object object : list) {
|
||||
for (Object value : (Object[]) object) {
|
||||
if (value instanceof Class && value != void.class) {
|
||||
result.add((Class<?>) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void registerBeanDefinition(BeanDefinitionRegistry registry,
|
||||
Class<?> type, String name) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(type);
|
||||
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
|
||||
registry.registerBeanDefinition(name, beanDefinition);
|
||||
|
||||
ConfigurationProperties properties = AnnotationUtils.findAnnotation(type,
|
||||
ConfigurationProperties.class);
|
||||
if (properties == null) {
|
||||
registerPropertiesHolder(registry, name);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerPropertiesHolder(BeanDefinitionRegistry registry, String name) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(ConfigurationPropertiesHolder.class);
|
||||
builder.addConstructorArgReference(name);
|
||||
registry.registerBeanDefinition(name + ".HOLDER", builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.logging;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.SystemPropertyUtils;
|
||||
|
||||
/**
|
||||
* Logging initializer for {@link Logger java.util.logging}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class JavaLoggerConfigurer {
|
||||
|
||||
/**
|
||||
* Configure the logging system from the specified location (a properties file).
|
||||
*
|
||||
* @param location the location to use to configure logging
|
||||
*/
|
||||
public static void initLogging(String location) throws FileNotFoundException {
|
||||
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
|
||||
try {
|
||||
LogManager.getLogManager().readConfiguration(
|
||||
ResourceUtils.getURL(resolvedLocation).openStream());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (ex instanceof FileNotFoundException) {
|
||||
throw (FileNotFoundException) ex;
|
||||
}
|
||||
throw new IllegalArgumentException("Could not initialize logging from "
|
||||
+ location, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.logging;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.slf4j.impl.StaticLoggerBinder;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.SystemPropertyUtils;
|
||||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.util.ContextInitializer;
|
||||
import ch.qos.logback.core.joran.spi.JoranException;
|
||||
|
||||
/**
|
||||
* Logging initializer for <a href="http://logback.qos.ch">logback</a>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class LogbackConfigurer {
|
||||
|
||||
/**
|
||||
* Configure the logback system from the specified location.
|
||||
*
|
||||
* @param location the location to use to configure logback
|
||||
*/
|
||||
public static void initLogging(String location) throws FileNotFoundException {
|
||||
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
|
||||
URL url = ResourceUtils.getURL(resolvedLocation);
|
||||
LoggerContext context = (LoggerContext) StaticLoggerBinder.getSingleton()
|
||||
.getLoggerFactory();
|
||||
context.stop();
|
||||
try {
|
||||
new ContextInitializer(context).configureByResource(url);
|
||||
}
|
||||
catch (JoranException ex) {
|
||||
throw new IllegalArgumentException("Could not initialize logging from "
|
||||
+ location, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.web;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.web.WebApplicationInitializer;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.zero.SpringApplication;
|
||||
import org.springframework.zero.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
|
||||
|
||||
/**
|
||||
* A handy opinionated {@link WebApplicationInitializer} for applications that only have
|
||||
* one Spring servlet, and no more than a single filter (which itself is only enabled when
|
||||
* Spring Security is detected). If your application is more complicated consider using
|
||||
* one of the other WebApplicationInitializers.
|
||||
*
|
||||
* <p/>
|
||||
* Note that a WebApplicationInitializer is only needed if you are building a war file and
|
||||
* deploying it. If you prefer to run an embedded container (we do) then you won't need
|
||||
* this at all.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class SpringServletInitializer implements WebApplicationInitializer {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
public void onStartup(ServletContext servletContext) throws ServletException {
|
||||
WebApplicationContext rootAppContext = this
|
||||
.createRootApplicationContext(servletContext);
|
||||
if (rootAppContext != null) {
|
||||
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent event) {
|
||||
// no-op because the application context is already initialized
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.logger.debug("No ContextLoaderListener registered, as "
|
||||
+ "createRootApplicationContext() did not "
|
||||
+ "return an application context");
|
||||
}
|
||||
}
|
||||
|
||||
protected WebApplicationContext createRootApplicationContext(
|
||||
ServletContext servletContext) {
|
||||
ApplicationContext parent = null;
|
||||
Object object = servletContext
|
||||
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
|
||||
if (object instanceof ApplicationContext) {
|
||||
this.logger.info("Root context already created (using as parent).");
|
||||
parent = (ApplicationContext) object;
|
||||
servletContext.setAttribute(
|
||||
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
|
||||
}
|
||||
SpringApplication application = new SpringApplication(
|
||||
(Object[]) getConfigClasses());
|
||||
AnnotationConfigEmbeddedWebApplicationContext context = new AnnotationConfigEmbeddedWebApplicationContext();
|
||||
context.setParent(parent);
|
||||
context.setServletContext(servletContext);
|
||||
application.setApplicationContext(context);
|
||||
return (WebApplicationContext) application.run();
|
||||
}
|
||||
|
||||
protected abstract Class<?>[] getConfigClasses();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Application Context Initializers
|
||||
org.springframework.context.ApplicationContextInitializer=\
|
||||
org.springframework.zero.context.initializer.ConfigFileApplicationContextInitializer,\
|
||||
org.springframework.zero.context.initializer.ContextIdApplicationContextInitializer,\
|
||||
org.springframework.zero.context.initializer.EnvironmentDelegateApplicationContextInitializer,\
|
||||
org.springframework.zero.context.initializer.LoggingApplicationContextInitializer,\
|
||||
org.springframework.zero.context.initializer.VcapApplicationContextInitializer
|
||||
BIN
spring-bootstrap/src/main/resources/favicon.ico
Normal file
BIN
spring-bootstrap/src/main/resources/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 946 B |
@@ -0,0 +1,18 @@
|
||||
log4j.rootCategory=INFO, CONSOLE, FILE
|
||||
|
||||
PID=????
|
||||
catalina.base=/tmp
|
||||
LOG_PATH=${catalina.base}/logs
|
||||
LOG_FILE=${LOG_PATH}/service.log
|
||||
LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] service%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n
|
||||
|
||||
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
|
||||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.CONSOLE.layout.ConversionPattern=${LOG_PATTERN}
|
||||
|
||||
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.FILE.File=${LOG_FILE}
|
||||
log4j.appender.FILE.MaxFileSize=10MB
|
||||
log4j.appender.FILE.layout = org.apache.log4j.PatternLayout
|
||||
log4j.appender.FILE.layout.ConversionPattern=${LOG_PATTERN}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<property name="LOG_PATTERN" value="[%d{yyyy-MM-dd HH:mm:ss.SSS}] - ${PID:-????} %5p [%t] --- %c{1}: %m%n"/>
|
||||
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-/tmp/}spring.log}"/>
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<encoder>
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
</encoder>
|
||||
<file>${LOG_FILE}</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||
<fileNamePattern>${LOG_FILE}.%i</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<triggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<MaxFileSize>10MB</MaxFileSize>
|
||||
</triggeringPolicy>
|
||||
</appender>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,9 @@
|
||||
handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
|
||||
.level = INFO
|
||||
|
||||
# File Logging
|
||||
java.util.logging.FileHandler.pattern = %t/service.log
|
||||
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
|
||||
java.util.logging.FileHandler.level = INFO
|
||||
java.util.logging.FileHandler.limit = 10485760
|
||||
java.util.logging.FileHandler.count = 10
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.zero.sampleconfig.MyComponent;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link BeanDefinitionLoader}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class BeanDefinitionLoaderTests {
|
||||
|
||||
// FIXME
|
||||
|
||||
private StaticApplicationContext registry;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.registry = new StaticApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadClass() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
MyComponent.class);
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadXmlResource() throws Exception {
|
||||
ClassPathResource resource = new ClassPathResource("sample-beans.xml", getClass());
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry, resource);
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myXmlComponent"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadPackage() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
MyComponent.class.getPackage());
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadClassName() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
MyComponent.class.getName());
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadResourceName() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
"classpath:org/springframework/zero/sample-beans.xml");
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myXmlComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadPackageName() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
MyComponent.class.getPackage().getName());
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myComponent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadPackageAndClassDoesNotDoubleAdd() throws Exception {
|
||||
BeanDefinitionLoader loader = new BeanDefinitionLoader(this.registry,
|
||||
MyComponent.class.getPackage(), MyComponent.class);
|
||||
int loaded = loader.load();
|
||||
assertThat(loaded, equalTo(1));
|
||||
assertTrue(this.registry.containsBean("myComponent"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.web.context.support.StaticWebApplicationContext;
|
||||
import org.springframework.zero.BeanDefinitionLoader;
|
||||
import org.springframework.zero.CommandLineRunner;
|
||||
import org.springframework.zero.ExitCodeGenerator;
|
||||
import org.springframework.zero.SpringApplication;
|
||||
import org.springframework.zero.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
|
||||
import org.springframework.zero.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringApplication}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringApplicationTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private Environment getEnvironment() {
|
||||
if (this.context instanceof ConfigurableApplicationContext) {
|
||||
return ((ConfigurableApplicationContext) this.context).getEnvironment();
|
||||
}
|
||||
throw new IllegalStateException("No Environment available");
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
if (this.context instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) this.context).close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourcesMustNotBeNull() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Sources must not be empty");
|
||||
new SpringApplication((Object[]) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourcesMustNotBeEmpty() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Sources must not be empty");
|
||||
new SpringApplication();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableBanner() throws Exception {
|
||||
SpringApplication application = spy(new SpringApplication(ExampleConfig.class));
|
||||
application.setWebEnvironment(false);
|
||||
application.setShowBanner(false);
|
||||
application.run();
|
||||
verify(application, never()).printBanner();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customBanner() throws Exception {
|
||||
SpringApplication application = spy(new SpringApplication(ExampleConfig.class));
|
||||
application.setWebEnvironment(false);
|
||||
application.run();
|
||||
verify(application).printBanner();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specificApplicationContext() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
ApplicationContext applicationContext = new StaticApplicationContext();
|
||||
application.setApplicationContext(applicationContext);
|
||||
this.context = application.run();
|
||||
assertThat(this.context, sameInstance(applicationContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specificApplicationContextClass() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setApplicationContextClass(StaticApplicationContext.class);
|
||||
this.context = application.run();
|
||||
assertThat(this.context, instanceOf(StaticApplicationContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void specificApplicationContextInitializer() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
final AtomicReference<ApplicationContext> reference = new AtomicReference<ApplicationContext>();
|
||||
application
|
||||
.addInitializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext context) {
|
||||
reference.set(context);
|
||||
}
|
||||
});
|
||||
this.context = application.run("--foo=bar");
|
||||
assertThat(this.context, sameInstance(reference.get()));
|
||||
// Custom initializers do not switch off the defaults
|
||||
assertThat(getEnvironment().getProperty("foo"), equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultApplicationContext() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
this.context = application.run();
|
||||
assertThat(this.context, instanceOf(AnnotationConfigApplicationContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultApplicationContextForWeb() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleWebConfig.class);
|
||||
application.setWebEnvironment(true);
|
||||
this.context = application.run();
|
||||
assertThat(this.context,
|
||||
instanceOf(AnnotationConfigEmbeddedWebApplicationContext.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customEnvironment() throws Exception {
|
||||
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
StaticApplicationContext applicationContext = spy(new StaticApplicationContext());
|
||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
application.setApplicationContext(applicationContext);
|
||||
application.setEnvironment(environment);
|
||||
application.run();
|
||||
verify(applicationContext).setEnvironment(environment);
|
||||
verify(application.getLoader()).setEnvironment(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customResourceLoader() throws Exception {
|
||||
TestSpringApplication application = new TestSpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
StaticApplicationContext applicationContext = spy(new StaticApplicationContext());
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
application.setApplicationContext(applicationContext);
|
||||
application.setResourceLoader(resourceLoader);
|
||||
application.run();
|
||||
verify(applicationContext).setResourceLoader(resourceLoader);
|
||||
verify(application.getLoader()).setResourceLoader(resourceLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customResourceLoaderFromConstructor() throws Exception {
|
||||
ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||
TestSpringApplication application = new TestSpringApplication(resourceLoader,
|
||||
ExampleWebConfig.class);
|
||||
StaticApplicationContext applicationContext = spy(new StaticApplicationContext());
|
||||
application.setApplicationContext(applicationContext);
|
||||
application.run();
|
||||
verify(applicationContext).setResourceLoader(resourceLoader);
|
||||
verify(application.getLoader()).setResourceLoader(resourceLoader);
|
||||
applicationContext.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customBeanNameGenerator() throws Exception {
|
||||
TestSpringApplication application = new TestSpringApplication(
|
||||
ExampleWebConfig.class);
|
||||
StaticWebApplicationContext applicationContext = spy(new StaticWebApplicationContext());
|
||||
applicationContext.setServletContext(new MockServletContext());
|
||||
BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
|
||||
application.setApplicationContext(applicationContext);
|
||||
application.setBeanNameGenerator(beanNameGenerator);
|
||||
this.context = application.run();
|
||||
verify(application.getLoader()).setBeanNameGenerator(beanNameGenerator);
|
||||
assertThat(
|
||||
this.context
|
||||
.getBean(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR),
|
||||
sameInstance((Object) beanNameGenerator));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commandLinePropertySource() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
application.setEnvironment(environment);
|
||||
application.run();
|
||||
assertThat(
|
||||
hasPropertySource(environment, MapPropertySource.class, "commandLineArgs"),
|
||||
equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableCommandLinePropertySource() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
application.setAddCommandLineProperties(false);
|
||||
ConfigurableEnvironment environment = new StandardEnvironment();
|
||||
application.setEnvironment(environment);
|
||||
application.run();
|
||||
assertThat(
|
||||
hasPropertySource(environment, MapPropertySource.class, "commandLineArgs"),
|
||||
equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runCommandLineRunners() throws Exception {
|
||||
SpringApplication application = new SpringApplication(CommandLineRunConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
this.context = application.run("arg");
|
||||
assertTrue(this.context.getBean("runnerA", TestCommandLineRunner.class).hasRun());
|
||||
assertTrue(this.context.getBean("runnerB", TestCommandLineRunner.class).hasRun());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadSources() throws Exception {
|
||||
Object[] sources = { ExampleConfig.class, "a", TestCommandLineRunner.class };
|
||||
TestSpringApplication application = new TestSpringApplication(sources);
|
||||
application.setWebEnvironment(false);
|
||||
application.setUseMockLoader(true);
|
||||
application.run();
|
||||
assertThat(application.getSources(), equalTo(sources));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void run() throws Exception {
|
||||
this.context = SpringApplication.run(ExampleWebConfig.class);
|
||||
assertNotNull(this.context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runComponents() throws Exception {
|
||||
this.context = SpringApplication.run(new Object[] { ExampleWebConfig.class,
|
||||
Object.class }, new String[0]);
|
||||
assertNotNull(this.context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exit() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
ApplicationContext context = application.run();
|
||||
assertNotNull(context);
|
||||
assertEquals(0, SpringApplication.exit(context));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exitWithExplicitCode() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setWebEnvironment(false);
|
||||
ApplicationContext context = application.run();
|
||||
assertNotNull(context);
|
||||
assertEquals(2, SpringApplication.exit(context, new ExitCodeGenerator() {
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
return 2;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCommandLineArgs() throws Exception {
|
||||
SpringApplication application = new SpringApplication(ExampleConfig.class);
|
||||
application.setDefaultCommandLineArgs("--foo", "--bar=spam", "bucket");
|
||||
application.setWebEnvironment(false);
|
||||
this.context = application.run("--bar=foo", "bucket", "crap");
|
||||
assertThat(this.context, instanceOf(AnnotationConfigApplicationContext.class));
|
||||
assertThat(getEnvironment().getProperty("bar"), equalTo("foo"));
|
||||
assertThat(getEnvironment().getProperty("foo"), equalTo(""));
|
||||
}
|
||||
|
||||
private boolean hasPropertySource(ConfigurableEnvironment environment,
|
||||
Class<?> propertySourceClass, String name) {
|
||||
for (PropertySource<?> source : environment.getPropertySources()) {
|
||||
if (propertySourceClass.isInstance(source)
|
||||
&& (name == null || name.equals(source.getName()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME test initializers
|
||||
|
||||
// FIXME test config files?
|
||||
|
||||
private static class TestSpringApplication extends SpringApplication {
|
||||
|
||||
private BeanDefinitionLoader loader;
|
||||
|
||||
private boolean useMockLoader;
|
||||
|
||||
private Object[] sources;
|
||||
|
||||
public TestSpringApplication(Object... sources) {
|
||||
super(sources);
|
||||
}
|
||||
|
||||
public TestSpringApplication(ResourceLoader resourceLoader, Object... sources) {
|
||||
super(resourceLoader, sources);
|
||||
}
|
||||
|
||||
public void setUseMockLoader(boolean useMockLoader) {
|
||||
this.useMockLoader = useMockLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BeanDefinitionLoader createBeanDefinitionLoader(
|
||||
BeanDefinitionRegistry registry, Object[] sources) {
|
||||
this.sources = sources;
|
||||
if (this.useMockLoader) {
|
||||
this.loader = mock(BeanDefinitionLoader.class);
|
||||
}
|
||||
else {
|
||||
this.loader = spy(super.createBeanDefinitionLoader(registry, sources));
|
||||
}
|
||||
return this.loader;
|
||||
}
|
||||
|
||||
public BeanDefinitionLoader getLoader() {
|
||||
return this.loader;
|
||||
}
|
||||
|
||||
public Object[] getSources() {
|
||||
return this.sources;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ExampleConfig {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ExampleWebConfig {
|
||||
|
||||
@Bean
|
||||
public JettyEmbeddedServletContainerFactory container() {
|
||||
return new JettyEmbeddedServletContainerFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class CommandLineRunConfig {
|
||||
|
||||
@Bean
|
||||
public TestCommandLineRunner runnerB() {
|
||||
return new TestCommandLineRunner(Ordered.LOWEST_PRECEDENCE, "runnerA");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestCommandLineRunner runnerA() {
|
||||
return new TestCommandLineRunner(Ordered.HIGHEST_PRECEDENCE);
|
||||
}
|
||||
}
|
||||
|
||||
static class TestCommandLineRunner implements CommandLineRunner,
|
||||
ApplicationContextAware, Ordered {
|
||||
|
||||
private String[] expectedBefore;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String[] args;
|
||||
|
||||
private int order;
|
||||
|
||||
public TestCommandLineRunner(int order, String... expectedBefore) {
|
||||
this.expectedBefore = expectedBefore;
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext)
|
||||
throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
this.args = args;
|
||||
for (String name : this.expectedBefore) {
|
||||
TestCommandLineRunner bean = this.applicationContext.getBean(name,
|
||||
TestCommandLineRunner.class);
|
||||
assertTrue(bean.hasRun());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRun() {
|
||||
return this.args != null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
/**
|
||||
* General test utilities.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public abstract class TestUtils {
|
||||
|
||||
public static void addEnviroment(ConfigurableApplicationContext context,
|
||||
String... pairs) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
for (String pair : pairs) {
|
||||
int index = pair.indexOf(":");
|
||||
String key = pair.substring(0, index > 0 ? index : pair.length());
|
||||
String value = index > 0 ? pair.substring(index + 1) : "";
|
||||
map.put(key, value);
|
||||
}
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test", map));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
import org.springframework.zero.bind.PropertiesConfigurationFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertiesConfigurationFactory}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertiesConfigurationFactoryTests {
|
||||
|
||||
private PropertiesConfigurationFactory<Foo> factory;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private boolean exceptionIfInvalid = true;
|
||||
|
||||
private boolean ignoreUnknownFields = true;
|
||||
|
||||
private String targetName = null;
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesLoadsWithNoErrors() throws Exception {
|
||||
Foo foo = createFoo("name: blah\nbar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesLoadsWithUpperCase() throws Exception {
|
||||
Foo foo = createFoo("NAME: blah\nbar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesLoadsWithDash() throws Exception {
|
||||
Foo foo = createFoo("na-me: blah\nbar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownPropertyOkByDefault() throws Exception {
|
||||
Foo foo = createFoo("hi: hello\nname: foo\nbar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
}
|
||||
|
||||
@Test(expected = NotWritablePropertyException.class)
|
||||
public void testUnknownPropertyCausesLoadFailure() throws Exception {
|
||||
this.ignoreUnknownFields = false;
|
||||
createFoo("hi: hello\nname: foo\nbar: blah");
|
||||
}
|
||||
|
||||
@Test(expected = BindException.class)
|
||||
public void testMissingPropertyCausesValidationError() throws Exception {
|
||||
this.validator = new SpringValidatorAdapter(Validation
|
||||
.buildDefaultValidatorFactory().getValidator());
|
||||
createFoo("bar: blah");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindToNamedTarget() throws Exception {
|
||||
this.targetName = "foo";
|
||||
Foo foo = createFoo("hi: hello\nfoo.name: foo\nfoo.bar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
}
|
||||
|
||||
private Foo createFoo(final String values) throws Exception {
|
||||
this.factory = new PropertiesConfigurationFactory<Foo>(Foo.class);
|
||||
this.factory.setProperties(PropertiesLoaderUtils
|
||||
.loadProperties(new ByteArrayResource(values.getBytes())));
|
||||
this.factory.setExceptionIfInvalid(this.exceptionIfInvalid);
|
||||
this.factory.setValidator(this.validator);
|
||||
this.factory.setTargetName(this.targetName);
|
||||
this.factory.setIgnoreUnknownFields(this.ignoreUnknownFields);
|
||||
this.factory.setMessageSource(new StaticMessageSource());
|
||||
this.factory.afterPropertiesSet();
|
||||
return this.factory.getObject();
|
||||
}
|
||||
|
||||
// Foo needs to be public and to have setters for all properties
|
||||
public static class Foo {
|
||||
@NotNull
|
||||
private String name;
|
||||
|
||||
private String bar;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return this.bar;
|
||||
}
|
||||
|
||||
public void setBar(String bar) {
|
||||
this.bar = bar;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertySourcesPropertyValues}.
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class PropertySourcesPropertyValuesTests {
|
||||
|
||||
private MutablePropertySources propertySources = new MutablePropertySources();
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
this.propertySources.addFirst(new PropertySource<String>("static", "foo") {
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
if (name.equals(getSource())) {
|
||||
return "bar";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
this.propertySources.addFirst(new MapPropertySource("map", Collections
|
||||
.<String, Object> singletonMap("name", "${foo}")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSize() {
|
||||
PropertySourcesPropertyValues propertyValues = new PropertySourcesPropertyValues(
|
||||
this.propertySources);
|
||||
assertEquals(1, propertyValues.getPropertyValues().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonEnumeratedValue() {
|
||||
PropertySourcesPropertyValues propertyValues = new PropertySourcesPropertyValues(
|
||||
this.propertySources);
|
||||
assertEquals("bar", propertyValues.getPropertyValue("foo").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnumeratedValue() {
|
||||
PropertySourcesPropertyValues propertyValues = new PropertySourcesPropertyValues(
|
||||
this.propertySources);
|
||||
assertEquals("bar", propertyValues.getPropertyValue("name").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverriddenValue() {
|
||||
this.propertySources.addFirst(new MapPropertySource("new", Collections
|
||||
.<String, Object> singletonMap("name", "spam")));
|
||||
PropertySourcesPropertyValues propertyValues = new PropertySourcesPropertyValues(
|
||||
this.propertySources);
|
||||
assertEquals("spam", propertyValues.getPropertyValue("name").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceholdersBinding() {
|
||||
TestBean target = new TestBean();
|
||||
DataBinder binder = new DataBinder(target);
|
||||
binder.bind(new PropertySourcesPropertyValues(this.propertySources));
|
||||
assertEquals("bar", target.getName());
|
||||
}
|
||||
|
||||
public static class TestBean {
|
||||
private String name;
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.Payload;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.DataBinder;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
import org.springframework.zero.bind.RelaxedDataBinder;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
/**
|
||||
* Tests for {@link RelaxedDataBinder}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class RelaxedDataBinderTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
private ConversionService conversionService;
|
||||
|
||||
@Test
|
||||
public void testBindString() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
bind(target, "foo: bar");
|
||||
assertEquals("bar", target.getFoo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindUnderscore() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
bind(target, "foo-bar: bar");
|
||||
assertEquals("bar", target.getFoo_bar());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindCamelCase() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
bind(target, "foo-baz: bar");
|
||||
assertEquals("bar", target.getFooBaz());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNumber() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
bind(target, "foo: bar\n" + "value: 123");
|
||||
assertEquals(123, target.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleValidation() throws Exception {
|
||||
ValidatedTarget target = new ValidatedTarget();
|
||||
BindingResult result = bind(target, "");
|
||||
assertEquals(1, result.getErrorCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredFieldsValidation() throws Exception {
|
||||
TargetWithValidatedMap target = new TargetWithValidatedMap();
|
||||
BindingResult result = bind(target, "info[foo]: bar");
|
||||
assertEquals(2, result.getErrorCount());
|
||||
for (FieldError error : result.getFieldErrors()) {
|
||||
System.err.println(new StaticMessageSource().getMessage(error,
|
||||
Locale.getDefault()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNested() throws Exception {
|
||||
TargetWithNestedObject target = new TargetWithNestedObject();
|
||||
bind(target, "nested.foo: bar\n" + "nested.value: 123");
|
||||
assertEquals(123, target.getNested().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedList() throws Exception {
|
||||
TargetWithNestedList target = new TargetWithNestedList();
|
||||
bind(target, "nested: bar,foo");
|
||||
bind(target, "nested[0]: bar");
|
||||
bind(target, "nested[1]: foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedListCommaDelimitedONly() throws Exception {
|
||||
TargetWithNestedList target = new TargetWithNestedList();
|
||||
this.conversionService = new DefaultConversionService();
|
||||
bind(target, "nested: bar,foo");
|
||||
assertEquals("[bar, foo]", target.getNested().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedMap() throws Exception {
|
||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
||||
bind(target, "nested.foo: bar\n" + "nested.value: 123");
|
||||
assertEquals("123", target.getNested().get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindNestedMapBracketReferenced() throws Exception {
|
||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
||||
bind(target, "nested[foo]: bar\n" + "nested[value]: 123");
|
||||
assertEquals("123", target.getNested().get("value"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testBindDoubleNestedMap() throws Exception {
|
||||
TargetWithNestedMap target = new TargetWithNestedMap();
|
||||
bind(target, "nested.foo: bar\n" + "nested.bar.spam: bucket\n"
|
||||
+ "nested.bar.value: 123\nnested.bar.foo: crap");
|
||||
assertEquals(2, target.getNested().size());
|
||||
assertEquals(3, ((Map<String, Object>) target.getNested().get("bar")).size());
|
||||
assertEquals("123",
|
||||
((Map<String, Object>) target.getNested().get("bar")).get("value"));
|
||||
assertEquals("bar", target.getNested().get("foo"));
|
||||
assertFalse(target.getNested().containsValue(target.getNested()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindErrorTypeMismatch() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
BindingResult result = bind(target, "foo: bar\n" + "value: foo");
|
||||
assertEquals(1, result.getErrorCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindErrorNotWritable() throws Exception {
|
||||
this.expected.expectMessage("property 'spam'");
|
||||
this.expected.expectMessage("not writable");
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
BindingResult result = bind(target, "spam: bar\n" + "value: 123");
|
||||
assertEquals(1, result.getErrorCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindErrorNotWritableWithPrefix() throws Exception {
|
||||
VanillaTarget target = new VanillaTarget();
|
||||
BindingResult result = bind(target, "spam: bar\n" + "vanilla.value: 123",
|
||||
"vanilla");
|
||||
assertEquals(0, result.getErrorCount());
|
||||
assertEquals(123, target.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindMap() throws Exception {
|
||||
Map<String, Object> target = new LinkedHashMap<String, Object>();
|
||||
BindingResult result = bind(target, "spam: bar\n" + "vanilla.value: 123",
|
||||
"vanilla");
|
||||
assertEquals(0, result.getErrorCount());
|
||||
assertEquals("123", target.get("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindMapNestedInMap() throws Exception {
|
||||
Map<String, Object> target = new LinkedHashMap<String, Object>();
|
||||
BindingResult result = bind(target, "spam: bar\n" + "vanilla.foo.value: 123",
|
||||
"vanilla");
|
||||
assertEquals(0, result.getErrorCount());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> map = (Map<String, Object>) target.get("foo");
|
||||
assertEquals("123", map.get("value"));
|
||||
}
|
||||
|
||||
private BindingResult bind(Object target, String values) throws Exception {
|
||||
return bind(target, values, null);
|
||||
}
|
||||
|
||||
private BindingResult bind(Object target, String values, String namePrefix)
|
||||
throws Exception {
|
||||
Properties properties = PropertiesLoaderUtils
|
||||
.loadProperties(new ByteArrayResource(values.getBytes()));
|
||||
DataBinder binder = new RelaxedDataBinder(target, namePrefix);
|
||||
binder.setIgnoreUnknownFields(false);
|
||||
LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
|
||||
validatorFactoryBean.afterPropertiesSet();
|
||||
binder.setValidator(validatorFactoryBean);
|
||||
binder.setConversionService(this.conversionService);
|
||||
binder.bind(new MutablePropertyValues(properties));
|
||||
binder.validate();
|
||||
|
||||
return binder.getBindingResult();
|
||||
}
|
||||
|
||||
@Documented
|
||||
@Target({ ElementType.FIELD })
|
||||
@Retention(RUNTIME)
|
||||
@Constraint(validatedBy = RequiredKeysValidator.class)
|
||||
public @interface RequiredKeys {
|
||||
|
||||
String[] value();
|
||||
|
||||
String message() default "Required fields are not provided for field ''{0}''";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
||||
|
||||
public static class RequiredKeysValidator implements
|
||||
ConstraintValidator<RequiredKeys, Map<String, Object>> {
|
||||
|
||||
private String[] fields;
|
||||
|
||||
@Override
|
||||
public void initialize(RequiredKeys constraintAnnotation) {
|
||||
this.fields = constraintAnnotation.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Map<String, Object> value,
|
||||
ConstraintValidatorContext context) {
|
||||
boolean valid = true;
|
||||
for (String field : this.fields) {
|
||||
if (!value.containsKey(field)) {
|
||||
context.buildConstraintViolationWithTemplate(
|
||||
"Missing field ''" + field + "''").addConstraintViolation();
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TargetWithValidatedMap {
|
||||
|
||||
@RequiredKeys({ "foo", "value" })
|
||||
private Map<String, Object> info;
|
||||
|
||||
public Map<String, Object> getInfo() {
|
||||
return this.info;
|
||||
}
|
||||
|
||||
public void setInfo(Map<String, Object> nested) {
|
||||
this.info = nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithNestedMap {
|
||||
private Map<String, Object> nested;
|
||||
|
||||
public Map<String, Object> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
|
||||
public void setNested(Map<String, Object> nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithNestedList {
|
||||
private List<String> nested;
|
||||
|
||||
public List<String> getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
|
||||
public void setNested(List<String> nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TargetWithNestedObject {
|
||||
private VanillaTarget nested;
|
||||
|
||||
public VanillaTarget getNested() {
|
||||
return this.nested;
|
||||
}
|
||||
|
||||
public void setNested(VanillaTarget nested) {
|
||||
this.nested = nested;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VanillaTarget {
|
||||
|
||||
private String foo;
|
||||
|
||||
private int value;
|
||||
|
||||
private String foo_bar;
|
||||
|
||||
private String fooBaz;
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
public String getFoo_bar() {
|
||||
return this.foo_bar;
|
||||
}
|
||||
|
||||
public void setFoo_bar(String foo_bar) {
|
||||
this.foo_bar = foo_bar;
|
||||
}
|
||||
|
||||
public String getFooBaz() {
|
||||
return this.fooBaz;
|
||||
}
|
||||
|
||||
public void setFooBaz(String fooBaz) {
|
||||
this.fooBaz = fooBaz;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ValidatedTarget {
|
||||
|
||||
@NotNull
|
||||
private String foo;
|
||||
|
||||
public String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.bind;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link YamlConfigurationFactory}
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlConfigurationFactoryTests {
|
||||
|
||||
private YamlConfigurationFactory<Foo> factory;
|
||||
|
||||
private Validator validator;
|
||||
|
||||
private Map<Class<?>, Map<String, String>> aliases = new HashMap<Class<?>, Map<String, String>>();
|
||||
|
||||
private Foo createFoo(final String yaml) throws Exception {
|
||||
this.factory = new YamlConfigurationFactory<Foo>(Foo.class);
|
||||
this.factory.setYaml(yaml);
|
||||
this.factory.setExceptionIfInvalid(true);
|
||||
this.factory.setPropertyAliases(this.aliases);
|
||||
this.factory.setValidator(this.validator);
|
||||
this.factory.setMessageSource(new StaticMessageSource());
|
||||
this.factory.afterPropertiesSet();
|
||||
return this.factory.getObject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidYamlLoadsWithNoErrors() throws Exception {
|
||||
Foo foo = createFoo("name: blah\nbar: blah");
|
||||
assertEquals("blah", foo.bar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidYamlWithAliases() throws Exception {
|
||||
this.aliases.put(Foo.class, Collections.singletonMap("foo-name", "name"));
|
||||
Foo foo = createFoo("foo-name: blah\nbar: blah");
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test(expected = YAMLException.class)
|
||||
public void unknownPropertyCausesLoadFailure() throws Exception {
|
||||
createFoo("hi: hello\nname: foo\nbar: blah");
|
||||
}
|
||||
|
||||
@Test(expected = BindException.class)
|
||||
public void missingPropertyCausesValidationError() throws Exception {
|
||||
this.validator = new SpringValidatorAdapter(Validation
|
||||
.buildDefaultValidatorFactory().getValidator());
|
||||
createFoo("bar: blah");
|
||||
}
|
||||
|
||||
private static class Foo {
|
||||
@NotNull
|
||||
public String name;
|
||||
|
||||
public String bar;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link JsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class JacksonParserTests extends SimpleJsonParserTests {
|
||||
|
||||
@Override
|
||||
protected JsonParser getParser() {
|
||||
return new JacksonJsonParser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link SimpleJsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class SimpleJsonParserTests {
|
||||
|
||||
private JsonParser parser = getParser();
|
||||
|
||||
protected JsonParser getParser() {
|
||||
return new SimpleJsonParser();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleMap() {
|
||||
Map<String, Object> map = this.parser.parseMap("{\"foo\":\"bar\",\"spam\":1}");
|
||||
assertEquals(2, map.size());
|
||||
assertEquals("bar", map.get("foo"));
|
||||
assertEquals("1", map.get("spam").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMap() {
|
||||
Map<String, Object> map = this.parser.parseMap("{}");
|
||||
assertEquals(0, map.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleList() {
|
||||
List<Object> list = this.parser.parseList("[\"foo\",\"bar\",1]");
|
||||
assertEquals(3, list.size());
|
||||
assertEquals("bar", list.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyList() {
|
||||
List<Object> list = this.parser.parseList("[]");
|
||||
assertEquals(0, list.size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testListOfMaps() {
|
||||
List<Object> list = this.parser
|
||||
.parseList("[{\"foo\":\"bar\",\"spam\":1},{\"foo\":\"baz\",\"spam\":2}]");
|
||||
assertEquals(2, list.size());
|
||||
assertEquals(2, ((Map<String, Object>) list.get(1)).size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link YamlJsonParser}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlJsonParserTests extends SimpleJsonParserTests {
|
||||
|
||||
@Override
|
||||
protected JsonParser getParser() {
|
||||
return new YamlJsonParser();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.zero.config.YamlProcessor.ResolutionMethod;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link YamlMapFactoryBean}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlMapFactoryBeanTests {
|
||||
|
||||
private YamlMapFactoryBean factory = new YamlMapFactoryBean();
|
||||
|
||||
@Test
|
||||
public void testSetIgnoreResourceNotFound() throws Exception {
|
||||
this.factory
|
||||
.setResolutionMethod(YamlMapFactoryBean.ResolutionMethod.OVERRIDE_AND_IGNORE);
|
||||
this.factory.setResources(new FileSystemResource[] { new FileSystemResource(
|
||||
"non-exsitent-file.yml") });
|
||||
assertEquals(0, this.factory.getObject().size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testSetBarfOnResourceNotFound() throws Exception {
|
||||
this.factory.setResources(new FileSystemResource[] { new FileSystemResource(
|
||||
"non-exsitent-file.yml") });
|
||||
assertEquals(0, this.factory.getObject().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetObject() throws Exception {
|
||||
this.factory.setResources(new ByteArrayResource[] { new ByteArrayResource(
|
||||
"foo: bar".getBytes()) });
|
||||
assertEquals(1, this.factory.getObject().size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testOverrideAndremoveDefaults() throws Exception {
|
||||
this.factory.setResources(new ByteArrayResource[] {
|
||||
new ByteArrayResource("foo:\n bar: spam".getBytes()),
|
||||
new ByteArrayResource("foo:\n spam: bar".getBytes()) });
|
||||
assertEquals(1, this.factory.getObject().size());
|
||||
assertEquals(2,
|
||||
((Map<String, Object>) this.factory.getObject().get("foo")).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFirstFound() throws Exception {
|
||||
this.factory.setResolutionMethod(ResolutionMethod.FIRST_FOUND);
|
||||
this.factory.setResources(new Resource[] { new AbstractResource() {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "non-existent";
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new IOException("planned");
|
||||
}
|
||||
}, new ByteArrayResource("foo:\n spam: bar".getBytes()) });
|
||||
assertEquals(1, this.factory.getObject().size());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.zero.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.zero.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.zero.config.YamlProcessor.ResolutionMethod;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Tests for {@link YamlPropertiesFactoryBean}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class YamlPropertiesFactoryBeanTests {
|
||||
|
||||
@Test
|
||||
public void testLoadResource() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo: bar\nspam:\n foo: baz".getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bar", properties.get("foo"));
|
||||
assertEquals("baz", properties.get("spam.foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourcesWithOverride() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] {
|
||||
new ByteArrayResource("foo: bar\nspam:\n foo: baz".getBytes()),
|
||||
new ByteArrayResource("foo:\n bar: spam".getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bar", properties.get("foo"));
|
||||
assertEquals("baz", properties.get("spam.foo"));
|
||||
assertEquals("spam", properties.get("foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// We can't fail on duplicate keys because the Map is created by the YAML library
|
||||
public void testLoadResourcesWithInternalOverride() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo: bar\nspam:\n foo: baz\nfoo: bucket".getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bar", properties.get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// We can't fail on duplicate keys because the Map is created by the YAML library
|
||||
public void testLoadResourcesWithNestedInternalOverride() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo:\n bar: spam\n foo: baz\nbreak: it\nfoo: bucket".getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("spam", properties.get("foo.bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceWithMultipleDocuments() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo: bar\nspam: baz\n---\nfoo: bag".getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bag", properties.get("foo"));
|
||||
assertEquals("baz", properties.get("spam"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceWithSelectedDocuments() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) });
|
||||
factory.setDocumentMatchers(Arrays
|
||||
.<DocumentMatcher> asList(new DocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
|
||||
: MatchStatus.NOT_FOUND;
|
||||
}
|
||||
}));
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bag", properties.get("foo"));
|
||||
assertEquals("bad", properties.get("spam"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceWithDefaultMatch() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setMatchDefault(true);
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) });
|
||||
factory.setDocumentMatchers(Arrays
|
||||
.<DocumentMatcher> asList(new DocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
if (!properties.containsKey("foo")) {
|
||||
return MatchStatus.ABSTAIN;
|
||||
}
|
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
|
||||
: MatchStatus.NOT_FOUND;
|
||||
}
|
||||
}));
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bag", properties.get("foo"));
|
||||
assertEquals("bad", properties.get("spam"));
|
||||
assertEquals("two", properties.get("one"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadResourceWithDefaultMatchSkippingMissedMatch() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setMatchDefault(true);
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"one: two\n---\nfoo: bag\nspam: bad\n---\nfoo: bar\nspam: baz".getBytes()) });
|
||||
factory.setDocumentMatchers(Arrays
|
||||
.<DocumentMatcher> asList(new DocumentMatcher() {
|
||||
@Override
|
||||
public MatchStatus matches(Properties properties) {
|
||||
if (!properties.containsKey("foo")) {
|
||||
return MatchStatus.ABSTAIN;
|
||||
}
|
||||
return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
|
||||
: MatchStatus.NOT_FOUND;
|
||||
}
|
||||
}));
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bag", properties.get("foo"));
|
||||
assertEquals("bad", properties.get("spam"));
|
||||
assertEquals("two", properties.get("one"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadNonExistentResource() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResolutionMethod(ResolutionMethod.OVERRIDE_AND_IGNORE);
|
||||
factory.setResources(new Resource[] { new ClassPathResource("no-such-file.yml") });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals(0, properties.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadNull() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource("foo: bar\nspam:"
|
||||
.getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bar", properties.get("foo"));
|
||||
assertEquals("", properties.get("spam"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadArrayOfString() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource("foo:\n- bar\n- baz"
|
||||
.getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
assertEquals("bar", properties.get("foo[0]"));
|
||||
assertEquals("baz", properties.get("foo[1]"));
|
||||
assertEquals("bar,baz", properties.get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadArrayOfObject() throws Exception {
|
||||
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
|
||||
factory.setResources(new Resource[] { new ByteArrayResource(
|
||||
"foo:\n- bar:\n spam: crap\n- baz\n- one: two\n three: four"
|
||||
.getBytes()) });
|
||||
Properties properties = factory.getObject();
|
||||
// System.err.println(properties);
|
||||
assertEquals("crap", properties.get("foo[0].bar.spam"));
|
||||
assertEquals("baz", properties.get("foo[1]"));
|
||||
assertEquals("two", properties.get("foo[2].one"));
|
||||
assertEquals("four", properties.get("foo[2].three"));
|
||||
assertEquals("{bar={spam=crap}},baz,{one=two, three=four}", properties.get("foo"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testYaml() {
|
||||
Yaml yaml = new Yaml();
|
||||
Map<String, ?> map = yaml.loadAs("foo: bar\nspam:\n foo: baz", Map.class);
|
||||
assertEquals("bar", map.get("foo"));
|
||||
assertEquals("baz", ((Map<String, Object>) map.get("spam")).get("foo"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.zero.context.condition.ConditionalOnMissingBean;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean classes and/or names are
|
||||
* not already contained in the {@link BeanFactory}, and throws an exception otherwise.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnMissingBean
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(AssertMissingBeanCondition.class)
|
||||
public @interface AssertMissingBean {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition matches when each
|
||||
* class specified is missing in the {@link ApplicationContext}.
|
||||
* @return the class types of beans to check
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The names of beans to check. The condition matches when each bean name specified is
|
||||
* missing in the {@link ApplicationContext}.
|
||||
* @return the name of beans to check
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* If the application context hierarchy (parent contexts) should be considered.
|
||||
*/
|
||||
boolean considerHierarchy() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.zero.context.condition.OnMissingBeanCondition;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks that specific beans are missing.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see AssertMissingBean
|
||||
*/
|
||||
class AssertMissingBeanCondition extends OnMissingBeanCondition {
|
||||
|
||||
@Override
|
||||
protected Class<?> annotationClass() {
|
||||
return AssertMissingBean.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata,
|
||||
List<String> beanClasses, List<String> beanNames) throws LinkageError {
|
||||
boolean result = super.matches(context, metadata, beanClasses, beanNames);
|
||||
if (!result) {
|
||||
throw new BeanCreationException("Found existing bean for classes="
|
||||
+ beanClasses + " and names=" + beanNames);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.zero.context.condition.ConditionalOnBean;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Ignore
|
||||
public class OnBeanConditionTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@Test
|
||||
public void testNameOnBeanCondition() {
|
||||
this.context.register(FooConfiguration.class, OnBeanNameConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameOnBeanConditionReverseOrder() {
|
||||
this.context.register(OnBeanNameConfiguration.class, FooConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassOnBeanCondition() {
|
||||
this.context.register(OnBeanClassConfiguration.class, FooConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnBeanConditionWithXml() {
|
||||
this.context.register(OnBeanNameConfiguration.class, XmlConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnBeanConditionWithCombinedXml() {
|
||||
this.context.register(CombinedXmlConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(name = "foo")
|
||||
protected static class OnBeanNameConfiguration {
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnBean(String.class)
|
||||
protected static class OnBeanClassConfiguration {
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class FooConfiguration {
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportResource("org/springframework/zero/context/annotation/foo.xml")
|
||||
protected static class XmlConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(OnBeanNameConfiguration.class)
|
||||
@ImportResource("org/springframework/zero/context/annotation/foo.xml")
|
||||
protected static class CombinedXmlConfiguration {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link OnClassCondition}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class OnClassConditionTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@Test
|
||||
public void testVanillaOnClassCondition() {
|
||||
this.context.register(BasicConfiguration.class, FooConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingOnClassCondition() {
|
||||
this.context.register(MissingConfiguration.class, FooConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertFalse(this.context.containsBean("bar"));
|
||||
assertEquals("foo", this.context.getBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClassConditionWithXml() {
|
||||
this.context.register(BasicConfiguration.class, XmlConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClassConditionWithCombinedXml() {
|
||||
this.context.register(CombinedXmlConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("bar"));
|
||||
assertEquals("bar", this.context.getBean("bar"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(OnClassConditionTests.class)
|
||||
protected static class BasicConfiguration {
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = "FOO")
|
||||
protected static class MissingConfiguration {
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class FooConfiguration {
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ImportResource("org/springframework/zero/context/foo.xml")
|
||||
protected static class XmlConfiguration {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(BasicConfiguration.class)
|
||||
@ImportResource("org/springframework/zero/context/foo.xml")
|
||||
protected static class CombinedXmlConfiguration {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-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.zero.context.condition;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link OnExpressionCondition}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class OnExpressionConditionTests {
|
||||
|
||||
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
|
||||
@Test
|
||||
public void testResourceExists() {
|
||||
this.context.register(BasicConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertTrue(this.context.containsBean("foo"));
|
||||
assertEquals("foo", this.context.getBean("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceNotExists() {
|
||||
this.context.register(MissingConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertFalse(this.context.containsBean("foo"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnExpression("false")
|
||||
protected static class MissingConfiguration {
|
||||
@Bean
|
||||
public String bar() {
|
||||
return "bar";
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnExpression("true")
|
||||
protected static class BasicConfiguration {
|
||||
@Bean
|
||||
public String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user