Календарь на Май 2024 года: calendar2008.ru/2024/may/
Навигация
Главная »  Xml 

Google Sitebricks для отличных Web-приложений


Источник: ibm
Пример 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 основного проекта.
 4.0.0 org.developerworks.reviewr reviewrMain jar 1.0-SNAPSHOT reviewrMain http://maven.apache.org   sonatype-google-snapshots Sonatype Google Snapshots Repository http://oss.sonatype.org/content/repositories/ google-snapshots/  true   true      junit junit 4.7 test   com.google.sitebricks sitebricks 0.8-SNAPSHOT   org.apache.derby derby 10.2.2.0      org.apache.maven.plugins maven-compiler-plugin 2.1  1.6 1.6      

Этот файл 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-приложения
 4.0.0 org.developerworks.reviewr reviewrWeb war 1.0-SNAPSHOT reviewrWeb Maven Webapp http://maven.apache.org   org.developerworks.reviewr reviewrMain 1.0-SNAPSHOT    reviewrWeb   org.mortbay.jetty maven-jetty-plugin     

В листинге 4 внесены два небольших изменения. Добавлена зависимость от основного проекта, которая сделает код, который мы пишем, зависимым от основного проекта, а также от всех зависимостей основного проекта (в том числе от Sitebricks). Кроме того, добавлен плагин Jetty, который позволяет запустить встроенный Web-сервер Jetty и выполнить на нем приложение. Теперь, когда проекты созданы, можно приступить к использованию Sitebricks.

Настройка Sitebricks с помощью Guice

Одно из преимуществ использования Sitebricks заключается в том, что он требует минимального количества настроек. Во-первых, нужно изменить файл web.xml приложения, как показано в листинге 5.

Листинг 5. web.xml
   Reviewr  webFilter com.google.inject.servlet.GuiceFilter    webFilter /*    org.developerworks.reviewr.AppInjector   

Приведенный выше код создает сервлет фильтра, который будет перехватывать входящие запросы 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 restaurants; private String category; private LinkedHashSet categories; private final RestaurantDao dao;  @Inject public ShowRestaurants(RestaurantDao dao){ this.dao = dao; }      @Get public void get(){ this.restaurants = dao.findAll(); categories = new LinkedHashSet(restaurants.size()); for (Restaurant restaurant : restaurants){ categories.add(restaurant.getCategory()); } if (category != null && category.trim().length() > 0){ List filtered = new ArrayList (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://?category=Thai, то категория будет равна Thai. В нашем случае, если категория задана, то она используется для фильтрации отображаемого списка ресторанов.

В листинге 8 показан код представления, использующий модель данных, построенную контроллером.

Листинг 8. Представление ShowRestaurants
    Reviewr   

All Restaurants

@Repeat(items=restaurants, var="restaurant")
Name Category Average Rating
${restaurant.name} ${restaurant.category}${restaurant.averageRating}
Not in the list? Add a restaurant!

Имя 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. Сведения о ресторане
       

${restaurant.name}

Category: ${restaurant.category} Average Rating: ${restaurant.averageRating} Reviews @ShowIf(noReviews) This restaurant has no reviews yet.

@Repeat(items=restaurant.reviews, var="review")
${review.author}:${review.rating}
${review.text}