[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
pull/1/merge
Dave Syer 12 years ago
parent ceea71fc38
commit 53078c320e

1
.gitignore vendored

@ -10,3 +10,4 @@
bin
build
target
.springBeans

@ -310,6 +310,11 @@
<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>
<version>1.0.1.Final</version>
@ -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

@ -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

@ -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,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,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>
Loading…
Cancel
Save