Создание лучших Web-приложений с помощью Google Sitebricks

Пример Web-приложения на языке Java с использованием Maven, Sitebricks и Guice

Из этой статьи вы узнаете, как создавать Web-приложения на базе Google Sitebricks.

Майкл Галпин, инженер по программному обеспечению, Vitria Technology

Майкл Галпин (Michael Galpin) имеет учёную степень по математике в Калифорнийском Технологическом институте. Он является Java-разработчиком с конца 90-х гг. и работает инженером по программному обеспечению в Vitria Technology, в Саннивейл, Калифорния.



31.08.2011

Введение

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 border="1">
          <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.

Присоединитесь к группе Web-разработки на сайте "Мой DeveloperWorks"

Обсудите темы и поделитесь ресурсами по Web-разработке с другими программистами в группе Web-разработки "Мой developerWorks".

Вы - не член сообщества "Мой DeveloperWorks"? Присоединяйтесь!

Взгляните на каждый из элементов формы и на их имена. Эти элементы совпадают с переменными-членами класса RestaurantDetails. Sitebricks автоматически связывает данные формы с членами-переменными, как в случае параметров запроса. Это значительно облегчает задачу разработчика. Метод addReview, который в Sitebricks является методом переадресации, возвращает строку. Он просто возвращает строку с относительным URL переадресации. В данном случае это тот же URL сведений о ресторане, так что будет перерисована страница с подробностями. Его можно легко заменить на URL главной или любой другой страницы.


Заключение

В этой статье говорится о том, насколько Google Sitebricks удобен для разработки Web-приложений Java. Мы рассмотрели все необходимое: создание проекта, использующего Sitebricks, настройку Sitebricks, использование его виджетов и отображение адресов URL. В статье рассмотрены также некоторые другие удобные функции, такие как динамические URL с именованными переменными и связывание форм.

Sitebricks – это компактная среда разработки с универсальными инструментами для быстрого создания Web-приложений. Он распространяет на Web философию Guice. Лучше всего, что Sitebricks ― еще молодая среда. Следите за новыми функциями и экосистемой, складывающейся вокруг Sitebricks.


Загрузка

ОписаниеИмяРазмер
Исходный код для этой статьиreviewr.zip108 КБ

Ресурсы

Научиться

Получить продукты и технологии

  • Загрузите Sitebricks из Google Code и получите всю последнюю информацию.(EN)
  • Загрузите Guice из Google Code.
  • Загрузите Java SDK. (Для этой статьи использовалась версия JDK 1.6.0_17.) (EN)
  • Загрузите Maven. (Для этой статьи использовалась версия 2.0.0.)(EN)

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=SOA и web-сервисы, Технология Java, Web-архитектура
ArticleID=754998
ArticleTitle=Создание лучших Web-приложений с помощью Google Sitebricks
publish-date=08312011