Пример Web-приложения на языке Java с использованием Maven, Sitebricks и Guice.
Sitebricks ― это новая среда разработки Web-приложений на языке Java™, которая пока существует в виде бета-версии. Вы можете спросить: "Зачем мне еще одна среда разработки?" С помощью Google Sitebricks можно быстро создать Web-приложение, с которым смогут работать другие. Среда Sitebricks построена поверх Guice. Она расширяет и распространяет многие принципы Guice на Web. Как и Guice, она активно использует аннотации для хранения конфигурации как части кода. Чтобы использовать Guice, не нужно создавать и редактировать большое количество XML-файлов. Sitebricks позволяет создавать гораздо более лаконичные Web-приложения. Код получается простым. Глядя на код Sitebricks, сразу понимаешь, что происходит. Sitebricks не ставит под угрозу безопасность или производительность типов.
В этой статье на примере конкретного Web-приложения рассматриваются возможности Sitebricks. Пример приложения Reviewr позволяет читать и писать ресторанные обзоры. Для хранения данных приложения мы будем использовать базу данных (но не станем тратить на это слишком много времени). Пример фокусируется на тех частях приложения, в которых используются возможности Sitebricks.
Загрузите код примера приложения Reviewr по ссылке, приведенной в таблице в конце статьи.
Предварительные требования
В разделе
Ресурсы содержатся ссылки на программы, которые нам потребуются.
- Sitebricks находится на стадии бета-версии; в этой статье используется версия 0.8.
- В настоящее время самый простой способ работы с Sitebricks ― с помощью Maven. Для этой статьи используется Maven 2.0.9. Конечно, Maven потянет за собой все прочие зависимости, необходимые для Sitebricks, такие как Google Guice.
- Sitebricks опирается на некоторые расширенные возможности Java, так что нам потребуется JDK 1.6. В этой статье применяется JDK 1.6.0_17.
- Для установки примера приложения из этой статьи можно использовать Java-сервер Web-приложений, но это не обязательно.
Пример приложения включает в себя встроенный сервер Jetty, которые можно использовать для тестирования.
Guice применяется в Sitebricks и в примере приложения. Было бы полезно знакомство с Guice или другой средой внедрения зависимостей, такой как Spring.
Использование Maven для создания Sitebricks-приложения
Как и многие современные Java-среды, Sitebricks использует Maven. Для создания нового проекта многие среды используют систему архетипа Maven. Это же планируется и для Sitebricks, но на момент написания статьи еще не реализовано. Поэтому в примере применяется несколько более ручной подход. Мы будем следовать передовому опыту Maven и сначала создадим проект бизнес-логики, как показано в листинге 1.
Листинг 1. Создание проекта бизнес-логики с применением Maven
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -DartifactId=reviewrMain
[INFO] Scanning for projects...
|
Код создает простую структуру проекта, характерную для Java-приложений, которая в конечном итоге будет упакована в файл JAR. Следующий шаг заключается в создании второго проекта, который будет Web-приложением, как показано ниже.
Листинг 2. Создание Web-приложения Java с использованием Maven
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -
DartifactId=reviewrWeb -DarchetypeArtifactId=maven-archetype-webapp
[INFO] Scanning for projects...
|
Похоже на листинг 1, но на этот раз идентификатором архетипа служит maven-archetype-webapp
. Это основной архетип в Maven, который создает базовый проект Web-приложения Java. Теперь, когда проекты созданы, откроем их файлы pom и добавим Sitebricks. Сначала сделаем это для основного
проекта бизнес-логики, как показано в листинге 3.
Листинг 3. Файл pom.xml основного проекта.
<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/
maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.developerworks.reviewr</groupId>
<artifactId>reviewrMain</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>reviewrMain</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>sonatype-google-snapshots</id>
<name>Sonatype Google Snapshots Repository</name>
<url>http://oss.sonatype.org/content/repositories/
google-snapshots/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.sitebricks</groupId>
<artifactId>sitebricks</artifactId>
<version>0.8-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.2.2.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
|
Этот файл pom содержит несколько изменений по сравнению с первоначальной версией, созданной в листинге 1.
- Мы добавили репозиторий
http://oss.sonatype.org/content/repositories/google-snapshots/
, где можно найти артефакты Maven для Sitebricks и Guice.
- В разделе зависимостей мы добавили две зависимости:
- зависимость от Sitebricks, которая будет включаться во все JAR-файлы Sitebricks, необходимые для проекта;
- зависимость от базы данных Apache Derby, которая используется в этом проекте. Derby - это встроенная база данных, входящая в Java 6. Главное, что дает эта зависимость ― встроенный драйвер JDBC, необходимый Derby.
Ниже показан файл pom проекта Web-приложения.
Листинг 4. Файл pom.xml Web-приложения
<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/
maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.developerworks.reviewr</groupId>
<artifactId>reviewrWeb</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>reviewrWeb Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.developerworks.reviewr</groupId>
<artifactId>reviewrMain</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>reviewrWeb</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
|
В листинге 4 внесены два небольших изменения. Добавлена зависимость от основного проекта, которая сделает код, который мы пишем, зависимым от основного проекта, а также от всех зависимостей основного проекта (в том числе от Sitebricks). Кроме того, добавлен плагин Jetty, который позволяет запустить встроенный Web-сервер Jetty и выполнить на нем приложение. Теперь, когда проекты созданы, можно приступить к использованию Sitebricks.
Настройка Sitebricks с помощью Guice
Одно из преимуществ использования Sitebricks заключается в том, что он требует минимального количества настроек. Во-первых, нужно изменить файл web.xml приложения, как показано в листинге 5.
Листинг 5. web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Reviewr</display-name>
<filter>
<filter-name>webFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>webFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.developerworks.reviewr.AppInjector</listener-class>
</listener>
</web-app>
|
Приведенный выше код создает сервлет фильтра, который будет перехватывать входящие запросы HTTP. Вместо того чтобы создать свой собственный фильтр, мы используем GuiceFilter в составе Guice. Для фильтра указываем /*
(все запросы направляются на этот фильтр).
В листинге 5 устанавливается также перехватчик контекста сервлета. Он вызывается при запуске Web-приложения. Это делается для настройки Sitebricks при запуске приложения. Напишем специальный класс AppInjector
, как показано в листинге 6.
Листинг 6. Настройка Sitebricks с помощью AppInjector
public class AppInjector extends GuiceServletContextListener {
@Override
public Injector getInjector() {
Module module = new SitebricksModule() {
@Override
protected void configureSitebricks() {
scan(ShowRestaurants.class.getPackage());
bind(RestaurantDao.class).to(RestaurantEmbeddedJdbcDao.class);
bind(ReviewDao.class).to(ReviewEmbeddedJdbcDao.class);
bind(String.class)
.annotatedWith(Names.named("connStr"))
.toInstance("jdbc:derby:reviewrDb;create=true");
bind(DbManager.class).to(EmbeddedDbManager.class);
}
};
Injector injector = Guice.createInjector(module);
return injector;
} |
Приведенный выше код должен показаться знакомым тем, кто знаком с Guice. Он создает модуль Guice. Обычно в Guice реализуют метод configure
, но в данном случае используется специальный подкласс Sitebricks Module
с реализацией его метода configureSitebricks
. Здесь-то и производится вся типовая настройка Guice: связывание интерфейсов для их реализации и настройка свойств приложения.
В примере применяется API scan
, уникальный для Sitebricks. Он принимает пакет Java и добавляет его содержимое в классы, управляемые Sitebricks. Мы рассмотрим его использование ниже.
Это все, что требуется для настройки приложения. Большая часть этих настроек на самом деле относится к конкретному приложению, например, все привязки вызовов AppInjector
. Они не обязательны, но позволяют использовать Guice для упрощения написания бизнес-логики. Если нужен только Sitebricks, достаточно web.xml и вызова scan
. Теперь, когда настройка завершена, можно написать логику приложения.
Отображение результатов
На первой странице приложения будут показаны все рестораны, имеющиеся в системе, и пользователи смогут фильтровать список ресторанов по блюдам, которые в них подают. Sitebricks ― это среда в стиле модель-представление-управление(model-view-control - MVC), но в данном приложении мы сосредоточимся на контроллерах и представлении. Контроллеры ― это простые классы Java. Контроллер для отображения всех ресторанов показан в листинге 7.
Листинг 7. Контроллер ShowRestaurants
@At("/")
public class ShowRestaurants {
private List<Restaurant> restaurants;
private String category;
private LinkedHashSet<String> categories;
private final RestaurantDao dao;
@Inject
public ShowRestaurants(RestaurantDao dao){
this.dao = dao;
}
@Get
public void get(){
this.restaurants = dao.findAll();
categories = new LinkedHashSet<String>(restaurants.size());
for (Restaurant restaurant : restaurants){
categories.add(restaurant.getCategory());
}
if (category != null && category.trim().length() > 0){
List<Restaurant> filtered = new ArrayList<Restaurant>
(restaurants.size());
for (Restaurant restaurant : restaurants){
if (restaurant.getCategory().equalsIgnoreCase(category)){
filtered.add(restaurant);
}
}
restaurants = filtered;
}
}
// геттеры и сеттеры опущены для краткости
}
|
Обратите внимание, что этот класс снабжен аннотацией @At
, которая представляет собой аннотацию Sitebricks. Значение "/"
указывает Sitebricks на то, что любые входящие запросы для "/"
нужно отображать на этот класс. Он будет служить контроллером "домашней страницы" приложения.
Sitebricks точно следует соглашениям HTTP и REST, так что можно использовать разные методы для разных HTTP-методов, таких как GET и POST. Аннотация @Get
указывает, какие методы использовать для запросов GET. Здесь можно указать любой метод; никакой связи с "get". Аннотация определяет, какой метод будет вызван.
Указанные выше класс имеет несколько членов-переменных. RestaurantDao
- это объект, который используется для получения данных, необходимых странице. Аннотация @Inject
конструктора для ShowRestaurants
указывает Guice, что нужно внедрить RestaurantDao
Другие члены-переменные, restaurants , categories и category , образуют модель данных страницы. Переменные restaurants и categories используются в представлении.
Переменная variable на самом деле служит параметром запроса. Sitebricks автоматически связывает с ней переменную запроса с тем же именем. Например, если запрос относится к http://<app>?category=Thai
, то категория будет равна Thai. В нашем случае, если категория задана, то она используется для фильтрации отображаемого списка ресторанов.
В листинге 8 показан код представления, использующий модель данных, построенную контроллером.
Листинг 8. Представление ShowRestaurants
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title>Reviewr</title>
</head>
<body>
<h1>All Restaurants</h1>
<form method="get">
<label for="category">Select category:</label>
<select name="category" id="category">
<option value="" label="Any"/>
@Repeat(items=categories, var="category")
<option>${category}</option>
</select>
<input type="submit" value="Filter"/>
</form>
<table class=borderall>
<thead>
<tr>
<td>Name</td>
<td>Category</td>
<td>Average Rating</td>
</tr>
</thead>
<tbody>
@Repeat(items=restaurants, var="restaurant")
<tr><td><a
href="/restaurant/${restaurant.name}">${restaurant.name}</a></td>
<td>${restaurant.category}</td><td>${restaurant.averageRating}
</td></tr>
</tbody>
</table>
<div class="msg">Not in the list?
<a href="/restaurants/new">Add a restaurant!</a>
</div>
</body>
</html>
|
Имя ShowRestaurants.html, указанное выше, совпадает с именем контроллера. Это соглашение используется Sitebricks, так что его не надо задавать через настройки. HTML ― это в основном чистый HTML; без скриптлетов (то есть без JSP). Виджет Sitebricks @Repeat
используется для перебора элементов коллекции. Для оценки виджетов он использует язык выражений MVEL. MVEL - это расширение типичного языка выражений JSP/JSF. Код создает таблицу ресторанов, где название каждого ресторана также является гиперссылкой на /restaurant/${restaurant.name}
. Например, для ресторана TCBY ссылка будет на /restaurant/TCBY
. Подобные динамические URL ― основа Sitebricks. В листинге 9 показано, как они работают с контроллером, который обрабатывает данный пример.
Листинг 9. Контроллер RestaurantDetails
@At("/restaurant/:name")
public class RestaurantDetails {
private final RestaurantDao dao;
private final ReviewDao reviewDao;
private final Logger logger;
private Restaurant restaurant;
private String text;
private String author;
private Integer rating;
private Integer restaurantId;
@Inject
public RestaurantDetails(RestaurantDao dao, ReviewDao reviewDao, Logger logger){
this.dao = dao;
this.reviewDao = reviewDao;
this.logger = logger;
}
@Get
public void get(@Named("name") String name){
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, null, ex);
}
this.restaurant = dao.findByName(name);
reviewDao.getReviewsForRestaurant(restaurant);
}
@Post
public String addReview(@Named("name") String name){
try {
name = URLDecoder.decode(name, "UTF-8");
} catch (UnsupportedEncodingException ex) {
logger.log(Level.SEVERE, null, ex);
}
restaurant = dao.findByName(name);
reviewDao.create(restaurant, text, author, rating);
return "/reviewrWeb/restaurant/"+restaurant.getName();
}
public boolean getNoReviews() {
return this.restaurant.getReviews().size() == 0;
}
// геттеры и сеттеры опущены для краткости
}
|
Опять же, обратите внимание на аннотацию @At
. На этот раз ее значение - "/restaurant/:name"
. :name
используется, чтобы сообщить Sitebricks, что это динамическая переменная. URL будет соответствовать указанному правилу, и последнюю часть URL будет помещена в переменную с именем name . Это переменная с нормальным для Guice именем.
Метод get
имеет входной параметр с аннотацией @Named("name")
. Поэтому Guice внедряет значение, извлеченное из URL, в эту переменную при вызове метода get
(который вызывается для запросов GET
в силу аннотации @Get
).
Пример выполняет декодирование URL переменной, поскольку она входит в состав URL, а не как параметр HTTP-запроса, где декодирование уже выполнено. Иначе Elephant Bar
представлялся бы как Elephant%20Bar
. Вы могли заметить, что в этом классе есть также метод addReview
с аннотацией @Post
.
Передача формы
Обработка форм может оказаться одной из наиболее трудоемких задач при разработке Web-приложений. Как и следовало ожидать, Sitebricks блистает и в этой области. В листинге 9 показан контроллер страницы детальных сведений о ресторане. Код представления показан в листинге 10.
Листинг 10. Сведения о ресторане
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<title></title>
</head>
<body>
<h1>${restaurant.name}</h1>
<h2>Category: ${restaurant.category}</h2>
<h2>Average Rating: ${restaurant.averageRating}</h2>
<h2>Reviews</h2>
@ShowIf(noReviews)
<p>This restaurant has no reviews yet.</p>
@Repeat(items=restaurant.reviews, var="review")
<div class="review">${review.author}:${review.rating}<br/>${review.text}</div>
<form method="post">
<label for="author">Your name:</label>
<input type="text" id="author" name="author"/>
<label for="rating">Your rating:</label>
<select name="rating">
<option value="0" label="0"/>
<option value="1" label="1"/>
<option value="2" label="2"/>
<option value="3" label="3"/>
<option value="4" label="4"/>
<option value="5" label="5"/>
</select><br/>
<label for="text">Write your review:</label><br/>
<textarea id="text" name="text" cols="60" rows="40"/><br/>
<input type="hidden" name="restaurantId" value="${restaurant.id}"/>
<input type="submit" value="Submit your review"/>
</form>
</body>
</html>
|
Следуя соглашению об именах, этот файл называется RestaurantDetails.html. В начале файла есть HTML-код для отображения информации о ресторане и всех отзывов о нем. Для этого используется @ShowIf
, другой важный виджет Sitebricks. Параметр для этого виджета - логическая переменная, которая в данном случае называется noReviews
. Еще влистинге 9 переменной с таким именем не было, но был метод getNoReviews
. Он соответствует соглашению об именах JavaBeans и вызывается после анализа виджета @ShowIf
.
Более интересная часть HTML-шаблона - это форма в его нижней части. В ней используется метод post
, как и должно быть в любой форме, которая меняет сервер (в данном случае, добавляя обзор в базу данных). Она возвращает данные себе же, так что будет использоваться тот же контроллер. В листинге 9 видно, что существует другой метод, addReview
с аннотацией @Post
. Это метод, который вызывается при передаче формы. Здесь используется тот же шаблон имен значений, что и для метода get
.
Взгляните на каждый из элементов формы и на их имена. Эти элементы совпадают с переменными-членами классаRestaurantDetails
. Sitebricks автоматически связывает данные формы с членами-переменными, как в случае параметров запроса. Это значительно облегчает задачу разработчика. Метод addReview
, который в Sitebricks является методом переадресации, возвращает строку. Он просто возвращает строку с относительным URL переадресации. В данном случае это тот же URL сведений о ресторане, так что будет перерисована страница с подробностями. Его можно легко заменить на URL главной или любой другой страницы.
Заключение
В этой статье говорится о том, насколько Google Sitebricks удобен для разработки Web-приложений Java. Мы рассмотрели все необходимое: создание проекта, использующего Sitebricks, настройку Sitebricks, использование его виджетов и отображение адресов URL. В статье рассмотрены также некоторые другие удобные функции, такие как динамические URL с именованными переменными и связывание форм.
Sitebricks - это компактная среда разработки с универсальными инструментами для быстрого создания Web-приложений. Он распространяет на Web философию Guice. Лучше всего, что Sitebricks ― еще молодая среда. Следите за новыми функциями и экосистемой, складывающейся вокруг Sitebricks.