Commit 2bb0f744 authored by Phillip Webb's avatar Phillip Webb

Polish

parent 8295e82e
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -119,36 +119,13 @@ public enum EmbeddedDatabaseConnection { ...@@ -119,36 +119,13 @@ public enum EmbeddedDatabaseConnection {
* @return true if the data sourceis one of the embedded types * @return true if the data sourceis one of the embedded types
*/ */
public static boolean isEmbedded(DataSource dataSource) { public static boolean isEmbedded(DataSource dataSource) {
boolean embedded = false;
try { try {
embedded = new JdbcTemplate(dataSource) return new JdbcTemplate(dataSource).execute(new IsEmbedded());
.execute(new ConnectionCallback<Boolean>() {
@Override
public Boolean doInConnection(Connection con)
throws SQLException, DataAccessException {
String productName = con.getMetaData()
.getDatabaseProductName();
if (productName == null) {
return false;
}
productName = productName.toUpperCase();
if (productName.contains(H2.name())) {
return true;
}
if (productName.contains(HSQL.name())) {
return true;
}
if (productName.contains(DERBY.name())) {
return true;
}
return false;
}
});
} }
catch (DataAccessException e) { catch (DataAccessException ex) {
// Could not connect, which means it's not embedded // Could not connect, which means it's not embedded
return false;
} }
return embedded;
} }
/** /**
...@@ -170,4 +147,27 @@ public enum EmbeddedDatabaseConnection { ...@@ -170,4 +147,27 @@ public enum EmbeddedDatabaseConnection {
return NONE; return NONE;
} }
/**
* {@link ConnectionCallback} to determine if a connection is embedded.
*/
private static class IsEmbedded implements ConnectionCallback<Boolean> {
@Override
public Boolean doInConnection(Connection connection) throws SQLException,
DataAccessException {
String productName = connection.getMetaData().getDatabaseProductName();
if (productName == null) {
return false;
}
productName = productName.toUpperCase();
EmbeddedDatabaseConnection[] candidates = EmbeddedDatabaseConnection.values();
for (EmbeddedDatabaseConnection candidate : candidates) {
if (candidate != NONE && productName.contains(candidate.name())) {
return true;
}
}
return false;
}
}
} }
...@@ -28,6 +28,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties ...@@ -28,6 +28,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
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.jms.core.JmsTemplate; import org.springframework.jms.core.JmsTemplate;
import org.springframework.util.StringUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} for {@link JmsTemplate}. * {@link EnableAutoConfiguration Auto-configuration} for {@link JmsTemplate}.
...@@ -64,25 +65,22 @@ public class JmsTemplateAutoConfiguration { ...@@ -64,25 +65,22 @@ public class JmsTemplateAutoConfiguration {
@Bean @Bean
public ConnectionFactory jmsConnectionFactory() { public ConnectionFactory jmsConnectionFactory() {
ConnectionFactory connectionFactory; ConnectionFactory connectionFactory = getActiveMQConnectionFactory();
if (this.config.getUser() != null && !"".equals(this.config.getUser())
&& this.config.getPassword() != null
&& !"".equals(this.config.getPassword())) {
connectionFactory = new ActiveMQConnectionFactory(this.config.getUser(),
this.config.getPassword(), this.config.getBrokerUrl());
}
else {
connectionFactory = new ActiveMQConnectionFactory(
this.config.getBrokerUrl());
}
if (this.config.isPooled()) { if (this.config.isPooled()) {
PooledConnectionFactory pool = new PooledConnectionFactory(); PooledConnectionFactory pool = new PooledConnectionFactory();
pool.setConnectionFactory(connectionFactory); pool.setConnectionFactory(connectionFactory);
return pool; return pool;
} }
else { return connectionFactory;
return connectionFactory; }
private ConnectionFactory getActiveMQConnectionFactory() {
if (StringUtils.hasLength(this.config.getUser())
&& StringUtils.hasLength(this.config.getPassword())) {
return new ActiveMQConnectionFactory(this.config.getUser(),
this.config.getPassword(), this.config.getBrokerUrl());
} }
return new ActiveMQConnectionFactory(this.config.getBrokerUrl());
} }
} }
......
...@@ -935,14 +935,14 @@ and {sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[`JpaBas ...@@ -935,14 +935,14 @@ and {sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[`JpaBas
for more details. for more details.
[[howto-use-custom-entity-manager]] [[howto-use-custom-entity-manager]]
=== Use a custom EntityManagerFactory === Use a custom EntityManagerFactory
To take full control of the configuration of the To take full control of the configuration of the `EntityManagerFactory`, you need to add
`EntityManagerFactory`, you need to add a `@Bean` named a `@Bean` named "entityManagerFactory". To avoid eager initialization of JPA
"entityManagerFactory". To avoid eager initialization of JPA infrastructure, Spring Boot auto-configuration does not switch on its entity manager
infrastructure Spring Boot autoconfiguration does not switch on its based on the presence of a bean of that type. Instead it has to do it by name.
entity manager based on the presence of a bean of that type. Instead
it has to do it by name.
[[howto-use-traditional-persistence-xml]] [[howto-use-traditional-persistence-xml]]
...@@ -1140,9 +1140,12 @@ use this in a webapp is to inject it into a void method in a ...@@ -1140,9 +1140,12 @@ use this in a webapp is to inject it into a void method in a
} }
---- ----
You will get the best results if you put this in a nested class, or a standalone class (i.e. You will get the best results if you put this in a nested class, or a standalone class
not mixed in with a lot of other `@Beans` that might be allowed to influence the order of (i.e. not mixed in with a lot of other `@Beans` that might be allowed to influence the
instantiation). The https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-web-secure[secure web sample] is a useful template to follow. order of instantiation). The {github-code}/spring-boot-samples/spring-boot-sample-web-secure[secure web sample]
is a useful template to follow.
[[howto-enable-https]] [[howto-enable-https]]
=== Enable HTTPS === Enable HTTPS
......
...@@ -251,8 +251,8 @@ all non-sensitive endpoints to be exposed over HTTP. The default convention is t ...@@ -251,8 +251,8 @@ all non-sensitive endpoints to be exposed over HTTP. The default convention is t
[[production-ready-sensitive-endpoints]] [[production-ready-sensitive-endpoints]]
=== Exposing sensitive endpoints === Exposing sensitive endpoints
If you use ``Spring Security'' sensitive endpoints will be exposed over HTTP, but also If you use ``Spring Security'' sensitive endpoints will be exposed over HTTP, but also
protected. By default ``basic'' authentication will be used with the username `user` protected. By default ``basic'' authentication will be used with the username `user`
and a generated password (which is printed on the console when the application starts). and a generated password (which is printed on the console when the application starts).
TIP: Generated passwords are logged as the application starts. Search for ``Using default TIP: Generated passwords are logged as the application starts. Search for ``Using default
...@@ -300,11 +300,10 @@ The `management.port` property can be used to change the HTTP port. ...@@ -300,11 +300,10 @@ The `management.port` property can be used to change the HTTP port.
management.port=8081 management.port=8081
---- ----
Since your management Since your management port is often protected by a firewall, and not exposed to the public
port is often protected by a firewall, and not exposed to the public you might not need you might not need security on the management endpoints, even if your main application is
security on the management endpoints, even if your main application is secure. In that secure. In that case you will have Spring Security on the classpath, and you can disable
case you will have Spring management security like this:
Security on the classpath, and you can disable management security like this:
[source,properties,indent=0] [source,properties,indent=0]
---- ----
...@@ -314,6 +313,8 @@ Security on the classpath, and you can disable management security like this: ...@@ -314,6 +313,8 @@ Security on the classpath, and you can disable management security like this:
(If you don't have Spring Security on the classpath then there is no need to explicitly (If you don't have Spring Security on the classpath then there is no need to explicitly
disable the management security in this way, and it might even break the application.) disable the management security in this way, and it might even break the application.)
[[production-ready-customizing-management-server-address]] [[production-ready-customizing-management-server-address]]
=== Customizing the management server address === Customizing the management server address
You can customize the address that the management endpoints are available on by You can customize the address that the management endpoints are available on by
......
...@@ -993,6 +993,8 @@ packaged as an executable archive), there are some limitations in the JSP suppor ...@@ -993,6 +993,8 @@ packaged as an executable archive), there are some limitations in the JSP suppor
There is a {github-code}/spring-boot-samples/spring-boot-sample-web-jsp[JSP sample] so There is a {github-code}/spring-boot-samples/spring-boot-sample-web-jsp[JSP sample] so
you can see how to set things up. you can see how to set things up.
[[boot-features-security]] [[boot-features-security]]
== Security == Security
If Spring Security is on the classpath then web applications will be secure by default If Spring Security is on the classpath then web applications will be secure by default
...@@ -1001,58 +1003,47 @@ application you can also add `@EnableGlobalMethodSecurity` with your desired set ...@@ -1001,58 +1003,47 @@ application you can also add `@EnableGlobalMethodSecurity` with your desired set
Additional information can be found in the {spring-security-reference}#jc-method[Spring Additional information can be found in the {spring-security-reference}#jc-method[Spring
Security Reference]. Security Reference].
The default `AuthenticationManager` has a single user (username The default `AuthenticationManager` has a single user (username ``user'' and password
``user'' and password random, printed at INFO level when the random, printed at INFO level when the application starts up). You can change the
application starts up). You can change the password by providing a password by providing a `security.user.password`. This and other useful properties are
`security.user.password`. This and other useful properties are externalized via {sc-spring-boot-autoconfigure}/security/SecurityProperties.{sc-ext}[`SecurityProperties`]
externalized via
{sc-spring-boot-autoconfigure}/security/SecurityProperties.{sc-ext}[`SecurityProperties`]
(properties prefix "security"). (properties prefix "security").
The default security configuration is implemented in The default security configuration is implemented in `SecurityAutoConfiguration` and in
`SecurityAutoConfiguration` and in the classes imported from there the classes imported from there (`SpringBootWebSecurityConfiguration` for web security
(`SpringBootWebSecurityConfiguration` for web security and and `AuthenticationManagerConfiguration` for authentication configuration which is also
`AuthenticationManagerConfiguration` for authentication configuration relevant in non-web applications). To switch off the Boot default configuration
which is also relevant in non-web applications). To switch off the completely in a web application you can add a bean with `@EnableWebSecurity`. To customize
Boot default configuration completely in a web application you can add it you normally use external properties and beans of type `WebConfigurerAdapter` (e.g. to
a bean with `@EnableWebSecurity`. To customize it you normally use
external properties and beans of type `WebConfigurerAdapter` (e.g. to
add form-based login). There are several secure applications in the add form-based login). There are several secure applications in the
{github-code}/spring-boot-samples/[Spring Boot samples] to get you {github-code}/spring-boot-samples/[Spring Boot samples] to get you started with common
started with common use cases. use cases.
The basic features you get out of the box in a web application are
* An `AuthenticationManager` bean with in-memory store and a single
user (see `SecurityProperties.User` for the properties of the user).
* Ignored (unsecure) paths for common static resource locations The basic features you get out of the box in a web application are:
(`/css/**`, `/js/**`, `/images/**` and `**/favicon.ico`).
* An `AuthenticationManager` bean with in-memory store and a single user (see
`SecurityProperties.User` for the properties of the user).
* Ignored (unsecure) paths for common static resource locations (`/css/**`, `/js/**`,
`/images/**` and `**/favicon.ico`).
* HTTP Basic security for all other endpoints. * HTTP Basic security for all other endpoints.
* Security events published to Spring's `ApplicationEventPublisher` (successful and
unsuccessful authentication and access denied).
* Common low-level features (HSTS, XSS, CSRF, caching) provided by Spring Security are
on by default.
* Security events published to Spring's `ApplicationEventPublisher` All of the above can be switched on and off or modified using external properties
(successful and unsuccessful authentication and access denied). (`security.*`).
* Common low-level features (HSTS, XSS, CSRF, caching) provided by Spring
Security are on by default.
All of the above can be switched on and off or modified using external
properties (`security.*`).
If the Actuator is also in use, you will find: If the Actuator is also in use, you will find:
* The management endpoints are secure even if the application * The management endpoints are secure even if the application endpoints are unsecure.
endpoints are unsecure. * Security events are transformed into `AuditEvents` and published to the `AuditService`.
* The default user will have the "ADMIN" role as well as the "USER" role.
* Security events are transformed into `AuditEvents` and published to The Actuator security features can be modified using external properties
the `AuditService`. (`management.security.*`).
* The default user will have the "ADMIN" role as well as the "USER"
role.
The Actuator security features can be modified using external
properties (`management.security.*`).
[[boot-features-sql]] [[boot-features-sql]]
== Working with SQL databases == Working with SQL databases
...@@ -1299,12 +1290,12 @@ following to your `application.properties`. ...@@ -1299,12 +1290,12 @@ following to your `application.properties`.
spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.hibernate.ddl-auto=create-drop
---- ----
Note that Hibernate's own internal property name for this (if you NOTE: Hibernate's own internal property name for this (if you happen to remember it
happen to remember it better) is `hibernate.hbm2ddl.auto`. You can set better) is `hibernate.hbm2ddl.auto`. You can set it, along with other Hibernate native
it, along with other Hibernate native properties, using properties, using `spring.jpa.properties.*` (the prefix is stripped before adding them
`spring.jpa.properties.*` (the prefix is stripped before adding them to the entity manager). Alternatively, `spring.jpa.generate-ddl=false` switches off all
to the entity manager). Also relevant: DDL generation.
`spring.jpa.generate-ddl=false` switches off all DDL generation.
[[boot-features-nosql]] [[boot-features-nosql]]
......
...@@ -35,7 +35,7 @@ import org.springframework.boot.loader.tools.MainClassFinder; ...@@ -35,7 +35,7 @@ import org.springframework.boot.loader.tools.MainClassFinder;
/** /**
* Run the project from Gradle. * Run the project from Gradle.
* *
* @author Dave Syer * @author Dave Syer
*/ */
public class RunApp extends DefaultTask { public class RunApp extends DefaultTask {
...@@ -75,7 +75,7 @@ public class RunApp extends DefaultTask { ...@@ -75,7 +75,7 @@ public class RunApp extends DefaultTask {
} }
if (outputDir != null) { if (outputDir != null) {
for (File directory : allResources) { for (File directory : allResources) {
FileUtils.removeDuplicatesFromCopy(outputDir, directory); FileUtils.removeDuplicatesFromOutputDirectory(outputDir, directory);
} }
} }
exec.exec(); exec.exec();
......
/* /*
* originright 2012-2013 the copyal author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a origin of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
...@@ -26,24 +26,24 @@ import java.io.File; ...@@ -26,24 +26,24 @@ import java.io.File;
public class FileUtils { public class FileUtils {
/** /**
* Utility to remove duplicate files from a "copy" directory if they already exist in * Utility to remove duplicate files from an "output" directory if they already exist
* an "origin". Recursively scans the origin directory looking for files (not * in an "origin". Recursively scans the origin directory looking for files (not
* directories) that exist in both places and deleting the copy. * directories) that exist in both places and deleting the copy.
* * @param outputDirectory the output directory
* @param copy the copy directory * @param originDirectory the origin directory
* @param origin the origin directory
*/ */
public static void removeDuplicatesFromCopy(File copy, File origin) { public static void removeDuplicatesFromOutputDirectory(File outputDirectory,
if (origin.isDirectory()) { File originDirectory) {
for (String name : origin.list()) { if (originDirectory.isDirectory()) {
File targetFile = new File(copy, name); for (String name : originDirectory.list()) {
File targetFile = new File(outputDirectory, name);
if (targetFile.exists() && targetFile.canWrite()) { if (targetFile.exists() && targetFile.canWrite()) {
if (!targetFile.isDirectory()) { if (!targetFile.isDirectory()) {
targetFile.delete(); targetFile.delete();
} }
else { else {
FileUtils.removeDuplicatesFromCopy(targetFile, new File(origin, FileUtils.removeDuplicatesFromOutputDirectory(targetFile,
name)); new File(originDirectory, name));
} }
} }
} }
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -27,60 +27,67 @@ import static org.junit.Assert.assertFalse; ...@@ -27,60 +27,67 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
* Tests fir {@link FileUtils}.
*
* @author Dave Syer * @author Dave Syer
*/ */
public class FileUtilsTests { public class FileUtilsTests {
private File origin; private File outputDirectory;
private File target;
private File originDirectory;
@Before @Before
public void init() { public void init() {
this.origin = new File("target/test/remove"); this.outputDirectory = new File("target/test/remove");
this.target = new File("target/test/keep"); this.originDirectory = new File("target/test/keep");
FileSystemUtils.deleteRecursively(this.origin); FileSystemUtils.deleteRecursively(this.outputDirectory);
FileSystemUtils.deleteRecursively(this.target); FileSystemUtils.deleteRecursively(this.originDirectory);
this.origin.mkdirs(); this.outputDirectory.mkdirs();
this.target.mkdirs(); this.originDirectory.mkdirs();
} }
@Test @Test
public void simpleDuplicateFile() throws IOException { public void simpleDuplicateFile() throws IOException {
File file = new File(this.origin, "logback.xml"); File file = new File(this.outputDirectory, "logback.xml");
file.createNewFile(); file.createNewFile();
new File(this.target, "logback.xml").createNewFile(); new File(this.originDirectory, "logback.xml").createNewFile();
FileUtils.removeDuplicatesFromCopy(this.origin, this.target); FileUtils.removeDuplicatesFromOutputDirectory(this.outputDirectory,
this.originDirectory);
assertFalse(file.exists()); assertFalse(file.exists());
} }
@Test @Test
public void nestedDuplicateFile() throws IOException { public void nestedDuplicateFile() throws IOException {
assertTrue(new File(this.origin, "sub").mkdirs()); assertTrue(new File(this.outputDirectory, "sub").mkdirs());
assertTrue(new File(this.target, "sub").mkdirs()); assertTrue(new File(this.originDirectory, "sub").mkdirs());
File file = new File(this.origin, "sub/logback.xml"); File file = new File(this.outputDirectory, "sub/logback.xml");
file.createNewFile(); file.createNewFile();
new File(this.target, "sub/logback.xml").createNewFile(); new File(this.originDirectory, "sub/logback.xml").createNewFile();
FileUtils.removeDuplicatesFromCopy(this.origin, this.target); FileUtils.removeDuplicatesFromOutputDirectory(this.outputDirectory,
this.originDirectory);
assertFalse(file.exists()); assertFalse(file.exists());
} }
@Test @Test
public void nestedNonDuplicateFile() throws IOException { public void nestedNonDuplicateFile() throws IOException {
assertTrue(new File(this.origin, "sub").mkdirs()); assertTrue(new File(this.outputDirectory, "sub").mkdirs());
assertTrue(new File(this.target, "sub").mkdirs()); assertTrue(new File(this.originDirectory, "sub").mkdirs());
File file = new File(this.origin, "sub/logback.xml"); File file = new File(this.outputDirectory, "sub/logback.xml");
file.createNewFile(); file.createNewFile();
new File(this.target, "sub/different.xml").createNewFile(); new File(this.originDirectory, "sub/different.xml").createNewFile();
FileUtils.removeDuplicatesFromCopy(this.origin, this.target); FileUtils.removeDuplicatesFromOutputDirectory(this.outputDirectory,
this.originDirectory);
assertTrue(file.exists()); assertTrue(file.exists());
} }
@Test @Test
public void nonDuplicateFile() throws IOException { public void nonDuplicateFile() throws IOException {
File file = new File(this.origin, "logback.xml"); File file = new File(this.outputDirectory, "logback.xml");
file.createNewFile(); file.createNewFile();
new File(this.target, "different.xml").createNewFile(); new File(this.originDirectory, "different.xml").createNewFile();
FileUtils.removeDuplicatesFromCopy(this.origin, this.target); FileUtils.removeDuplicatesFromOutputDirectory(this.outputDirectory,
this.originDirectory);
assertTrue(file.exists()); assertTrue(file.exists());
} }
......
...@@ -198,7 +198,7 @@ public class RunMojo extends AbstractMojo { ...@@ -198,7 +198,7 @@ public class RunMojo extends AbstractMojo {
for (Resource resource : this.project.getResources()) { for (Resource resource : this.project.getResources()) {
File directory = new File(resource.getDirectory()); File directory = new File(resource.getDirectory());
urls.add(directory.toURI().toURL()); urls.add(directory.toURI().toURL());
FileUtils.removeDuplicatesFromCopy(this.classesDirectory, directory); FileUtils.removeDuplicatesFromOutputDirectory(this.classesDirectory, directory);
} }
} }
} }
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2014 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment