Refactor actuator package locations

Restructure actuator packages to improve structure. The following
changes have been made:

 - Separate actuator and actuator auto-configuration into different
   modules.
 - Move endpoint code into `spring-boot-actuator`.
 - Move `Endpoint` implementations from a single package into
   technology specific packages.
 - Move `HealthIndicator` implementations from a single package into
   technology specific packages.
 - As much as possible attempt to mirror the `spring-boot` package
   structure and class naming in `spring-boot-actuator` and
   `spring-boot-actuator-autoconfigure`.
 - Move `DataSourceBuilder` and DataSource meta-data support from
   `spring-boot-actuator` to `spring-boot`.

Fixes gh-10261
This commit is contained in:
Phillip Webb
2017-08-31 22:19:44 -07:00
parent 0f99b29b1a
commit 2e51b48cd9
501 changed files with 8340 additions and 4736 deletions

View File

@@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

View File

@@ -1,149 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.jdbc;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.util.ClassUtils;
/**
* Convenience class for building a {@link DataSource} with common implementations and
* properties. If HikariCP, Tomcat or Commons DBCP are on the classpath one of them will
* be selected (in that order with Hikari first). In the interest of a uniform interface,
* and so that there can be a fallback to an embedded database if one can be detected on
* the classpath, only a small set of common configuration properties are supported. To
* inject additional properties into the result you can downcast it, or use
* {@code @ConfigurationProperties}.
*
* @author Dave Syer
* @author Madhura Bhave
* @since 1.1.0
*/
public class DataSourceBuilder {
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] {
"com.zaxxer.hikari.HikariDataSource",
"org.apache.tomcat.jdbc.pool.DataSource",
"org.apache.commons.dbcp2.BasicDataSource" };
private Class<? extends DataSource> type;
private ClassLoader classLoader;
private Map<String, String> properties = new HashMap<>();
public static DataSourceBuilder create() {
return new DataSourceBuilder(null);
}
public static DataSourceBuilder create(ClassLoader classLoader) {
return new DataSourceBuilder(classLoader);
}
public DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public DataSource build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return result;
}
private void maybeGetDriverClassName() {
if (!this.properties.containsKey("driverClassName")
&& this.properties.containsKey("url")) {
String url = this.properties.get("url");
String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
this.properties.put("driverClassName", driverClass);
}
}
private void bind(DataSource result) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(
this.properties);
ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
aliases.addAliases("url", "jdbc-url");
aliases.addAliases("username", "user");
Binder binder = new Binder(source.withAliases(aliases));
binder.bind(ConfigurationPropertyName.EMPTY, Bindable.ofInstance(result));
}
public DataSourceBuilder type(Class<? extends DataSource> type) {
this.type = type;
return this;
}
public DataSourceBuilder url(String url) {
this.properties.put("url", url);
return this;
}
public DataSourceBuilder driverClassName(String driverClassName) {
this.properties.put("driverClassName", driverClassName);
return this;
}
public DataSourceBuilder username(String username) {
this.properties.put("username", username);
return this;
}
public DataSourceBuilder password(String password) {
this.properties.put("password", password);
return this;
}
@SuppressWarnings("unchecked")
public Class<? extends DataSource> findType() {
if (this.type != null) {
return this.type;
}
for (String name : DATA_SOURCE_TYPE_NAMES) {
try {
return (Class<? extends DataSource>) ClassUtils.forName(name,
this.classLoader);
}
catch (Exception ex) {
// Swallow and continue
}
}
return null;
}
private Class<? extends DataSource> getType() {
Class<? extends DataSource> type = findType();
if (type != null) {
return type;
}
throw new IllegalStateException("No supported DataSource type found");
}
}

View File

@@ -27,6 +27,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.config.ResourceNotFoundException;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource;

View File

