Shale Clay Pluginを使う
最近はHTMLをテンプレートとするFrameworkが増えてきました.
(最近ではPOHP・・・Plain Old HTML Pageと呼ばれているようです)
例えば,S2JSF,Facelets,Tapestry,Wicket...
S2JSFとFaceletsを一度使用してみると,やはりHTMLテンプレートはいいなと.
特に,JSFを使用していると正直タグを書く気にはなれなくなります.
ShaleもViewにHTMLテンプレートを使用することができます.
では,HTML Viewを使ってみます.
HTML View
HTML View を使用するための準備をします.
Jar
- commons-beanutils.jar
- commons-chain.jar
- commons-codec.jar
- commons-digester.jar
- commons-logging.jar
- commons-validator.jar
- myfaces-api.jar
- myfaces-impl.jar
- shale-clay.jar
- shale-core.jar
とりあえず,今回はshale-usecase-YYYYMMDD.warを展開して,
libに入っているjarを使用することにします.
上記Jarは,今回作成するサンプルに必要最小限のjarです.
設定
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- Select JSF State Saving Mode --> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param> <!-- Commons Chain Configuration Resources --> <context-param> <param-name> org.apache.commons.chain.CONFIG_WEB_RESOURCE </param-name> <param-value>/WEB-INF/chain-config.xml</param-value> </context-param> <!-- Clay Common Configuration Resources --> <context-param> <param-name> org.apache.shale.clay.COMMON_CONFIG_FILES </param-name> <param-value>/WEB-INF/clay-config.xml</param-value> </context-param> <!-- Shale Application Controller Filter --> <filter> <filter-name>shale</filter-name> <filter-class> org.apache.shale.faces.ShaleApplicationFilter </filter-class> </filter> <!-- Shale Application Controller Filter Mapping --> <filter-mapping> <filter-name>shale</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Commons Chain Configuration Listener --> <listener> <listener-class> org.apache.commons.chain.web.ChainListener </listener-class> </listener> <!-- Clay Configuration Listener --> <listener> <listener-class> org.apache.shale.clay.config.ClayConfigureListener </listener-class> </listener> <!-- JavaServer Faces Servlet Configuration --> <servlet> <servlet-name>faces</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- JavaServer Faces Servlet Mapping for Clay HTML Full View --> <servlet-mapping> <servlet-name>faces</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <!-- Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> <application> <locale-config> <default-locale>en</default-locale> <supported-locale>jp</supported-locale> <supported-locale>en</supported-locale> </locale-config> </application> <!-- ViewController Beans --> <managed-bean> <managed-bean-name>hello</managed-bean-name> <managed-bean-class>sample.HelloBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> </faces-config>
chain-config.xml
<?xml version="1.0" encoding="UTF-8"?> <catalogs> <!-- Define preprocessing command chain for Shale to execute --> <catalog name="shale"> <chain name="preprocess"> <!-- Perform remote request processing for matching requests --> <!-- Successful match will terminate the processing chain --> <command className="org.apache.commons.chain.generic.LookupCommand" catalogName="shale" name="remote" optional="true" /> <!-- This command is only needed for full clay html views with myfaces --> <command className="org.apache.shale.clay.faces.ClayViewHandlerCommand" /> <!-- This filter command wakes up the watchdog monitoring the Clay configuration files for change. --> <command className="org.apache.shale.clay.config.beans.ConfigDefinitionsWatchdogFilter" includes="\S*\.faces,\S*\.html,/index\.jsp,\S*\.xml" /> <!-- Disallow direct access to JSP and JSFP resources --> <command className="org.apache.shale.application.ContextRelativePathFilter" includes="\S*\.xml,\S*\.faces,\S*\.html,\S*\.gif,\S*\.jpg,/index\.jsp" excludes="\S*\.jsp,\S*\.jspf" /> </chain> </catalog> </catalogs>
clay-config.xml (2006/06/04 修正)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE view PUBLIC "-//Apache Software Foundation//DTD Shale Clay View Configuration 1.0//EN" "http://struts.apache.org/dtds/shale-clay-config_1_0.dtd"> <view> <!-- Hello --> <component jsfid="name" extends="outputText" allowBody="false"> <attributes> <set name="value" value="#{@managed-bean-name.name}" /> </attributes> </component> </view>
hello.html (2006/06/04 修正)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello</title> </head> <body> <form id="helloForm"> name:<input type="text" value="#{@managed-bean-name.name}" id="name" /> <input type="submit" action="#{@managed-bean-name.doSetName}" value="go" id="go" /> </form> Hello. <span jsfid="name">sample</span> </body> </html>
sample.HelloBean
package sample; public class HelloBean { private String name_; public String getName() { return name_; } public void setName(String name) { name_ = name; } public String doSetName() { return null; } }
アプリケーションを動かしてみる
http://localhost:8080/sample1/hello.html
画面が表示されましたか?
では,nameに「hoge」とでも入れてみますか.
Hello. hoge
と,表示されればOK.
では,日本語を入力してみます.
nameに「てすと」と入力してみます.
Hello. a?|a??a?¨
文字化けしました.
こんなときは,Encodeing Filterを設定します.
インストールしたTomcatからFilterを持ってきましょう.
$TOMCAT_HOME/webapps/servlets-examples/WEB-INF/classes/filters
にソースがあります.コピーしてください.
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <!-- Select JSF State Saving Mode --> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param> <!-- Commons Chain Configuration Resources --> <context-param> <param-name> org.apache.commons.chain.CONFIG_WEB_RESOURCE </param-name> <param-value>/WEB-INF/chain-config.xml</param-value> </context-param> <!-- Clay Common Configuration Resources --> <context-param> <param-name> org.apache.shale.clay.COMMON_CONFIG_FILES </param-name> <param-value>/WEB-INF/clay-config.xml</param-value> </context-param> <!-- Set character encoding on each request --> <filter> <filter-name>Set Character Encoding</filter-name> <filter-class>filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <!-- Shale Application Controller Filter --> <filter> <filter-name>shale</filter-name> <filter-class> org.apache.shale.faces.ShaleApplicationFilter </filter-class> </filter> <!-- Mapping to apply the "Set Character Encoding" filter to *all* requests processed by this web application --> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Shale Application Controller Filter Mapping --> <filter-mapping> <filter-name>shale</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Commons Chain Configuration Listener --> <listener> <listener-class> org.apache.commons.chain.web.ChainListener </listener-class> </listener> <!-- Clay Configuration Listener --> <listener> <listener-class> org.apache.shale.clay.config.ClayConfigureListener </listener-class> </listener> <!-- JavaServer Faces Servlet Configuration --> <servlet> <servlet-name>faces</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- JavaServer Faces Servlet Mapping for Clay HTML Full View --> <servlet-mapping> <servlet-name>faces</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <!-- Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
では,もう一度日本語を入力してみます.
nameに「てすと」と入力してみます.
Hello. てすと
表示できました.
HTMLテンプレートに日本語を使ってみる
では,次にテンプレートに日本語を使ってみます.
hello.html (2006/06/04 修正)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>こんにちは</title> </head> <body> <form id="helloForm"> 名前:<input type="text" value="#{@managed-bean-name.name}" id="name" /> <input type="submit" action="#{@managed-bean-name.doSetName}" value="go" id="go" /> </form> こんにちは. <span jsfid="name">sample</span> </body> </html>
もう一度,表示してみます.
http://localhost:8080/sample1/hello.html
...文字化けしました...orz
Clayを修正します.
日本語が文字化けするようでは使い物にならない為,修正します.
前回,ShaleのBuildを参考にしてjarを作成します.
修正するファイルは,
$Shaleのルート/clay-plugin/src/java/org/apache/shale/clay/config/ClayTemplateParser.java
org.apache.shale.clay.config.ClayTemplateParser.java
/* * Copyright 2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: ClayTemplateParser.java 409894 2006-05-27 22:57:44Z gvanmatre $ */ package org.apache.shale.clay.config; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.Iterator; import java.util.List; import javax.faces.context.FacesContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shale.clay.config.beans.ComponentBean; import org.apache.shale.clay.config.beans.ComponentConfigBean; import org.apache.shale.clay.config.beans.ConfigBean; import org.apache.shale.clay.config.beans.ElementBean; import org.apache.shale.clay.config.beans.TemplateConfigBean; import org.apache.shale.clay.parser.Node; import org.apache.shale.clay.parser.Parser; import org.apache.shale.clay.parser.builder.Builder; import org.apache.shale.util.Messages; import org.xml.sax.SAXException; /** * <p>This class is responsible for loading an HTML template into a graph * of {@link ComponentBean}'s that represents a JSF component tree. It * is used by {@link org.apache.shale.clay.config.beans.TemplateConfigBean}, * a subclass of {@link org.apache.shale.clay.config.beans.ComponentConfigBean}. * </p> */ public class ClayTemplateParser implements ClayConfigParser { /** * <p>Commons logging utility object static instance.</p> */ private static Log log; static { log = LogFactory.getLog(org.apache.shale.clay.config.ClayXmlParser.class); } /** * <p>Message resources for this class.</p> */ private static Messages messages = new Messages("org.apache.shale.clay.Bundle", ClayConfigureListener.class.getClassLoader()); /** * <p>Object pool for HTML template configuration files.</p> */ private ConfigBean config = null; /** * <p>Sets an object pool for HTML template configuration files.</p> */ public void setConfig(ConfigBean config) { this.config = config; } /** * <p>Returns an object pool for HTML template configuration files.</p> */ public ConfigBean getConfig() { return config; } /** * <p>Loads the <code>templateURL</code> identified by the <code>templateName</code> * into a graph of {@link ComponentBean}'s.</p> */ public void loadConfigFile(URL templateURL, String templateName) throws IOException, SAXException { ((ComponentConfigBean) config).addChild(generateElement(templateURL, templateName)); } /** * <p>Loads the template file and parses it into a composition * of metadata that's used by the {@link org.apache.shale.clay.component.Clay} * component. This metadata is used to construct a JSF subtree * within target view. * </p> */ protected ComponentBean generateElement(URL templateURL, String templateName) throws IOException { if (log.isInfoEnabled()) log.info(messages.getMessage("loading.template", new Object[] {templateName})); ComponentBean root = new ComponentBean(); root.setJsfid(templateName); root.setComponentType("javax.faces.NamingContainer"); // generate the document InputStreamReader inputStreamReader = null; StringBuffer buffer = null; try { InputStream inputStream = templateURL.openStream(); FacesContext context = FacesContext.getCurrentInstance(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); inputStreamReader = new InputStreamReader(inputStream,request.getCharacterEncoding()); buffer = loadTemplate(inputStreamReader, templateName); } finally { if (inputStreamReader != null) inputStreamReader.close(); } List roots = new Parser().parse(buffer); Iterator ri = roots.iterator(); while (ri.hasNext()) { Node node = (Node) ri.next(); Builder renderer = node.getBuilder(); ElementBean child = renderer.createElement(node); root.addChild(child); if (renderer.isChildrenAllowed()) { renderer.encode(node, child, child); } else { renderer.encode(node, child, root); } } roots.clear(); roots = null; buffer.setLength(0); buffer = null; ri = null; //verify there is not a duplicate component id within a naming //container. config.checkTree(root); //compress the tree merging adjacent verbatim nodes if (config instanceof TemplateConfigBean) ((TemplateConfigBean) config).optimizeTree(root); return root; } /** * <p>Loads the template into a <code>StringBuffer</code> using * the <code>inputStream</code> and <code>templateName<code> parameters. * </p> */ protected StringBuffer loadTemplate(InputStreamReader inputStreamReader, String templateName) throws IOException { StringBuffer buff = new StringBuffer(); try { int c = 0; done: while (true) { c = inputStreamReader.read(); if (c > -1) buff.append((char) c); else break done; } } catch (IOException e) { log.error(messages.getMessage("loading.template.exception", new Object[] {templateName}), e); throw e; } return buff; } }
作成した shale-clay.jar をコピーしてください.
もう一度,表示してみます.
http://localhost:8080/sample1/hello.html
うまく表示されましたね.
テンプレートを読み込む再に,InputStreamReaderを使用するように修正しました.
指定するエンコードは,リクエストで取得したエンコードを指定しました.
海外のFrameworkを使用する際に,やはり日本語が使用できるか否かは気になりますよね.
残念ながら,Clayはまだアルファリリースの為日本語に若干の問題が残っているようです.
バグフィックスされるまでは,上記の修正で暫定対応して続きをやっていこうと思います.
patch.txt
Index: /clay-plugin/src/java/org/apache/shale/clay/config/ClayTemplateParser.java =================================================================== --- /clay-plugin/src/java/org/apache/shale/clay/config/ClayTemplateParser.java (revision 411191) +++ /clay-plugin/src/java/org/apache/shale/clay/config/ClayTemplateParser.java (working copy) @@ -19,10 +19,14 @@ import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.util.Iterator; import java.util.List; +import javax.faces.context.FacesContext; +import javax.servlet.http.HttpServletRequest; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.shale.clay.config.beans.ComponentBean; @@ -108,15 +112,18 @@ // generate the document - InputStream inputStream = null; + InputStreamReader inputStreamReader = null; StringBuffer buffer = null; try { - inputStream = templateURL.openStream(); - buffer = loadTemplate(inputStream, templateName); + InputStream inputStream = templateURL.openStream(); + FacesContext context = FacesContext.getCurrentInstance(); + HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); + inputStreamReader = new InputStreamReader(inputStream, request.getCharacterEncoding()); + buffer = loadTemplate(inputStreamReader, templateName); } finally { - if (inputStream != null) - inputStream.close(); + if (inputStreamReader != null) + inputStreamReader.close(); } List roots = new Parser().parse(buffer); @@ -158,7 +165,7 @@ * the <code>inputStream</code> and <code>templateName<code> parameters. * </p> */ - protected StringBuffer loadTemplate(InputStream inputStream, String templateName) throws IOException { + protected StringBuffer loadTemplate(InputStreamReader inputStreamReader, String templateName) throws IOException { StringBuffer buff = new StringBuffer(); @@ -166,7 +173,7 @@ try { int c = 0; done: while (true) { - c = inputStream.read(); + c = inputStreamReader.read(); if (c > -1) buff.append((char) c); else