Как стать автором
Обновить

Запускаем JSF2 приложение на GAE

Время на прочтение6 мин
Количество просмотров235
Фреймворк Java Server Faces достиг версии 2.0 и близок к официальному релизу в качестве одного из стандартов JEE 6 (выход ожидается в ноябре-декабре). Изменений, по сравнению с предыдущей версией (1.2), — масса, и многие возлагают надежды на то, что этот релиз будет также успешен и приветливо встречен, как в свое время EJB3, поскольку большинство нареканий к предыдущим версиям устранено, в стандарт вошли большинство перспективных и опробованных на практике наработок из таких фреймворков, как JBoss Seam, RichFaces, Pretty Faces и др.

Мы попробуем использовать эту технологию на Google App Engine — популярной облачной платформе.

UPDATE Дабы не мучить читателей, не обладающих изрядным запасом терпения и крепкими нервами, сразу скажу, что результаты эксперимента можно поглядеть здесь, а воспользоваться ими здесь.

Если быть точным, то нас, конечно, интересует Google App Engine для Java, стартовавшая в апреле 2009, встреченная с восторгом, но оказавшаяся весьма капризной. Неполнота J2SE реализации, недочеты в библиотеках, ограничения на вызовы API приводят к тому, что далеко не все фреймворки могут быть нормально использованы (и даже загружены) в GAE-приложении, впрочем, список счастливчиков не такой уж и маленький.

Первоначально JSF не запускался на GAE (точнее запускалась устаревшая реализация MyFaces 1.1), сейчас, судя по предыдущей ссылке, 1.2 можно запустить, подкрутив отверткой, и, совсем недавно, появилась статья о том, каким бубном нужно помахать в сторону JSF 2.0. Овчинка стоит выделки и решено было применить знания на практике.

Итак, нам нужно основательно подредактировать web.xml, добавить кучку библиотек, конфигурация JSF2 стала теперь минимальной… и поместить все это в Eclipse, дополненный неплохим Google Plugin for Eclipse. К нашему пущему удовольствию каркас проекта заботливо выложили в открытый доступ — бери и пользуйся.

После выкачивания этого дела из svn репозитария приложение запускается в имитаторе GAE (кнопочкой Debug as Web application), и радостно выводит много красненького текста, вроде
05.11.2009 23:53:10 com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: failed com.google.apphosting.utils.jetty.DevAppEngineWebAppContext@1b2b131
java.lang.NoClassDefFoundError: javax.naming.InitialContext is a restricted class. Please see the Google App Engine developer's guide for more details.
at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
at com.sun.faces.config.WebConfiguration.processJndiEntries(WebConfiguration.java:578)
at com.sun.faces.config.WebConfiguration.<init>(WebConfiguration.java:114)
at com.sun.faces.config.WebConfiguration.getInstance(WebConfiguration.java:174)
at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:157)
at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:530)
at org.mortbay.jetty.servlet.Context.startContext(Context.java:135)
...

При попытке зайти на localhost:8080, уже без удивления видим

HTTP ERROR: 404

NOT_FOUND

RequestURI=/home.jsf


Powered by jetty://




Из коробки не работает. Впрочем, Google уверенно стремится быть самовосстанавливающейся системой (как всякий приличный кандидат в Skynet'ы), поэтому в поиске находим бубен покрупнее.

Автора используемой нами статьи сложно винить, поддержку JSF2 сломали в недавно вышедшей GAE SDK 1.2.6, поэтому, скрепя сердце и все еще надеясь на светлое будущее, применяем костыльбубен и пробуем еще раз.
You are now up and running with JavaServer Faces 2.0 on the Google App Engine

Первая победа, двигаемся дальше. Для проверки возможностей JSF2 пишем тривиальный ManagedBean

@ManagedBean(name = "helloBean")
@SessionScoped
public class HelloWorld implements Serializable {
  private static final long serialVersionUID = 1L;
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

* This source code was highlighted with Source Code Highlighter
.

и facelet:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><br><html xmlns="http://www.w3.org/1999/xhtml"<br>   xmlns:f="http://java.sun.com/jsf/core"   <br>   xmlns:h="http://java.sun.com/jsf/html"><br>  <f:view contentType="text/html"/><br>  <h:head><br>    <title>Hello World!</title><br>  </h:head><br>  <h:body bgcolor="white"><br>    <h2>My name is Duke. What is yours?</h2><br>    <h:form id="helloForm" ><br>      <h:graphicImage id="waveImg" url="#{resource['wave.med.gif']}" /><br>      <h:inputText id="username" value="#{helloBean.name}"/><br>      <h:commandButton id="submit" action="response" value="Submit"/><br>    </h:form><br>  </h:body><br></html><br><br>* This source code was highlighted with Source Code Highlighter.


Меняем имя приложения (appengine-web.xml), нажимаем волшебную кнопку «Deploy App Engine Project», вводим параметры google account, и заходим на xxx.appspot.com чтобы…
Посмотреть на ошибку 500.

Дальше начинается использование контрабандных бубнов, откуда их взять я не готов объяснить, единственное, что я пробовал — добавить в web.xml параметр:
 <listener><br>  <listener-class>com.sun.faces.config.ConfigureListener</listener-class><br> </listener><br><br>* This source code was highlighted with Source Code Highlighter.

но потом выяснилось, что он никакой роли не играет. В общем, после второй попытки deploy исключение:
java.lang.IllegalStateException: Application was not properly initialized at startup, could not find Factory: javax.faces.lifecycle.LifecycleFactory
at javax.faces.FactoryFinder$FactoryManager.getFactory(FactoryFinder.java:804)
at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:306)
at com.sun.faces.lifecycle.ELResolverInitPhaseListener.afterPhase(ELResolverInitPhaseListener.java:94)
at com.sun.faces.lifecycle.Phase.handleAfterPhase(Phase.java:189)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:107)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
...
пропало и больше не появлялось.

