mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +08:00
More ordering issues in service apps
* The TraceAutoConfiguration in the service project was loading too early because it contains a BPP * It also had a Spring Security dependency without any @Conditional* configuration * Fixed by nesting the BPP in a class with @Conditional*
This commit is contained in:
parent
0a58e560b3
commit
ec0e9b17ad
@ -99,11 +99,13 @@ public class CleanCommand extends OptionParsingCommand {
|
|||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
if (options.has(this.allOption)
|
if (options.has(this.allOption)
|
||||||
|| group.equals("org.springframework.bootstrap")) {
|
|| group.equals("org.springframework.bootstrap")) {
|
||||||
|
System.out.println("Deleting: " + file);
|
||||||
FileUtil.forceDelete(file);
|
FileUtil.forceDelete(file);
|
||||||
} else {
|
} else {
|
||||||
for (Object obj : FileUtil.listAll(file, Collections.emptyList())) {
|
for (Object obj : FileUtil.listAll(file, Collections.emptyList())) {
|
||||||
File candidate = (File) obj;
|
File candidate = (File) obj;
|
||||||
if (candidate.getName().contains("SNAPSHOT")) {
|
if (candidate.getName().contains("SNAPSHOT")) {
|
||||||
|
System.out.println("Deleting: " + candidate);
|
||||||
FileUtil.forceDelete(candidate);
|
FileUtil.forceDelete(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash -x
|
#!/bin/bash
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false;
|
cygwin=false;
|
||||||
@ -48,8 +48,14 @@ fi
|
|||||||
|
|
||||||
if [ "$SPRING_HOME" == "" ]; then
|
if [ "$SPRING_HOME" == "" ]; then
|
||||||
SPRING_HOME=`cd "$(dirname $0)"/.. && pwd`
|
SPRING_HOME=`cd "$(dirname $0)"/.. && pwd`
|
||||||
|
else
|
||||||
|
if [ ! -d "${SPRING_HOME}" ]; then
|
||||||
|
echo "Not a directory: SPRING_HOME=${SPRING_HOME}"
|
||||||
|
echo "Please rectify and restart."
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
SPRING_HOME=`cd "${SPRING_HOME}" && pwd`
|
||||||
fi
|
fi
|
||||||
SPRING_HOME=`cd "${SPRING_HOME}" && pwd`
|
|
||||||
SPRING_BIN=$(dirname $0)
|
SPRING_BIN=$(dirname $0)
|
||||||
|
|
||||||
TARGETDIR=target/classes
|
TARGETDIR=target/classes
|
||||||
@ -60,15 +66,7 @@ mkdir -p "${TARGETDIR%/}"
|
|||||||
|
|
||||||
CLASSPATH="${CLASSPATH}":"${SPRING_BIN}":"${TARGETDIR}"
|
CLASSPATH="${CLASSPATH}":"${SPRING_BIN}":"${TARGETDIR}"
|
||||||
|
|
||||||
if [ ! "${SPRING_BIN}" == "${SPRING_HOME}" ]; then
|
for f in "${SPRING_HOME}"/*.jar "${SPRING_HOME}"/lib/*.jar "${SPRING_HOME}"/classes; do
|
||||||
CLASSPATH="${CLASSPATH}":"${SPRING_HOME}"/classes
|
|
||||||
fi
|
|
||||||
|
|
||||||
for f in "${SPRING_HOME}"/lib/*.jar; do
|
|
||||||
[ -f $f ] && CLASSPATH="${CLASSPATH}":$f
|
|
||||||
done
|
|
||||||
|
|
||||||
for f in "${SPRING_HOME}"/*.jar; do
|
|
||||||
[ -f $f ] && CLASSPATH="${CLASSPATH}":$f
|
[ -f $f ] && CLASSPATH="${CLASSPATH}":$f
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -0,0 +1,120 @@
|
|||||||
|
package org.springframework.bootstrap.sample.service;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.bootstrap.SpringApplication;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||||
|
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||||
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
|
import org.springframework.http.client.InterceptingClientHttpRequestFactory;
|
||||||
|
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||||
|
import org.springframework.security.crypto.codec.Base64;
|
||||||
|
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration tests for separate management and main service ports.
|
||||||
|
*
|
||||||
|
* @author Dave Syer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ShutdownServiceBootstrapApplicationTests {
|
||||||
|
|
||||||
|
private static ConfigurableApplicationContext context;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void start() throws Exception {
|
||||||
|
Future<ConfigurableApplicationContext> future = Executors
|
||||||
|
.newSingleThreadExecutor().submit(
|
||||||
|
new Callable<ConfigurableApplicationContext>() {
|
||||||
|
@Override
|
||||||
|
public ConfigurableApplicationContext call() throws Exception {
|
||||||
|
return (ConfigurableApplicationContext) SpringApplication
|
||||||
|
.run(ServiceBootstrapApplication.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
context = future.get(10, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stop() {
|
||||||
|
if (context != null) {
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHome() throws Exception {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
ResponseEntity<Map> entity = getRestTemplate("user", "password").getForEntity(
|
||||||
|
"http://localhost:8080", Map.class);
|
||||||
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> body = entity.getBody();
|
||||||
|
assertEquals("Hello Phil", body.get("message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShutdown() throws Exception {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
ResponseEntity<Map> entity = getRestTemplate("user", "password").postForEntity(
|
||||||
|
"http://localhost:8080/shutdown", null, Map.class);
|
||||||
|
assertEquals(HttpStatus.OK, entity.getStatusCode());
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> body = entity.getBody();
|
||||||
|
assertTrue("Wrong body: " + body,
|
||||||
|
((String) body.get("message")).contains("Shutting down"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RestTemplate getRestTemplate(final String username, final String password) {
|
||||||
|
|
||||||
|
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
|
||||||
|
|
||||||
|
if (username != null) {
|
||||||
|
|
||||||
|
interceptors.add(new ClientHttpRequestInterceptor() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||||
|
ClientHttpRequestExecution execution) throws IOException {
|
||||||
|
request.getHeaders().add(
|
||||||
|
"Authorization",
|
||||||
|
"Basic "
|
||||||
|
+ new String(Base64
|
||||||
|
.encode((username + ":" + password)
|
||||||
|
.getBytes())));
|
||||||
|
return execution.execute(request, body);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
RestTemplate restTemplate = new RestTemplate(
|
||||||
|
new InterceptingClientHttpRequestFactory(
|
||||||
|
new SimpleClientHttpRequestFactory(), interceptors));
|
||||||
|
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
|
||||||
|
@Override
|
||||||
|
public void handleError(ClientHttpResponse response) throws IOException {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return restTemplate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -65,6 +65,7 @@
|
|||||||
<groupId>log4j</groupId>
|
<groupId>log4j</groupId>
|
||||||
<artifactId>log4j</artifactId>
|
<artifactId>log4j</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
@ -37,6 +37,7 @@ import org.springframework.bootstrap.service.properties.ContainerProperties;
|
|||||||
import org.springframework.bootstrap.service.properties.ContainerProperties.Tomcat;
|
import org.springframework.bootstrap.service.properties.ContainerProperties.Tomcat;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,6 +49,7 @@ import org.springframework.util.StringUtils;
|
|||||||
// the right order
|
// the right order
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass({ Servlet.class })
|
@ConditionalOnClass({ Servlet.class })
|
||||||
|
@Order(Integer.MIN_VALUE)
|
||||||
public class ContainerConfiguration implements BeanPostProcessor, BeanFactoryAware {
|
public class ContainerConfiguration implements BeanPostProcessor, BeanFactoryAware {
|
||||||
|
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
@ -18,17 +18,19 @@ package org.springframework.bootstrap.autoconfigure.service;
|
|||||||
|
|
||||||
import javax.servlet.Servlet;
|
import javax.servlet.Servlet;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
|
||||||
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
|
||||||
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
|
||||||
import org.springframework.bootstrap.service.properties.ContainerProperties;
|
|
||||||
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
|
import org.springframework.bootstrap.service.trace.InMemoryTraceRepository;
|
||||||
import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor;
|
import org.springframework.bootstrap.service.trace.SecurityFilterPostProcessor;
|
||||||
import org.springframework.bootstrap.service.trace.TraceEndpoint;
|
import org.springframework.bootstrap.service.trace.TraceEndpoint;
|
||||||
import org.springframework.bootstrap.service.trace.TraceRepository;
|
import org.springframework.bootstrap.service.trace.TraceRepository;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,24 +43,34 @@ import org.springframework.web.servlet.DispatcherServlet;
|
|||||||
@ConditionalOnMissingBean({ TraceEndpoint.class })
|
@ConditionalOnMissingBean({ TraceEndpoint.class })
|
||||||
public class TraceAutoConfiguration {
|
public class TraceAutoConfiguration {
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ContainerProperties configuration = new ContainerProperties();
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private TraceRepository traceRepository = new InMemoryTraceRepository();
|
private TraceRepository traceRepository = new InMemoryTraceRepository();
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean({ TraceRepository.class })
|
@ConditionalOnMissingBean(TraceRepository.class)
|
||||||
protected TraceRepository traceRepository() {
|
protected TraceRepository traceRepository() {
|
||||||
return this.traceRepository;
|
return this.traceRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Configuration
|
||||||
public SecurityFilterPostProcessor securityFilterPostProcessor() {
|
@ConditionalOnClass(SecurityFilterChain.class)
|
||||||
SecurityFilterPostProcessor processor = new SecurityFilterPostProcessor(
|
public static class SecurityFilterPostProcessorConfiguration {
|
||||||
traceRepository());
|
|
||||||
processor.setDumpRequests(this.configuration.isDumpRequests());
|
@Autowired
|
||||||
return processor;
|
private TraceRepository traceRepository;
|
||||||
|
|
||||||
|
@Value("${container.dump_requests:false}")
|
||||||
|
private boolean dumpRequests;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterPostProcessor securityFilterPostProcessor(
|
||||||
|
BeanFactory beanFactory) {
|
||||||
|
SecurityFilterPostProcessor processor = new SecurityFilterPostProcessor(
|
||||||
|
this.traceRepository);
|
||||||
|
processor.setDumpRequests(this.dumpRequests);
|
||||||
|
return processor;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -35,6 +35,7 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
@ -51,7 +52,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SecurityFilterPostProcessor implements BeanPostProcessor {
|
public class SecurityFilterPostProcessor implements BeanPostProcessor, Ordered {
|
||||||
|
|
||||||
private final static Log logger = LogFactory
|
private final static Log logger = LogFactory
|
||||||
.getLog(SecurityFilterPostProcessor.class);
|
.getLog(SecurityFilterPostProcessor.class);
|
||||||
@ -60,6 +61,20 @@ public class SecurityFilterPostProcessor implements BeanPostProcessor {
|
|||||||
|
|
||||||
private TraceRepository traceRepository = new InMemoryTraceRepository();
|
private TraceRepository traceRepository = new InMemoryTraceRepository();
|
||||||
|
|
||||||
|
private int order = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param order the order to set
|
||||||
|
*/
|
||||||
|
public void setOrder(int order) {
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return this.order;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param traceRepository
|
* @param traceRepository
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user