На прошлой неделе я потратил несколько часов на обновление
AppFuse с JSF 1.2 до JSF 2.0. На самом деле я обновил
MyFaces 1.2.7 до 2.0.4, но все реализации JSF должны быть одинаковыми, верно? В целом, это было довольно простое обновление с несколькими незначительными специфическими для AppFuse вещами. Моя цель в обновлении состояла в том, чтобы сделать все возможное, чтобы все заработало, и оставить интеграцию функций JSF 2 на более поздний срок.
В дополнение к обновлению MyFaces мне пришлось обновить Tomahawk, изменив artifactId зависимости на tomahawk20 . Я также смог удалить следующий слушатель из моего web.xml:
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
<listener>
После этого я обнаружил, что MyFaces использует новый URI (/javax.faces.resource/) для обслуживания некоторых своих файлов ресурсов. Я любезно попросил Spring Security игнорировать эти запросы, добавив следующее в мой файл security.xml.
<intercept-url pattern="/javax.faces.resource/**" filters="none"/>
Поскольку JSF 2 по умолчанию включает Facelets, я попытался удалить Facelets в качестве зависимости. После этого я получил следующую ошибку:
ERROR [308855416@qtp-120902214-7] ViewHandlerWrapper.fillChain(158) | Error instantiation parent Faces ViewHandler java.lang.ClassNotFoundException: com.sun.facelets.FaceletViewHandler at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50) at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:244) at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:230) at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:401) at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363) at org.ajax4jsf.framework.ViewHandlerWrapper.fillChain(ViewHandlerWrapper.java:144) at org.ajax4jsf.framework.ViewHandlerWrapper.calculateRenderKitId(ViewHandlerWrapper.java:68) at org.apache.myfaces.lifecycle.DefaultRestoreViewSupport.isPostback(DefaultRestoreViewSupport.java:179) at org.apache.myfaces.lifecycle.RestoreViewExecutor.execute(RestoreViewExecutor.java:113) at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:171) at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:189)
Это было вызвано следующим элементом в моем файле web.xml …
<context-param>
<param-name>org.ajax4jsf.VIEW_HANDLERS</param-name>
<param-value>com.sun.facelets.FaceletViewHandler</param-value>
</context-param>
… Я удалил его и попробовал еще раз. На этот раз я получил NoClassDefFoundError:
java.lang.NoClassDefFoundError: com/sun/facelets/tag/TagHandler at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141) at java.net.URLClassLoader.defineClass(URLClassLoader.java:283) at java.net.URLClassLoader.access$000(URLClassLoader.java:58) at java.net.URLClassLoader$1.run(URLClassLoader.java:197) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:190) at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:392) at org.mortbay.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:363) at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:247) at org.apache.myfaces.shared_impl.util.ClassUtils.classForName(ClassUtils.java:184) at org.apache.myfaces.view.facelets.util.ReflectionUtil.forName(ReflectionUtil.java:67)
Поскольку казалось, что все работает с Facelets в пути к классам, я решил сохранить эту головную боль на более поздний срок. Я ввел две проблемы в JIRA AppFuse: одну для удаления Facelets и одну для замены Ajax4JSF на RichFaces .
Следующей проблемой, с которой я столкнулся, было перенаправление со страницы подсказки пароля AppFuse. Правило навигации для этой страницы следующее:
navigation-rule>
<from-view-id>/passwordHint.xhtml</from-view-id>
<navigation-case>
<from-outcome>success</from-outcome>
<to-view-id>/login</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
В JSF 2.0 правило изменяет URL-адрес на /login.xhtml при перенаправлении (где оно было оставлено как / login с 1.2), и оно было обнаружено настройкой безопасности в моем web.xml, которая запрещает пользователям просматривать необработанные шаблоны.
<security-constraint>
<web-resource-collection>
<web-resource-name>Protect XHTML Templates</web-resource-name>
<url-pattern>*.xhtml</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
Чтобы решить эту проблему, мне пришлось внести пару изменений:
- Закомментируйте ограничение безопасности в файле web.xml и переместите его в файл security.xml Spring Security.
<intercept-url pattern="/**/*.xhtml" access="ROLE_NOBODY"/>
- Добавьте правило в urlrewrite.xml, которое перенаправляет обратно на страницу входа (так как login.xhtml не существует, и я использую URL без расширений).
<rule match-type="regex">
<from>^/login.xhtml$</from>
<to type="redirect">%{context-path}/login</to>
</rule>
Получив функцию подсказки пароля в браузере, я попытался запустить интеграционные тесты (на основе Canoo WebTest ). Тест подсказки пароля не удался со следующей ошибкой:
[ERROR] /Users/mraible/dev/appfuse/web/jsf/src/test/resources/web-tests.xml:51: JavaScript error loading page http://localhost:9876/appfuse-jsf-2.1.0-SNAPSHOT/passwordHint?username=admin: syntax error (http:// localhost:9876/appfuse-jsf-2.1.0-SNAPSHOT/javax.faces.resource/oamSubmit.js.jsf?ln=org.apache.myfaces#122)
Понимая, что это было вызвано тем, что мой хакер отправил форму при загрузке страницы , я переключился на Pretty Faces , что позволяет вам вызывать метод непосредственно из URL. После добавления зависимостей Pretty Faces в мой pom.xml я создал файл src / main / webapp / WEB-INF / pretty-config.xml со следующим XML:
Это позволило мне удалить и editProfile.xhtml, и passwordHint.xhtml, оба из которых просто автоматически отправляли формы.
В этот момент я решил, что мне будет хорошо пойти и снова запустить интеграционные тесты. Первое, что я обнаружил, было то, что «.jsf» был добавлен в мой красивый URL, скорее всего, с помощью UrlRewriteFilter. Добавление следующего в мой класс PasswordHint.java решило эту проблему.
if (username.endsWith(".jsf")) {
username = username.substring(0, username.indexOf(".jsf"));
}
Следующей вещью была загадочная ошибка, на которую мне понадобилось время, чтобы понять.
DEBUG [1152467051@qtp-144702232-0] PasswordHint.execute(38) | Processing Password Hint... 2011-03-05 05:48:52.471:WARN::/passwordHint/admin com.ocpsoft.pretty.PrettyException: Exception occurred while processing <:#{passwordHint.execute}> null at com.ocpsoft.pretty.faces.beans.ActionExecutor.executeActions(ActionExecutor.java:71) at com.ocpsoft.pretty.faces.event.PrettyPhaseListener.processEvent(PrettyPhaseListener.java:214) at com.ocpsoft.pretty.faces.event.PrettyPhaseListener.afterPhase(PrettyPhaseListener.java:108) at org.apache.myfaces.lifecycle.PhaseListenerManager.informPhaseListenersAfter(PhaseListenerManager.java:111) at org.apache.myfaces.lifecycle.LifecycleImpl.executePhase(LifecycleImpl.java:185) at org.apache.myfaces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) at javax.faces.webapp.FacesServlet.service(FacesServlet.java:189)
Покопавшись в недрах MyFaces, я обнаружил, что класс искал viewId с расширением, и никакой view-id не устанавливался. Добавление следующего в начало метода execute () решило эту проблему.
getFacesContext().getViewRoot().setViewId("/passwordHint.xhtml");
После внесения этого изменения все интеграционные тесты AppFuse проходят, и обновление кажется завершенным. Единственные другие проблемы, с которыми я столкнулся, были связаны с журналированием. Первая — ошибка в томагавке, которая, похоже, ни на что не влияет.
Mar 5, 2011 6:44:01 AM com.sun.facelets.compiler.TagLibraryConfig loadImplicit SEVERE: Error Loading Library: jar:file:/Users/mraible/.m2/repository/org/apache/myfaces/tomahawk/tomahawk20/1.1.10/tomahawk20-1.1.10.jar!/META-INF/tomahawk.taglib.xml java.io.IOException: Error parsing [jar:file:/Users/mraible/.m2/repository/org/apache/myfaces/tomahawk/tomahawk20/1.1.10/tomahawk20-1.1.10.jar!/META-INF/tomahawk.taglib.xml]: at com.sun.facelets.compiler.TagLibraryConfig.create(TagLibraryConfig.java:410) at com.sun.facelets.compiler.TagLibraryConfig.loadImplicit(TagLibraryConfig.java:431) at com.sun.facelets.compiler.Compiler.initialize(Compiler.java:87) at com.sun.facelets.compiler.Compiler.compile(Compiler.java:104)
Второе — чрезмерная регистрация в MyFaces. Насколько я могу судить, это потому, что MyFaces переключился на java.util.logging вместо общего журнала. Я думаю, что со всеми фреймворками, которые использует AppFuse, теперь у него есть все фреймворки журналирования в своем classpath. Я надеялся исправить это, отправив сообщение в список рассылки , но пока не получил ответа.
[WARNING] [talledLocalContainer] Mar 5, 2011 6:50:25 AM org.apache.myfaces.config.annotation.TomcatAnnotationLifecycleProvider newInstance [WARNING] [talledLocalContainer] INFO: Creating instance of org.appfuse.webapp.action.BasePage [WARNING] [talledLocalContainer] Mar 5, 2011 6:50:25 AM org.apache.myfaces.config.annotation.TomcatAnnotationLifecycleProvider destroyInstance [WARNING] [talledLocalContainer] INFO: Destroy instance of org.appfuse.webapp.action.BasePage
После успешного обновления AppFuse я переключился на AppFuse Light, где все было намного проще .
Теперь, когда AppFuse использует JSF 2, я надеюсь начать использовать некоторые из его
новых функций . Если вы хотите начать с ними сегодня, я приглашаю вас
взять исходный код и начать интегрировать их самостоятельно.