[bs-118], [bs-119]: add UI builder features

* MessageSource created automatically (location
spring.messages.basename:messages)
* Thymeleaf configured automatically to look for
templates in classpath:/templates
* Added static resource handlers for classpath:/static
and classpath:/

[Fixes #49832165] [bs-118] Support for thymeleaf templates
This commit is contained in:
Dave Syer
2013-05-15 15:58:18 +01:00
parent ceea71fc38
commit 53078c320e
49 changed files with 12042 additions and 93 deletions
+1
View File
@@ -10,3 +10,4 @@
bin
build
target
.springBeans
+15
View File
@@ -309,6 +309,11 @@
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
@@ -525,6 +530,16 @@
<artifactId>snakeyaml</artifactId>
<version>1.12</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
<version>2.0.16</version>
</dependency>
<dependency>
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>1.0.6</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
@@ -1,4 +1,2 @@
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
org.springframework.bootstrap.actuate.autoconfigure.ActuatorAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
org.springframework.bootstrap.logging.LoggingInitializer
@@ -3,10 +3,10 @@
<parent>
<artifactId>spring-bootstrap-samples</artifactId>
<groupId>org.springframework.bootstrap</groupId>
<version>0.0.1-SNAPSHOT</version>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-bootstrap-service-sample</artifactId>
<artifactId>spring-bootstrap-integration-sample</artifactId>
<build>
<plugins>
<plugin>
@@ -14,77 +14,6 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>tomcat</id>
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>jetty</id>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>8.1.9.v20130131</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>javax.servlet</artifactId>
<groupId>org.eclipse.jetty.orbit</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-jsp</artifactId>
<version>8.1.9.v20130131</version>
<scope>compile</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>juli</id>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>log4j</id>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>logback</id>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>junit</groupId>
@@ -128,8 +57,7 @@
</dependency>
</dependencies>
<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
<start-class>org.springframework.bootstrap.sample.service.ServiceBootstrapApplication</start-class>
<start-class>org.springframework.bootstrap.sample.service.IntegrationBootstrapApplication</start-class>
</properties>
</project>
@@ -41,7 +41,7 @@ public class TradBootstrapApplicationTests {
.run(TradBootstrapApplication.class);
}
});
context = future.get(10, TimeUnit.SECONDS);
context = future.get(30, TimeUnit.SECONDS);
}
@AfterClass
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
<artifactId>spring-bootstrap-samples</artifactId>
<version>0.5.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-bootstrap-ui-sample</artifactId>
<packaging>war</packaging>
<properties>
<m2eclipse.wtp.contextRoot>/</m2eclipse.wtp.contextRoot>
<start-class>org.springframework.bootstrap.sample.ui.UiBootstrapApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>spring-bootstrap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
</dependency>
<dependency>
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-logging-juli</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.3.1.Final</version>
<!-- FIXME: make this optional -->
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,52 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.sample.ui;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Dave Syer
*
*/
public class InMemoryMessageRespository implements MessageRepository {
private static AtomicLong counter = new AtomicLong();
private ConcurrentMap<Long, Message> messages = new ConcurrentHashMap<Long, Message>();
@Override
public Iterable<Message> findAll() {
return this.messages.values();
}
@Override
public Message save(Message message) {
Long id = message.getId();
if (id == null) {
id = counter.incrementAndGet();
message.setId(id);
}
this.messages.put(id, message);
return message;
}
@Override
public Message findMessage(Long id) {
return this.messages.get(id);
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.sample.ui;
import java.util.Calendar;
import org.hibernate.validator.constraints.NotEmpty;
/**
*
* @author Rob Winch
*
*/
public class Message {
private Long id;
@NotEmpty(message = "Message is required.")
private String text;
@NotEmpty(message = "Summary is required.")
private String summary;
private Calendar created = Calendar.getInstance();
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Calendar getCreated() {
return this.created;
}
public void setCreated(Calendar created) {
this.created = created;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public String getSummary() {
return this.summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.sample.ui;
/**
*
* @author Rob Winch
*
*/
public interface MessageRepository {
Iterable<Message> findAll();
Message save(Message message);
Message findMessage(Long id);
}
@@ -0,0 +1,34 @@
package org.springframework.bootstrap.sample.ui;
import org.springframework.bootstrap.SpringApplication;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class UiBootstrapApplication {
@Bean
public MessageRepository messageRepository() {
return new InMemoryMessageRespository();
}
@Bean
public Converter<String, Message> messageConverter() {
return new Converter<String, Message>() {
@Override
public Message convert(String id) {
return messageRepository().findMessage(Long.valueOf(id));
}
};
}
public static void main(String[] args) throws Exception {
SpringApplication.run(UiBootstrapApplication.class, args);
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.bootstrap.sample.ui.mvc;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.sample.ui.Message;
import org.springframework.bootstrap.sample.ui.MessageRepository;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
/**
*
* @author Rob Winch
*
*/
@Controller
@RequestMapping("/")
public class MessageController {
private MessageRepository messageRepository;
@Autowired
public MessageController(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@RequestMapping
public ModelAndView list() {
Iterable<Message> messages = messageRepository.findAll();
return new ModelAndView("messages/list", "messages", messages);
}
@RequestMapping("{id}")
public ModelAndView view(@PathVariable("id")
Message message) {
return new ModelAndView("messages/view", "message", message);
}
@RequestMapping(params = "form", method = RequestMethod.GET)
public String createForm(@ModelAttribute
Message message) {
return "messages/form";
}
@RequestMapping(method = RequestMethod.POST)
public ModelAndView create(@Valid
Message message, BindingResult result, RedirectAttributes redirect) {
if (result.hasErrors()) {
return new ModelAndView("messages/form", "formErrors",
result.getAllErrors());
}
message = messageRepository.save(message);
redirect.addFlashAttribute("globalMessage",
"Successfully created a new message");
return new ModelAndView("redirect:/{message.id}",
"message.id", message.getId());
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<title>Layout</title>
<link rel="stylesheet"
th:href="@{/resources/css/bootstrap.min.css}"
href="../../resources/css/bootstrap.min.css"/>
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<a class="brand"
href="https://github.com/ultraq/thymeleaf-layout-dialect">
Thymeleaf - Layout
</a>
<ul class="nav">
<li>
<a th:href="@{/}" href="messages.html">
Messages
</a>
</li>
</ul>
</div>
</div>
<h1 layout:fragment="header">Layout</h1>
<div layout:fragment="content">
Fake content
</div>
</div>
</body>
</html>
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
layout:decorator="layout">
<head>
<title>Messages : Create</title>
</head>
<body>
<h1 layout:fragment="header">Messages : Create</h1>
<div layout:fragment="content"
class="container">
<form id="messageForm"
th:action="@{/(form)}"
th:object="${message}"
action="#"
method="post">
<div th:if="${#fields.hasErrors('*')}"
class="alert alert-error">
<p th:each="error : ${#fields.errors('*')}"
th:text="${error}">
Validation error
</p>
</div>
<div class="pull-right">
<a th:href="@{/}" href="messages.html">
Messages
</a>
</div>
<label for="summary">Summary</label>
<input type="text"
th:field="*{summary}"
th:class="${#fields.hasErrors('summary')} ? 'field-error'"/>
<label for="text">Message</label>
<textarea
th:field="*{text}"
th:class="${#fields.hasErrors('text')} ? 'field-error'"></textarea>
<div class="form-actions">
<input type="submit" value="Create"/>
</div>
</form>
</div>
</body>
</html>
@@ -0,0 +1,45 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
layout:decorator="layout">
<head>
<title>Messages : View all</title>
</head>
<body>
<h1 layout:fragment="header">Messages : View all</h1>
<div layout:fragment="content" class="container">
<div class="pull-right">
<a href="form.html" th:href="@{/(form)}">Create Message</a>
</div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td>ID</td>
<td>Created</td>
<td>Summary</td>
</tr>
</thead>
<tbody>
<tr th:if="${messages.empty}">
<td colspan="3">
No messages
</td>
</tr>
<tr th:each="message : ${messages}">
<td th:text="${message.id}">1</td>
<td th:text="${#calendars.format(message.created)}">
July 11, 2012 2:17:16 PM CDT
</td>
<td>
<a href="view.html"
th:href="@{'/' + ${message.id}}"
th:text="${message.summary}">
The summary
</a>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
@@ -0,0 +1,42 @@
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout"
layout:decorator="layout">
<head>
<title>Messages : View</title>
</head>
<body>
<h1 layout:fragment="header">Messages : Create</h1>
<div layout:fragment="content"
class="container">
<div class="alert alert-success"
th:if="${globalMessage}"
th:text="${globalMessage}">
Some Success message
</div>
<div class="pull-right">
<a th:href="@{/}" href="list.html">
Messages
</a>
</div>
<dl>
<dt>ID</dt>
<dd id="id" th:text="${message.id}">123</dd>
<dt>Date</dt>
<dd id="created"
th:text="${#calendars.format(message.created)}">
July 11, 2012 2:17:16 PM CDT
</dd>
<dt>Summary</dt>
<dd id="summary"
th:text="${message.summary}">
A short summary...
</dd>
<dt>Message</dt>
<dd id="text"
th:text="${message.text}">
A detailed message that is longer than the summary.
</dd>
</dl>
</div>
</body>
</html>
@@ -0,0 +1,98 @@
package org.springframework.bootstrap.sample.ui;
import java.io.IOException;
import java.net.URI;
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.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* Basic integration tests for demo application.
*
* @author Dave Syer
*
*/
public class UiBootstrapApplicationTests {
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(UiBootstrapApplication.class);
}
});
context = future.get(30, TimeUnit.SECONDS);
}
@AfterClass
public static void stop() {
if (context != null) {
context.close();
}
}
@Test
public void testHome() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:8080", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity
.getBody().contains("<title>Messages"));
assertFalse("Wrong body (found layout:fragment):\n" + entity.getBody(), entity
.getBody().contains("layout:fragment"));
}
@Test
public void testCreate() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.set("text", "FOO text");
map.set("summary", "FOO");
URI location = getRestTemplate().postForLocation("http://localhost:8080", map);
assertTrue("Wrong location:\n" + location,
location.toString().contains("localhost:8080"));
}
@Test
public void testCss() throws Exception {
ResponseEntity<String> entity = getRestTemplate().getForEntity(
"http://localhost:8080/css/bootstrap.min.css", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body"));
}
private RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public void handleError(ClientHttpResponse response) throws IOException {
}
});
return restTemplate;
}
}
@@ -0,0 +1,7 @@
handlers = java.util.logging.ConsoleHandler
.level = INFO
java.util.logging.ConsoleHandler.level = FINE
sun.net.www.protocol.http.HttpURLConnection.level = ALL
org.springframework.bootstrap.context.annotation.level = ALL
org.thymeleaf.level = ALL
+12 -1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.bootstrap</groupId>
@@ -105,6 +106,16 @@
<version>1.6</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>nz.net.ultraq.web.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.hibernate</groupId>
@@ -0,0 +1,46 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.autoconfigure;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link MessageSource}.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnMissingBean(MessageSource.class)
public class MessageSourceAutoConfiguration {
@Value("${spring.messages.basename:messages}")
private String basename;
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename(this.basename);
return messageSource;
}
}
@@ -36,4 +36,5 @@ import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
@ConditionalOnMissingBean(JpaRepositoryFactoryBean.class)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
public class JpaRepositoriesAutoConfiguration {
}
@@ -19,6 +19,7 @@ package org.springframework.bootstrap.autoconfigure.orm.jpa;
import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.ejb.HibernateEntityManager;
import org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
@@ -36,7 +37,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
* @author Phillip Webb
*/
@Configuration
@ConditionalOnClass(name = "org.hibernate.ejb.HibernateEntityManager")
@ConditionalOnClass(HibernateEntityManager.class)
@EnableTransactionManagement
public class HibernateJpaAutoConfiguration extends JpaAutoConfiguration {
@@ -53,7 +53,7 @@ public abstract class JpaAutoConfiguration implements BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Bean
public PlatformTransactionManager txManager() {
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
@@ -0,0 +1,155 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.autoconfigure.thymeleaf;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.Servlet;
import nz.net.ultraq.web.thymeleaf.LayoutDialect;
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.ConditionalOnMissingBean;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingClass;
import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.TemplateProcessingParameters;
import org.thymeleaf.resourceresolver.IResourceResolver;
import org.thymeleaf.spring3.SpringTemplateEngine;
import org.thymeleaf.spring3.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Thymeleaf templating.
*
* @author Dave Syer
*/
@Configuration
@ConditionalOnClass(SpringTemplateEngine.class)
public class ThymeleafAutoConfiguration {
@Configuration
@ConditionalOnMissingBean(name = "defaultTemplateResolver")
protected static class DefaultTemplateResolverConfiguration {
@Autowired
private ResourceLoader resourceLoader = new DefaultResourceLoader();
@Value("${spring.template.prefix:classpath:/templates/}")
private String prefix = "classpath:/templates/";
@Value("${spring.template.suffix:.html}")
private String suffix = ".html";
@Value("${spring.template.mode:HTML5}")
private String templateMode = "HTML5";
@Bean
public ITemplateResolver defaultTemplateResolver() {
TemplateResolver resolver = new TemplateResolver();
resolver.setResourceResolver(new IResourceResolver() {
@Override
public InputStream getResourceAsStream(
TemplateProcessingParameters templateProcessingParameters,
String resourceName) {
try {
return DefaultTemplateResolverConfiguration.this.resourceLoader
.getResource(resourceName).getInputStream();
} catch (IOException e) {
return null;
}
}
@Override
public String getName() {
return "SPRING";
}
});
resolver.setPrefix(this.prefix);
resolver.setSuffix(this.suffix);
resolver.setTemplateMode(this.templateMode);
return resolver;
}
}
@Configuration
@ConditionalOnMissingClass("nz.net.ultraq.web.thymeleaf.LayoutDialect")
@ConditionalOnMissingBean(SpringTemplateEngine.class)
protected static class ThymeleafDefaultConfiguration {
@Autowired
private Collection<ITemplateResolver> templateResolvers = Collections.emptySet();
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
for (ITemplateResolver templateResolver : this.templateResolvers) {
engine.addTemplateResolver(templateResolver);
}
return engine;
}
}
@Configuration
@ConditionalOnClass({ LayoutDialect.class })
@ConditionalOnMissingBean(SpringTemplateEngine.class)
protected static class ThymeleafWebLayoutConfiguration {
@Autowired
private Collection<ITemplateResolver> templateResolvers = Collections.emptySet();
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
for (ITemplateResolver templateResolver : this.templateResolvers) {
engine.addTemplateResolver(templateResolver);
}
engine.addDialect(new LayoutDialect());
return engine;
}
}
@Configuration
@ConditionalOnClass({ Servlet.class })
protected static class ThymeleafViewResolverConfiguration {
@Autowired
private SpringTemplateEngine templateEngine;
@Bean
@ConditionalOnMissingBean(name = "thymeleafViewResolver")
public ThymeleafViewResolver thymeleafViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(this.templateEngine);
return resolver;
}
}
}
@@ -18,6 +18,8 @@ package org.springframework.bootstrap.autoconfigure.web;
import javax.servlet.Servlet;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration.WebMvcConfiguration;
import org.springframework.bootstrap.context.annotation.ConditionalOnClass;
import org.springframework.bootstrap.context.annotation.ConditionalOnMissingBean;
@@ -25,11 +27,16 @@ import org.springframework.bootstrap.context.annotation.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
@@ -51,6 +58,9 @@ public class WebMvcAutoConfiguration {
@EnableWebMvc
public static class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Autowired
private ListableBeanFactory beanFactory;
@Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
@@ -62,6 +72,29 @@ public class WebMvcAutoConfiguration {
configurer.enable();
}
@Override
public void addFormatters(FormatterRegistry registry) {
for (Converter<?, ?> converter : this.beanFactory.getBeansOfType(
Converter.class).values()) {
registry.addConverter(converter);
}
for (GenericConverter converter : this.beanFactory.getBeansOfType(
GenericConverter.class).values()) {
registry.addConverter(converter);
}
for (Formatter<?> formatter : this.beanFactory
.getBeansOfType(Formatter.class).values()) {
registry.addFormatter(formatter);
}
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("/")
.addResourceLocations("classpath:/static")
.addResourceLocations("classpath:/");
}
}
}
@@ -36,7 +36,7 @@ import org.springframework.util.MultiValueMap;
*/
abstract class AbstractOnBeanCondition implements Condition {
private static Log logger = LogFactory.getLog(OnBeanCondition.class);
protected Log logger = LogFactory.getLog(getClass());
protected abstract Class<?> annotationClass();
@@ -53,8 +53,8 @@ abstract class AbstractOnBeanCondition implements Condition {
List<String> beanClassesFound = new ArrayList<String>();
List<String> beanNamesFound = new ArrayList<String>();
if (logger.isDebugEnabled()) {
logger.debug("Looking for beans with class: " + beanClasses);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking for beans with class: " + beanClasses);
}
for (String beanClass : beanClasses) {
try {
@@ -72,8 +72,8 @@ abstract class AbstractOnBeanCondition implements Condition {
}
}
if (logger.isDebugEnabled()) {
logger.debug("Looking for beans with names: " + beanNames);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking for beans with names: " + beanNames);
}
for (String beanName : beanNames) {
if (context.getBeanFactory().containsBeanDefinition(beanName)) {
@@ -82,8 +82,8 @@ abstract class AbstractOnBeanCondition implements Condition {
}
boolean result = evaluate(beanClassesFound, beanNamesFound);
if (logger.isDebugEnabled()) {
logger.debug("Finished matching and result is matches" + result);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Finished matching and result is matches: " + result);
}
return result;
}
@@ -0,0 +1,43 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional} that only matches when the specified classes are on the classpath.
*
* @author Dave Syer
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnMissingClassCondition.class)
public @interface ConditionalOnMissingClass {
/**
* The classes names that must be absent.
* @return the class names that must be absent.
*/
public String[] value() default {};
}
@@ -0,0 +1,38 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional} that only matches when the application context is a not a web
* application context.
*
* @author Dave Syer
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnNotWebApplicationCondition.class)
public @interface ConditionalOnNotWebApplication {
}
@@ -0,0 +1,38 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
/**
* {@link Conditional} that only matches when the application context is a web application
* context.
*
* @author Dave Syer
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {
}
@@ -61,6 +61,9 @@ class OnClassCondition implements Condition {
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Classes not found (search terminated with matches=true)");
}
return true;
}
@@ -0,0 +1,75 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* {@link Condition} that checks for the specific classes.
*
* @author Dave Syer
* @see ConditionalOnMissingClass
*/
class OnMissingClassCondition implements Condition {
private static Log logger = LogFactory.getLog(OnMissingClassCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
ConditionalOnMissingClass.class.getName(), true);
if (attributes != null) {
List<String> classNames = new ArrayList<String>();
collectClassNames(classNames, attributes.get("value"));
Assert.isTrue(classNames.size() > 0,
"@ConditionalOnMissingClass annotations must specify at least one class value");
for (String className : classNames) {
if (logger.isDebugEnabled()) {
logger.debug("Checking for class: " + className);
}
if (ClassUtils.isPresent(className, context.getClassLoader())) {
if (logger.isDebugEnabled()) {
logger.debug("Found class: " + className
+ " (search terminated with matches=false)");
}
return false;
}
}
}
return true;
}
private void collectClassNames(List<String> classNames, List<Object> values) {
for (Object value : values) {
for (Object valueItem : (Object[]) value) {
classNames.add(valueItem instanceof Class<?> ? ((Class<?>) valueItem)
.getName() : valueItem.toString());
}
}
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link Condition} that checks for a web application context and returns false if one is
* found.
*
* @author Dave Syer
* @see ConditionalOnNotWebApplication
*/
class OnNotWebApplicationCondition implements Condition {
private static Log logger = LogFactory.getLog(OnNotWebApplicationCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (!ClassUtils.isPresent(
"org.springframework.web.context.support.GenericWebApplicationContext",
null)) {
if (logger.isDebugEnabled()) {
logger.debug("Web application classes not found");
}
return true;
}
boolean result = !StringUtils.arrayToCommaDelimitedString(
context.getBeanFactory().getRegisteredScopeNames()).contains("session");
if (logger.isDebugEnabled()) {
logger.debug("Web application context found: " + !result);
}
return result;
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.context.annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.StandardServletEnvironment;
/**
* {@link Condition} that checks for a web application context.
*
* @author Dave Syer
* @see ConditionalOnWebApplication
*/
class OnWebApplicationCondition implements Condition {
private static Log logger = LogFactory.getLog(OnWebApplicationCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (!ClassUtils.isPresent(
"org.springframework.web.context.support.GenericWebApplicationContext",
null)) {
if (logger.isDebugEnabled()) {
logger.debug("Web application classes not found");
}
return false;
}
boolean result = StringUtils.arrayToCommaDelimitedString(
context.getBeanFactory().getRegisteredScopeNames()).contains("session")
|| context.getEnvironment() instanceof StandardServletEnvironment;
if (logger.isDebugEnabled()) {
logger.debug("Web application context found: " + result);
}
return result;
}
}
@@ -320,7 +320,7 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
*/
protected final File getValidDocumentRoot() {
File[] roots = new File[] { getDocumentRoot(), new File("src/main/webapp"),
new File("public") };
new File("public"), new File("static") };
for (File root : roots) {
if (root != null && root.exists() && root.isDirectory()) {
return root.getAbsoluteFile();
@@ -14,12 +14,14 @@
* limitations under the License.
*/
package org.springframework.bootstrap.logging;
package org.springframework.bootstrap.context.initializer;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.Map;
import org.springframework.bootstrap.logging.JavaLoggerConfigurer;
import org.springframework.bootstrap.logging.LogbackConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
@@ -198,7 +200,7 @@ public class LoggingInitializer implements
}
// Fallback to the default
String defaultPath = ClassUtils.getPackageName(LoggingInitializer.class);
String defaultPath = ClassUtils.getPackageName(JavaLoggerConfigurer.class);
defaultPath = defaultPath.replace(".", "/");
defaultPath = defaultPath + "/" + this.paths[this.paths.length - 1];
return "classpath:" + defaultPath;
@@ -1,13 +1,16 @@
org.springframework.bootstrap.context.annotation.EnableAutoConfiguration=\
org.springframework.bootstrap.autoconfigure.PropertyPlaceholderAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.MessageSourceAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.data.JpaRepositoriesAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.jdbc.EmbeddedDatabaseAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedContainerCustomizerConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedJettyAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.EmbeddedTomcatAutoConfiguration,\
org.springframework.bootstrap.autoconfigure.web.WebMvcAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
org.springframework.bootstrap.context.initializer.ConfigFileApplicationContextInitializer,\
org.springframework.bootstrap.context.initializer.LoggingInitializer,\
org.springframework.bootstrap.context.initializer.EnvironmentDelegateApplicationContextInitializer
@@ -290,7 +290,7 @@ public class SpringApplicationTests {
}
@Test
public void exitWithExplicitCOde() throws Exception {
public void exitWithExplicitCode() throws Exception {
SpringApplication application = new SpringApplication(ExampleConfig.class);
application.setWebEnvironment(false);
ApplicationContext context = application.run();
@@ -0,0 +1,60 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.autoconfigure;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;
import static org.junit.Assert.assertEquals;
/**
* @author Dave Syer
*
*/
public class MessageSourceAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Test
public void testDefaultMessageSource() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(MessageSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertEquals("Foo message",
this.context.getMessage("foo", null, "Foo message", Locale.UK));
}
@Test
public void testMessageSourceCreated() throws Exception {
this.context = new AnnotationConfigApplicationContext();
this.context.register(MessageSourceAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("spring.messages.basename", "test/messages");
this.context.getEnvironment().getPropertySources()
.addFirst(new MapPropertySource("test", map));
this.context.refresh();
assertEquals("bar",
this.context.getMessage("foo", null, "Foo message", Locale.UK));
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.bootstrap.autoconfigure.thymeleaf;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.Test;
import org.springframework.bootstrap.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.RequestContext;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring3.view.ThymeleafView;
import org.thymeleaf.spring3.view.ThymeleafViewResolver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @author Dave Syer
*/
public class ThymeleafAutoConfigurationTests {
@Test
public void createFromConfigClass() throws Exception {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ThymeleafAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("spring.template.mode", "XHTML");
map.put("spring.template.suffix", "");
context.getEnvironment().getPropertySources()
.addFirst(new MapPropertySource("test", map));
context.refresh();
TemplateEngine engine = context.getBean(TemplateEngine.class);
Context attrs = new Context(Locale.UK, Collections.singletonMap("foo", "bar"));
String result = engine.process("template.txt", attrs);
assertEquals("<html>bar</html>", result);
context.close();
}
@Test
public void createLayoutFromConfigClass() throws Exception {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(ThymeleafAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
MockServletContext servletContext = new MockServletContext();
context.setServletContext(servletContext);
context.refresh();
ThymeleafView view = (ThymeleafView) context.getBean(ThymeleafViewResolver.class)
.resolveViewName("view", Locale.UK);
MockHttpServletResponse response = new MockHttpServletResponse();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(RequestContext.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
view.render(Collections.singletonMap("foo", "bar"), request, response);
String result = response.getContentAsString();
assertTrue("Wrong result: " + result, result.contains("<title>Content</title>"));
assertTrue("Wrong result: " + result, result.contains("<span>bar</span>"));
context.close();
}
}
@@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.bootstrap.context.initializer.LoggingInitializer;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.PropertySource;
@@ -7,7 +7,8 @@
<pattern>${LOG_PATTERN}</pattern>
</encoder>
</appender>
<logger name="org.springframework.beans.factory.annotation" level="TRACE" />
<logger name="org.springframework.bootstrap.context.annotation" level="TRACE" />
<logger name="org.thymeleaf" level="TRACE" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
@@ -0,0 +1 @@
body: {background: red}
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout">
<head>
<title layout:fragment="title">Layout</title>
</head>
<body>
<div class="container">
<h1 layout:fragment="title">Layout</h1>
<div layout:fragment="content">
Fake content
</div>
</div>
</body>
</html>
@@ -0,0 +1 @@
<html th:text="${foo}">foo</html>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.1//EN"
"http://tiles.apache.org/dtds/tiles-config_2_1.dtd">
<tiles-definitions>
<definition name="*" template="layout">
<put-attribute name="content" value="content/{1}" />
<put-attribute name="title" value="title/{1}" />
</definition>
<definition name="content/*" template="{1} :: content" />
<definition name="title/*" template="{1} :: title" />
</tiles-definitions>
@@ -0,0 +1,10 @@
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/web/thymeleaf/layout" layout:decorator="layout">
<head>
<title layout:fragment="title">Content</title>
</head>
<body>
<div layout:fragment="content">
<span th:text="${foo}">foo</span>
</div>
</body>
</html>
@@ -0,0 +1 @@
foo=bar