@@ -28,6 +28,7 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.context.EnvironmentAware;

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2016 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.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource;
/**
* A base {@link DataSourcePoolMetadata} implementation.
*
* @param <T> the data source type
* @author Stephane Nicoll
* @since 1.2.0
*/
public abstract class AbstractDataSourcePoolMetadata<T extends DataSource>
implements DataSourcePoolMetadata {
private final T dataSource;
/**
* Create an instance with the data source to use.
* @param dataSource the data source
*/
protected AbstractDataSourcePoolMetadata(T dataSource) {
this.dataSource = dataSource;
}
@Override
public Float getUsage() {
Integer maxSize = getMax();
Integer currentSize = getActive();
if (maxSize == null || currentSize == null) {
return null;
}
if (maxSize < 0) {
return -1F;
}
if (currentSize == 0) {
return 0F;
}
return (float) currentSize / (float) maxSize;
}
protected final T getDataSource() {
return this.dataSource;
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-2015 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.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
/**
* {@link DataSourcePoolMetadata} for an Apache Commons DBCP2 {@link DataSource}.
*
* @author Stephane Nicoll
* @since 1.3.1
*/
public class CommonsDbcp2DataSourcePoolMetadata
extends AbstractDataSourcePoolMetadata<BasicDataSource> {
public CommonsDbcp2DataSourcePoolMetadata(BasicDataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
return getDataSource().getNumActive();
}
@Override
public Integer getMax() {
return getDataSource().getMaxTotal();
}
@Override
public Integer getMin() {
return getDataSource().getMinIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getValidationQuery();
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2012-2015 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.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource;
/**
* Provides access meta-data that is commonly available from most pooled
* {@link DataSource} implementations.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public interface DataSourcePoolMetadata {
/**
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
* limited).
* <ul>
* <li>1 means that the maximum number of connections have been allocated</li>
* <li>0 means that no connection is currently active</li>
* <li>-1 means there is not limit to the number of connections that can be allocated
* </li>
* </ul>
* This may also return {@code null} if the data source does not provide the necessary
* information to compute the poll usage.
* @return the usage value or {@code null}
*/
Float getUsage();
/**
* Return the current number of active connections that have been allocated from the
* data source or {@code null} if that information is not available.
* @return the number of active connections or {@code null}
*/
Integer getActive();
/**
* Return the maximum number of active connections that can be allocated at the same
* time or {@code -1} if there is no limit. Can also return {@code null} if that
* information is not available.
* @return the maximum number of active connections or {@code null}
*/
Integer getMax();
/**
* Return the minimum number of idle connections in the pool or {@code null} if that
* information is not available.
* @return the minimum number of active connections or {@code null}
*/
Integer getMin();
/**
* Return the query to use to validate that a connection is valid or {@code null} if
* that information is not available.
* @return the validation query or {@code null}
*/
String getValidationQuery();
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource;
/**
* Provide a {@link DataSourcePoolMetadata} based on a {@link DataSource}.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
@FunctionalInterface
public interface DataSourcePoolMetadataProvider {
/**
* Return the {@link DataSourcePoolMetadata} instance able to manage the specified
* {@link DataSource} or {@code null} if the given data source could not be handled.
* @param dataSource the data source
* @return the data source pool metadata
*/
DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.jdbc.metadata;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
/**
* A {@link DataSourcePoolMetadataProvider} implementation that returns the first
* {@link DataSourcePoolMetadata} that is found by one of its delegate.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class DataSourcePoolMetadataProviders implements DataSourcePoolMetadataProvider {
private final List<DataSourcePoolMetadataProvider> providers;
/**
* Create a {@link DataSourcePoolMetadataProviders} instance with an initial
* collection of delegates to use.
* @param providers the data source pool metadata providers
*/
public DataSourcePoolMetadataProviders(
Collection<? extends DataSourcePoolMetadataProvider> providers) {
this.providers = (providers == null
? Collections.<DataSourcePoolMetadataProvider>emptyList()
: new ArrayList<>(providers));
}
@Override
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider
.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
}
return null;
}
}

View File

@@ -20,6 +20,10 @@ import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.jdbc.metadata.CommonsDbcp2DataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.jdbc.metadata.HikariDataSourcePoolMetadata;
import org.springframework.boot.jdbc.metadata.TomcatDataSourcePoolMetadata;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

View File

@@ -1,69 +0,0 @@
/*
* Copyright 2012-2014 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.boot.autoconfigure.jdbc.metadata;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import org.springframework.beans.DirectFieldAccessor;
/**
* {@link DataSourcePoolMetadata} for a Hikari {@link DataSource}.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class HikariDataSourcePoolMetadata
extends AbstractDataSourcePoolMetadata<HikariDataSource> {
public HikariDataSourcePoolMetadata(HikariDataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
try {
return getHikariPool().getActiveConnections();
}
catch (Exception ex) {
return null;
}
}
private HikariPool getHikariPool() {
return (HikariPool) new DirectFieldAccessor(getDataSource())
.getPropertyValue("pool");
}
@Override
public Integer getMax() {
return getDataSource().getMaximumPoolSize();
}
@Override
public Integer getMin() {
return getDataSource().getMinimumIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getConnectionTestQuery();
}
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright 2012-2015 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.boot.autoconfigure.jdbc.metadata;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
* {@link DataSourcePoolMetadata} for a Tomcat DataSource.
*
* @author Stephane Nicoll
*/
public class TomcatDataSourcePoolMetadata
extends AbstractDataSourcePoolMetadata<DataSource> {
public TomcatDataSourcePoolMetadata(DataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
ConnectionPool pool = getDataSource().getPool();
return (pool == null ? 0 : pool.getActive());
}
@Override
public Integer getMax() {
return getDataSource().getMaxActive();
}
@Override
public Integer getMin() {
return getDataSource().getMinIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getValidationQuery();
}
}

View File

@@ -36,9 +36,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

View File

@@ -23,6 +23,8 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;

View File

@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

View File

@@ -1,212 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.web.servlet.error;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
/**
* Default implementation of {@link ErrorAttributes}. Provides the following attributes
* when possible:
* <ul>
* <li>timestamp - The time that the errors were extracted</li>
* <li>status - The status code</li>
* <li>error - The error reason</li>
* <li>exception - The class name of the root exception (if configured)</li>
* <li>message - The exception message</li>
* <li>errors - Any {@link ObjectError}s from a {@link BindingResult} exception
* <li>trace - The exception stack trace</li>
* <li>path - The URL path when the exception was raised</li>
* </ul>
*
* @author Phillip Webb
* @author Dave Syer
* @author Stephane Nicoll
* @author Vedran Pavic
* @since 1.1.0
* @see ErrorAttributes
*/
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DefaultErrorAttributes
implements ErrorAttributes, HandlerExceptionResolver, Ordered {
private static final String ERROR_ATTRIBUTE = DefaultErrorAttributes.class.getName()
+ ".ERROR";
private final boolean includeException;
/**
* Create a new {@link DefaultErrorAttributes} instance that does not include the
* "exception" attribute.
*/
public DefaultErrorAttributes() {
this(false);
}
/**
* Create a new {@link DefaultErrorAttributes} instance.
* @param includeException whether to include the "exception" attribute
*/
public DefaultErrorAttributes(boolean includeException) {
this.includeException = includeException;
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
storeErrorAttributes(request, ex);
return null;
}
private void storeErrorAttributes(HttpServletRequest request, Exception ex) {
request.setAttribute(ERROR_ATTRIBUTE, ex);
}
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, webRequest);
addErrorDetails(errorAttributes, webRequest, includeStackTrace);
addPath(errorAttributes, webRequest);
return errorAttributes;
}
private void addStatus(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
Integer status = getAttribute(requestAttributes,
"javax.servlet.error.status_code");
if (status == null) {
errorAttributes.put("status", 999);
errorAttributes.put("error", "None");
return;
}
errorAttributes.put("status", status);
try {
errorAttributes.put("error", HttpStatus.valueOf(status).getReasonPhrase());
}
catch (Exception ex) {
// Unable to obtain a reason
errorAttributes.put("error", "Http Status " + status);
}
}
private void addErrorDetails(Map<String, Object> errorAttributes,
WebRequest webRequest, boolean includeStackTrace) {
Throwable error = getError(webRequest);
if (error != null) {
while (error instanceof ServletException && error.getCause() != null) {
error = ((ServletException) error).getCause();
}
if (this.includeException) {
errorAttributes.put("exception", error.getClass().getName());
}
addErrorMessage(errorAttributes, error);
if (includeStackTrace) {
addStackTrace(errorAttributes, error);
}
}
Object message = getAttribute(webRequest, "javax.servlet.error.message");
if ((!StringUtils.isEmpty(message) || errorAttributes.get("message") == null)
&& !(error instanceof BindingResult)) {
errorAttributes.put("message",
StringUtils.isEmpty(message) ? "No message available" : message);
}
}
private void addErrorMessage(Map<String, Object> errorAttributes, Throwable error) {
BindingResult result = extractBindingResult(error);
if (result == null) {
errorAttributes.put("message", error.getMessage());
return;
}
if (result.getErrorCount() > 0) {
errorAttributes.put("errors", result.getAllErrors());
errorAttributes.put("message",
"Validation failed for object='" + result.getObjectName()
+ "'. Error count: " + result.getErrorCount());
}
else {
errorAttributes.put("message", "No errors");
}
}
private BindingResult extractBindingResult(Throwable error) {
if (error instanceof BindingResult) {
return (BindingResult) error;
}
if (error instanceof MethodArgumentNotValidException) {
return ((MethodArgumentNotValidException) error).getBindingResult();
}
return null;
}
private void addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
StringWriter stackTrace = new StringWriter();
error.printStackTrace(new PrintWriter(stackTrace));
stackTrace.flush();
errorAttributes.put("trace", stackTrace.toString());
}
private void addPath(Map<String, Object> errorAttributes,
RequestAttributes requestAttributes) {
String path = getAttribute(requestAttributes, "javax.servlet.error.request_uri");
if (path != null) {
errorAttributes.put("path", path);
}
}
@Override
public Throwable getError(WebRequest webRequest) {
Throwable exception = getAttribute(webRequest, ERROR_ATTRIBUTE);
if (exception == null) {
exception = getAttribute(webRequest, "javax.servlet.error.exception");
}
return exception;
}
@SuppressWarnings("unchecked")
private <T> T getAttribute(RequestAttributes requestAttributes, String name) {
return (T) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.web.servlet.error;
import java.util.Map;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
/**
* Provides access to error attributes which can be logged or presented to the user.
*
* @author Phillip Webb
* @since 1.1.0
* @see DefaultErrorAttributes
*/
public interface ErrorAttributes {
/**
* Returns a {@link Map} of the error attributes. The map can be used as the model of
* an error page {@link ModelAndView}, or returned as a {@link ResponseBody}.
* @param webRequest the source request
* @param includeStackTrace if stack trace elements should be included
* @return a map of error attributes
*/
Map<String, Object> getErrorAttributes(WebRequest webRequest,
boolean includeStackTrace);
/**
* Return the underlying cause of the error or {@code null} if the error cannot be
* extracted.
* @param webRequest the source request
* @return the {@link Exception} that caused the error or {@code null}
*/
Throwable getError(WebRequest webRequest);
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.web.servlet.error;
import org.springframework.stereotype.Controller;
/**
* Marker interface used to indicate that a {@link Controller @Controller} is used to
* render errors. Primarily used to know the error paths that will not need to be secured.
*
* @author Phillip Webb
*/
@FunctionalInterface
public interface ErrorController {
/**
* Returns the path of the error page.
* @return the error path
*/
String getErrorPath();
}

View File

@@ -51,6 +51,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;

View File

@@ -38,8 +38,8 @@ import org.mockito.InOrder;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.SchemaManagement;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.boot.test.util.TestPropertyValues;

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.jdbc;
import java.io.Closeable;
import java.io.IOException;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.test.context.HidePackagesClassLoader;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DataSourceBuilder}.
*
* @author Stephane Nicoll
*/
public class DataSourceBuilderTests {
private DataSource dataSource;
@After
public void shutdownDataSource() throws IOException {
if (this.dataSource instanceof Closeable) {
((Closeable) this.dataSource).close();
}
}
@Test
public void defaultToHikari() {
this.dataSource = DataSourceBuilder.create().url("jdbc:h2:test").build();
assertThat(this.dataSource).isInstanceOf(HikariDataSource.class);
}
@Test
public void defaultToTomcatIfHikariIsNotAvailable() {
this.dataSource = DataSourceBuilder
.create(new HidePackagesClassLoader("com.zaxxer.hikari"))
.url("jdbc:h2:test").build();
assertThat(this.dataSource)
.isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
}
@Test
public void defaultToCommonsDbcp2AsLastResort() {
this.dataSource = DataSourceBuilder
.create(new HidePackagesClassLoader("com.zaxxer.hikari",
"org.apache.tomcat.jdbc.pool"))
.url("jdbc:h2:test").build();
assertThat(this.dataSource).isInstanceOf(BasicDataSource.class);
}
}

View File

@@ -35,6 +35,7 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

View File

@@ -28,6 +28,7 @@ import org.junit.Test;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.jdbc.metadata;
import org.junit.Test;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for {@link DataSourcePoolMetadata} tests.
*
* @param <D> the data source pool metadata type
* @author Stephane Nicoll
*/
public abstract class AbstractDataSourcePoolMetadataTests<D extends AbstractDataSourcePoolMetadata<?>> {
/**
* Return a data source metadata instance with a min size of 0 and max size of 2.
* @return the data source metadata
*/
protected abstract D getDataSourceMetadata();
@Test
public void getMaxPoolSize() {
assertThat(getDataSourceMetadata().getMax()).isEqualTo(Integer.valueOf(2));
}
@Test
public void getMinPoolSize() {
assertThat(getDataSourceMetadata().getMin()).isEqualTo(Integer.valueOf(0));
}
@Test
public void getPoolSizeNoConnection() {
// Make sure the pool is initialized
JdbcTemplate jdbcTemplate = new JdbcTemplate(
getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> null);
assertThat(getDataSourceMetadata().getActive()).isEqualTo(Integer.valueOf(0));
assertThat(getDataSourceMetadata().getUsage()).isEqualTo(Float.valueOf(0));
}
@Test
public void getPoolSizeOneConnection() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(
getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> {
assertThat(getDataSourceMetadata().getActive()).isEqualTo(Integer.valueOf(1));
assertThat(getDataSourceMetadata().getUsage()).isEqualTo(Float.valueOf(0.5F));
return null;
});
}
@Test
public void getPoolSizeTwoConnections() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(
getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> {
jdbcTemplate.execute((ConnectionCallback<Void>) connection1 -> {
assertThat(getDataSourceMetadata().getActive()).isEqualTo(2);
assertThat(getDataSourceMetadata().getUsage()).isEqualTo(1.0f);
return null;
});
return null;
});
}
@Test
public abstract void getValidationQuery();
protected DataSourceBuilder initializeBuilder() {
return DataSourceBuilder.create().driverClassName("org.hsqldb.jdbc.JDBCDriver")
.url("jdbc:hsqldb:mem:test").username("sa");
}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright 2012-2016 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.boot.autoconfigure.jdbc.metadata;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CommonsDbcp2DataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
public class CommonsDbcp2DataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<CommonsDbcp2DataSourcePoolMetadata> {
private CommonsDbcp2DataSourcePoolMetadata dataSourceMetadata;
@Before
public void setup() {
this.dataSourceMetadata = createDataSourceMetadata(0, 2);
}
@Override
protected CommonsDbcp2DataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Test
public void getPoolUsageWithNoCurrent() {
CommonsDbcp2DataSourcePoolMetadata dsm = new CommonsDbcp2DataSourcePoolMetadata(
createDataSource()) {
@Override
public Integer getActive() {
return null;
}
};
assertThat(dsm.getUsage()).isNull();
}
@Test
public void getPoolUsageWithNoMax() {
CommonsDbcp2DataSourcePoolMetadata dsm = new CommonsDbcp2DataSourcePoolMetadata(
createDataSource()) {
@Override
public Integer getMax() {
return null;
}
};
assertThat(dsm.getUsage()).isNull();
}
@Test
public void getPoolUsageWithUnlimitedPool() {
DataSourcePoolMetadata unlimitedDataSource = createDataSourceMetadata(0, -1);
assertThat(unlimitedDataSource.getUsage()).isEqualTo(Float.valueOf(-1F));
}
@Override
public void getValidationQuery() {
BasicDataSource dataSource = createDataSource();
dataSource.setValidationQuery("SELECT FROM FOO");
assertThat(
new CommonsDbcp2DataSourcePoolMetadata(dataSource).getValidationQuery())
.isEqualTo("SELECT FROM FOO");
}
private CommonsDbcp2DataSourcePoolMetadata createDataSourceMetadata(int minSize,
int maxSize) {
BasicDataSource dataSource = createDataSource();
dataSource.setMinIdle(minSize);
dataSource.setMaxTotal(maxSize);
return new CommonsDbcp2DataSourcePoolMetadata(dataSource);
}
private BasicDataSource createDataSource() {
return (BasicDataSource) initializeBuilder().type(BasicDataSource.class).build();
}
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright 2012-2016 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.boot.autoconfigure.jdbc.metadata;
import java.util.Arrays;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link DataSourcePoolMetadataProviders}.
*
* @author Stephane Nicoll
*/
public class DataSourcePoolMetadataProvidersTests {
@Mock
private DataSourcePoolMetadataProvider firstProvider;
@Mock
private DataSourcePoolMetadata first;
@Mock
private DataSource firstDataSource;
@Mock
private DataSourcePoolMetadataProvider secondProvider;
@Mock
private DataSourcePoolMetadata second;
@Mock
private DataSource secondDataSource;
@Mock
private DataSource unknownDataSource;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
given(this.firstProvider.getDataSourcePoolMetadata(this.firstDataSource))
.willReturn(this.first);
given(this.firstProvider.getDataSourcePoolMetadata(this.secondDataSource))
.willReturn(this.second);
}
@Test
public void createWithProviders() {
DataSourcePoolMetadataProviders provider = new DataSourcePoolMetadataProviders(
Arrays.asList(this.firstProvider, this.secondProvider));
assertThat(provider.getDataSourcePoolMetadata(this.firstDataSource))
.isSameAs(this.first);
assertThat(provider.getDataSourcePoolMetadata(this.secondDataSource))
.isSameAs(this.second);
assertThat(provider.getDataSourcePoolMetadata(this.unknownDataSource)).isNull();
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2016 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.boot.autoconfigure.jdbc.metadata;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.Before;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HikariDataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
public class HikariDataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<HikariDataSourcePoolMetadata> {
private HikariDataSourcePoolMetadata dataSourceMetadata;
@Before
public void setup() {
this.dataSourceMetadata = new HikariDataSourcePoolMetadata(
createDataSource(0, 2));
}
@Override
protected HikariDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Override
public void getValidationQuery() {
HikariDataSource dataSource = createDataSource(0, 4);
dataSource.setConnectionTestQuery("SELECT FROM FOO");
assertThat(new HikariDataSourcePoolMetadata(dataSource).getValidationQuery())
.isEqualTo("SELECT FROM FOO");
}
private HikariDataSource createDataSource(int minSize, int maxSize) {
HikariDataSource dataSource = (HikariDataSource) initializeBuilder()
.type(HikariDataSource.class).build();
dataSource.setMinimumIdle(minSize);
dataSource.setMaximumPoolSize(maxSize);
return dataSource;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2016 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.boot.autoconfigure.jdbc.metadata;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.junit.Before;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TomcatDataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
public class TomcatDataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<TomcatDataSourcePoolMetadata> {
private TomcatDataSourcePoolMetadata dataSourceMetadata;
@Before
public void setup() {
this.dataSourceMetadata = new TomcatDataSourcePoolMetadata(
createDataSource(0, 2));
}
@Override
protected TomcatDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Override
public void getValidationQuery() {
DataSource dataSource = createDataSource(0, 4);
dataSource.setValidationQuery("SELECT FROM FOO");
assertThat(new TomcatDataSourcePoolMetadata(dataSource).getValidationQuery())
.isEqualTo("SELECT FROM FOO");
}
private DataSource createDataSource(int minSize, int maxSize) {
DataSource dataSource = (DataSource) initializeBuilder().type(DataSource.class)
.build();
dataSource.setMinIdle(minSize);
dataSource.setMaxActive(maxSize);
// Avoid warnings
dataSource.setInitialSize(minSize);
dataSource.setMaxIdle(maxSize);
return dataSource;
}
}

View File

@@ -36,7 +36,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

View File

@@ -32,8 +32,8 @@ import org.junit.rules.TemporaryFolder;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.liquibase.CommonsLoggingLiquibaseLogger;
import org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener;
import org.springframework.boot.test.rule.OutputCapture;

View File

@@ -25,8 +25,8 @@ import org.junit.After;
import org.junit.Test;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

View File

@@ -1,214 +0,0 @@
/*
* Copyright 2012-2017 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.boot.autoconfigure.web.servlet.error;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import javax.servlet.ServletException;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MapBindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DefaultErrorAttributes}.
*
* @author Phillip Webb
* @author Vedran Pavic
*/
public class DefaultErrorAttributesTests {
private DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes();
private MockHttpServletRequest request = new MockHttpServletRequest();
private WebRequest webRequest = new ServletWebRequest(this.request);
@Test
public void includeTimeStamp() throws Exception {
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("timestamp")).isInstanceOf(Date.class);
}
@Test
public void specificStatusCode() throws Exception {
this.request.setAttribute("javax.servlet.error.status_code", 404);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("error"))
.isEqualTo(HttpStatus.NOT_FOUND.getReasonPhrase());
assertThat(attributes.get("status")).isEqualTo(404);
}
@Test
public void missingStatusCode() throws Exception {
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("error")).isEqualTo("None");
assertThat(attributes.get("status")).isEqualTo(999);
}
@Test
public void mvcError() throws Exception {
RuntimeException ex = new RuntimeException("Test");
ModelAndView modelAndView = this.errorAttributes.resolveException(this.request,
null, null, ex);
this.request.setAttribute("javax.servlet.error.exception",
new RuntimeException("Ignored"));
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
assertThat(modelAndView).isNull();
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void servletError() throws Exception {
RuntimeException ex = new RuntimeException("Test");
this.request.setAttribute("javax.servlet.error.exception", ex);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(ex);
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void servletMessage() throws Exception {
this.request.setAttribute("javax.servlet.error.message", "Test");
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void nullMessage() throws Exception {
this.request.setAttribute("javax.servlet.error.exception",
new RuntimeException());
this.request.setAttribute("javax.servlet.error.message", "Test");
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void unwrapServletException() throws Exception {
RuntimeException ex = new RuntimeException("Test");
ServletException wrapped = new ServletException(new ServletException(ex));
this.request.setAttribute("javax.servlet.error.exception", wrapped);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(wrapped);
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void getError() throws Exception {
Error error = new OutOfMemoryError("Test error");
this.request.setAttribute("javax.servlet.error.exception", error);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(this.errorAttributes.getError(this.webRequest)).isSameAs(error);
assertThat(attributes.get("exception")).isNull();
assertThat(attributes.get("message")).isEqualTo("Test error");
}
@Test
public void extractBindingResultErrors() throws Exception {
BindingResult bindingResult = new MapBindingResult(
Collections.singletonMap("a", "b"), "objectName");
bindingResult.addError(new ObjectError("c", "d"));
Exception ex = new BindException(bindingResult);
testBindingResult(bindingResult, ex);
}
@Test
public void extractMethodArgumentNotValidExceptionBindingResultErrors()
throws Exception {
BindingResult bindingResult = new MapBindingResult(
Collections.singletonMap("a", "b"), "objectName");
bindingResult.addError(new ObjectError("c", "d"));
Exception ex = new MethodArgumentNotValidException(null, bindingResult);
testBindingResult(bindingResult, ex);
}
private void testBindingResult(BindingResult bindingResult, Exception ex) {
this.request.setAttribute("javax.servlet.error.exception", ex);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("message"))
.isEqualTo("Validation failed for object='objectName'. Error count: 1");
assertThat(attributes.get("errors")).isEqualTo(bindingResult.getAllErrors());
}
@Test
public void withExceptionAttribute() throws Exception {
DefaultErrorAttributes errorAttributes = new DefaultErrorAttributes(true);
RuntimeException ex = new RuntimeException("Test");
this.request.setAttribute("javax.servlet.error.exception", ex);
Map<String, Object> attributes = errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("exception"))
.isEqualTo(RuntimeException.class.getName());
assertThat(attributes.get("message")).isEqualTo("Test");
}
@Test
public void trace() throws Exception {
RuntimeException ex = new RuntimeException("Test");
this.request.setAttribute("javax.servlet.error.exception", ex);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, true);
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
}
@Test
public void noTrace() throws Exception {
RuntimeException ex = new RuntimeException("Test");
this.request.setAttribute("javax.servlet.error.exception", ex);
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("trace")).isNull();
}
@Test
public void path() throws Exception {
this.request.setAttribute("javax.servlet.error.request_uri", "path");
Map<String, Object> attributes = this.errorAttributes
.getErrorAttributes(this.webRequest, false);
assertThat(attributes.get("path")).isEqualTo("path");
}
}