Двигаемся дальше. Для полного счастья следующего шага к совершенству нужно улучшить EL-процессор. Внимательно читавшим первую статью на английском было видно, что для нормальной работы JSF2 в GAE el-реализацию уже заменили. Но В JEE6 RI (aka glassfish v3) уже давно имеется реализация новой спецификации которая позволяет использовать вызовы методов в el-выражениях. Пробуем несколько вариантов — первый: JEUL, без результатов, второй — выцепить реализацию el из glassfish, тоже неудача: в новом el изменено API, которое, к сожалению, в GAE вшито в платформу.

Следующая попытка — удается со второго раза, используем JBoss EL (с успехом задействованное в Seam), но не последнюю версию 2.x (там работают с многопоточностью, что — КЮ с точки зрения GAE), а последнюю ревизию версии 1.0.

Локальный тест. Загрузка на GAE. Работает.

Счастье было так близко, но после небольшой активности (щелканья по кнопочкам форм), получаем пустой экран, который, увы, так и остается пустым, куда бы мы не зашли в нашем приложении. К статье про JSF2 бубен внизу даны несколько ссылок, среди которых JavaServer Faces 2.0 and Google AppEngine Compatibility Issues, где мы узнаем о

com.google.appengine.api.datastore.DatastoreTimeoutException


In the grand tradition of practicing what you preach, the Google AppEngine for Java leverages the Datastore (built atop BigTable) and Memcache frameworks for session persistence. It seems that, over time, a small percentage of Datastore operations will fail.

When attempting to display a web page containing JSF input components, a «com.google.appengine.api.datastore.DatastoreTimeoutException» (sample stack trace) may be encountered by your web application. Once this error page is encountered in your application, no other page will work until your session times out.

According to Don Schwartz of Google, the next release of the Google AppEngine for Java will include «improved retry and error handling logic» that are intended to lessen the likelihood of frameworks such as JSF, Struts, etc. encountering this problem.

И правда, удаляем cookie JSESSIONID и получаем работающее приложение до следующего сбоя.
Благородный Дон Шварц обещал это исправить еще в SDK 1.2.2, но сейчас уже 1.2.6, и по опыту с моим примитивным приложением трудно считать, что «small percentage of Datastore operations will fail». Падает почти сразу. Хорошо, что в этой же статье предлагается workaround: использовать javax.faces.STATE_SAVING_METHOD = client. Пробуем — помогает.

После такого бега с барьерами доверия к GAE+JSF2 изрядно поубавилось, но, тем не менее, попытка перенести и запустить один из примеров (ajax-component) увенчалась успехом, который и был оформлен в виде работающего приложения и проекта на google code.

P.S. Думаю, в дальнейшем можно ожидать расширения JEE6 стека в тестовом примере для GAE. Первым кандидатом идет WELD (aka JCDI RI, aka JSR299 RI, aka WebBeans), потом Jersey (aka JAX-RS 1.1 RI), на который, похоже, тоже имеется бубен.

Терпеливым спасибо за внимание.
Теги:
Хабы:
Всего голосов 8: ↑7 и ↓1+6
Комментарии0

Публикации

Работа

Java разработчик
170 вакансий

Ближайшие события