本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

Hamlet をコンパイルする

テンプレート・コンパイルを追加することで、このフレームワークのパフォーマンスを向上させることができます

René Pawlitzek (rpa@zurich.ibm.com), Research and Development Engineer, IBM
Photo of Rene Pawlitzek
Rene Pawlitzek は、リヒテンシュタインの市民であり、Swiss Federal Institute of Technology (ETH Zurich) のコンピューター・サイエンスの工学学位を取得しています。スイスにある IBM Zurich Research Laboratory の Advanced Operating Environment グループ (旧 GSAL (Global Security Analysis Lab)) で、セキュリティー情報管理ソリューションにフォーカスした研究・開発のエンジニアとして働いています。IBM に入社する前は、カリフォルニアの Hewlett-Packard、WindRiver Systems、および Borland International で働いていました。

概要: Rene Pawlitzek は、Java サーブレットを拡張してコンテンツとプレゼンテーションを分離した、Hamlet フレームワークを発展させ続けています。この記事では、新たな改良点、すなわちアプリケーションのパフォーマンスを向上できる Hamlet テンプレートのコンパイル方法を提案します。

日付:  2006年 6月 20日
レベル:  初級 この記事の原文:  英語
アクティビティー: 1061 ビュー
お気軽にご意見・ご感想をお寄せください: 


今日のアプリケーションは、サイズが大きくて実行速度が遅く、あまりに複雑になることがよくあります。リスト 1 のスタック・トレースは、筆者が JSF (JavaServer Faces) を使って初めて開発したときのもので、最近のコードがいかに複雑になっているかを示しています。


リスト 1. JSF スタック・トレース
                
java.lang.Exception: Authorization server name not found
at com.ibm.zurich.gsal.billyboard.libs.IntranetAuthProcessor.configure(Unknown Source)
at com.ibm.zurich.gsal.billyboard.apps.board.LoginController.<init>(Unknown Source)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance
(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:274)
at java.lang.Class.newInstance0(Class.java:308)
at java.lang.Class.newInstance(Class.java:261)
at java.beans.Beans.instantiate(Beans.java:204)
at java.beans.Beans.instantiate(Beans.java:48)
at com.sun.faces.config.ManagedBeanFactory.newInstance(ManagedBeanFactory.java:203)
at com.sun.faces.application.ApplicationAssociate.createAndMaybeStoreManagedBeans
(ApplicationAssociate.java:256)
at com.sun.faces.el.VariableResolverImpl.resolveVariable(VariableResolverImpl.java:78)
at com.sun.faces.el.impl.NamedValue.evaluate(NamedValue.java:125)
at com.sun.faces.el.impl.ComplexValue.evaluate(ComplexValue.java:146)
at com.sun.faces.el.impl.ExpressionEvaluatorImpl.evaluate
(ExpressionEvaluatorImpl.java:243)
at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:173)
at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:154)
at javax.faces.component.UIOutput.getValue(UIOutput.java:147)
at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue
(HtmlBasicInputRenderer.java:82)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue
(HtmlBasicRenderer.java:191)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd
(HtmlBasicRenderer.java:169)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:720)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive
(HtmlBasicRenderer.java:443)
at com.sun.faces.renderkit.html_basic.GridRenderer.encodeChildren(GridRenderer.java:233)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:701)
at javax.faces.webapp.UIComponentTag.encodeChildren(UIComponentTag.java:607)
at javax.faces.webapp.UIComponentTag.doEndTag(UIComponentTag.java:544)
at com.sun.faces.taglib.html_basic.PanelGridTag.doEndTag(PanelGridTag.java:460)
at org.apache.jsp.login_jsp._jspx_meth_h_panelGrid_0(login_jsp.java:200)
at org.apache.jsp.login_jsp._jspx_meth_h_form_0(login_jsp.java:150)
at org.apache.jsp.login_jsp._jspx_meth_f_view_0(login_jsp.java:120)
at org.apache.jsp.login_jsp._jspService(login_jsp.java:85)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:94)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:324)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter
(ApplicationFilterChain.java:237)
at org.apache.catalina.core.ApplicationFilterChain.doFilter
(ApplicationFilterChain.java:157)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:704)
at org.apache.catalina.core.ApplicationDispatcher.processRequest
(ApplicationDispatcher.java:474)
at org.apache.catalina.core.ApplicationDispatcher.doForward
(ApplicationDispatcher.java:409)
at org.apache.catalina.core.ApplicationDispatcher.forward
(ApplicationDispatcher.java:312)
at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:322)
at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:130)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:87)
at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:200)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:117)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:198)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter
(ApplicationFilterChain.java:237)
at org.apache.catalina.core.ApplicationFilterChain.doFilter
(ApplicationFilterChain.java:157)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:214)
at org.apache.catalina.core.StandardValveContext.invokeNext
(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardContextValve.invokeInternal
(StandardContextValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:152)
at org.apache.catalina.core.StandardValveContext.invokeNext
(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.core.StandardValveContext.invokeNext
(StandardValveContext.java:104)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:118)
at org.apache.catalina.core.StandardValveContext.invokeNext
(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.core.StandardValveContext.invokeNext
(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:799)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection
(Http11Protocol.java:705)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:577)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:683)
at java.lang.Thread.run(Thread.java:534) 


「Introducing Hamlets」で始まるこのシリーズの以前の記事で、Web ベース・アプリケーション開発用の Hamlet と呼ばれる、使いやすくて理解しやすいフレームワークを提案しました。このフレームワークは、徹底してソフトウェアを単純にした成果が反映されています。 (これらの古い記事をまだお読みでなければ、「参考文献」にリンクがあります。)

Hamlet は、テンプレート・ファイルを読めるように、SAX (Simple API for XML) を使って Java サーブレットを拡張したものです。テンプレート・ファイルが読み込まれている一方で、Hamlet は、小さいコールバック関数のセット (HamletHandler によって実装されたもの) を使って、テンプレート内で特別ななタグと ID でマークされた場所に動的コンテンツを追加します。図 1 は、そのプロセスを示したものです。


図 1. Hamlet はテンプレート・ファイルからコンテンツを読み取るために SAX を使用し、動的コンテンツを追加するために HamletHandler を呼び出します
図 1. Hamlet はテンプレート・ファイルからコンテンツを読み取るために SAX を使用し、動的コンテンツを追加するために HamletHandler を呼び出します

この資料で使われているプロジェクト名 Hamlet は、Web ベース・アプリケーションにおいてコンテンツとプレゼンテーションを分離するためのサーブレット・ベースのフレームワークを開発する内部プロジェクトのことを指しています。この記事の最後の方にある「ダウンロード」セクションから、この記事のすべてのサンプル・コードを含むアーカイブ・ファイル hamlet-compiler.zip をダウンロードできます。このコードは、IBM alphaWorks サイトで Hamlet v1.2 としてもリリースされています (リンクについては、「参考文献」を参照してください)。

Hamlet テンプレートをコンパイルする

この記事では、Hamlet フレームワークへの小さな追加機能、すなわち Hamlet を高速にするためのテンプレート・コンパイラーについて説明します。そのテンプレート・コンパイラーの実装には、600 行以下の Java ソース・コードが必要です。テンプレート・コンパイラーは、SAX を使ってテンプレート・ファイルを読み込み、コンテンツを Java ソース・コードに変換します。そして、標準の JDK Java コンパイラーを呼び出して Java バイトコードを生成します。実行時に Hamlet はこのコードを実行し、動的コンテンツを追加するために HamletHandler をコールします。図 2 では、そのプロセスを説明しています。


図 2. テンプレート・コンパイラーはテンプレート・ファイルのコンテンツを Java バイトコードに変換し、Hamlet はこのコードを実行して、動的コンテンツを追加するために HamletHandler をコールします
図 2. テンプレート・コンパイラーはテンプレート・ファイルのコンテンツを Java バイトコードに変換し、Hamlet はこのコードを実行して、動的コンテンツを追加するために HamletHandler をコールします

コンパイル済み Hamlet (つまり、コンパイル済みのテンプレートを使った Hamlet) により、たくさんのユーザーが同時に利用できる Web ベース・アプリケーションを実装できるようになります。またコンパイル済み Hamlet は、必要とするリソースが少ないため、組み込みデバイスにも同様に適しています。



動作

テンプレート・コンパイラーは、2 つのステージで操作されます。最初は、テンプレート・ファイルの HTML コンテンツが読み込まれ、Java ソース・コードに変換されます。例えば、リスト 2 を見てください。HTML コンテンツが BasicTestTemplate.html. に組み込まれています。


リスト 2. 基本 HTML コード
                
<HTML>
<HEAD>
<TITLE>Hamlet Test Suite</TITLE>
</HEAD>
<BODY>

<H1>Hamlet Test Suite</H1>
<DIV>
The following test cases test the functionality of the <I>hamlet.jar</I> library.
It is in working condition if the output in each section is exactly repeated.
</DIV>

<H2>Replace Test</H2>
<DIV>Hello Mr. <REPLACE ID="Name">X</REPLACE>.</DIV>
<DIV>Hello Mr. Pawlitzek.</DIV>

<H2>Repeat Test</H2>
<REPEAT ID="Rows">
<DIV>
<REPEAT ID="Cols">
<REPLACE ID="Value">0</REPLACE>
</REPEAT>
</DIV>
</REPEAT>
<DIV>?</DIV>
<DIV>11 12 13</DIV>
<DIV>21 22 23</DIV>
<DIV>31 32 33</DIV>

<H2>Attribute Test</H2>
<DIV><A>www.pawlitzek.com</A></DIV>
<DIV><A ID="Ref" HREF="www.pawlitzek.com">www.pawlitzek.com</A></DIV>

<H2>Include Test</H2>
<DIV>c Copyright IBM Corp. 2006</DIV>
<DIV><INCLUDE SRC="BasicTestInclude.html" /></DIV>
<DIV><INCLUDE ID="Copyright" SRC="BasicTestInclude.html" /></DIV>

</BODY>
</HTML>

このコードは、リスト 3 の Java ソース・コード (BasicTestTemplate.java) に変換されます。


リスト 3. Java コードに変換されたリスト 2 の HTML
                
import java.io.*;
import com.ibm.hamlet.*;
import org.xml.sax.helpers.*;

public class BasicTestTemplate implements Template {

public void serveDoc (PrintWriter writer, ContentHandler handler) throws Exception {
writer.print (
"<HTML>\n" + 
"  <HEAD>\n" + 
"   <TITLE>Hamlet Test Suite</TITLE>\n" + 
"  </HEAD>\n" + 
"  <BODY>\n" + 
"\n" + 
"   <H1>Hamlet Test Suite</H1>\n" + 
"   <DIV>\n" + 
"    The following test cases test the functionality" + 
" of the <I>hamlet.jar</I> library.\n" + 
"    It is in working condition if the output in each section" + 
" is exactly repeated.\n" + 
"   </DIV>\n" + 
"\n" + 
"   <H2>Replace Test</H2>\n" + 
"   <DIV>Hello Mr. "
);
AttributesImpl atts0 = new AttributesImpl ();
atts0.addAttribute ("", "ID", "ID", "CDATA", "Name");
writer.print (handler.getElementReplacement ("Name", "REPLACE", atts0));
writer.print (
".</DIV>\n" + 
"   <DIV>Hello Mr. Pawlitzek.</DIV>\n" + 
"\n" + 
"   <H2>Repeat Test</H2>\n" + 
"    "
);
AttributesImpl atts1 = new AttributesImpl ();
atts1.addAttribute ("", "ID", "ID", "CDATA", "Rows");
int count0 = handler.getElementRepeatCount ("Rows", "REPEAT", atts1);
for (int loop0 = 0; loop0 < count0; loop0++) {
writer.print (
"\n" + 
"      <DIV>\n" + 
"      "
);
AttributesImpl atts2 = new AttributesImpl ();
atts2.addAttribute ("", "ID", "ID", "CDATA", "Cols");
int count1 = handler.getElementRepeatCount ("Cols", "REPEAT", atts2);
for (int loop1 = 0; loop1 < count1; loop1++) {
writer.print (
"\n" + 
"        "
);
AttributesImpl atts3 = new AttributesImpl ();
atts3.addAttribute ("", "ID", "ID", "CDATA", "Value");
writer.print (handler.getElementReplacement ("Value", "REPLACE", atts3));
writer.print (
"\n" + 
"      "
);
if (handler.getElementRepeatCount ("Cols", "REPEAT", atts2) == 0)
break;
} // for
writer.print (
"\n" + 
"      </DIV>\n" + 
"    "
);
if (handler.getElementRepeatCount ("Rows", "REPEAT", atts1) == 0)
break;
} // for
writer.print (
"\n" + 
"   <DIV> </DIV>\n" + 
"   <DIV>11 12 13</DIV>\n" + 
"   <DIV>21 22 23</DIV>\n" + 
"   <DIV>31 32 33</DIV>\n" + 
"\n" + 
"   <H2>Attribute Test</H2>\n" + 
"   <DIV><A>www.pawlitzek.com</A></DIV>\n" + 
"   <DIV>"
);
AttributesImpl atts4 = new AttributesImpl ();
atts4.addAttribute ("", "ID", "ID", "CDATA", "Ref");
atts4.addAttribute ("", "HREF", "HREF", "CDATA", "www.pawlitzek.com");
RuntimeUtilities.printTag 
(writer, "A", handler.getElementAttributes ("Ref", "A", atts4));
writer.print (
"www.pawlitzek.com</A></DIV>\n" + 
"\n" + 
"   <H2>Include Test</H2>\n" + 
"   <DIV>(c) Copyright IBM Corp. 2006</DIV>\n" + 
"   <DIV>"
);
AttributesImpl atts5 = new AttributesImpl ();
atts5.addAttribute ("", "SRC", "SRC", "CDATA", "BasicTestInclude.html");
RuntimeUtilities.printInclude 
(writer, handler.getElementIncludeSource (null, "INCLUDE", atts5));
writer.print (
"</DIV>\n" + 
"   <DIV>"
);
AttributesImpl atts6 = new AttributesImpl ();
atts6.addAttribute ("", "ID", "ID", "CDATA", "Copyright");
atts6.addAttribute ("", "SRC", "SRC", "CDATA", "BasicTestInclude.html");
RuntimeUtilities.printInclude (writer, 
handler.getElementIncludeSource ("Copyright", "INCLUDE", 
handler.getElementAttributes ("Copyright", "INCLUDE", atts6)));
writer.print (
"</DIV>\n" + 
"\n" + 
"  </BODY>\n" + 
"</HTML>"
);
} // serveDoc

} // BasicTestTemplate 

Java ソース・コードへの変換は直接行われ、以下の内容が実行されます。

  • 静的コンテンツの箇所が集められ、各箇所ごとに writer.print() ステートメントが生成されます。例えば、リスト 4 のコードが、リスト 5 のコードに変換されます。


    リスト 4. 静的コンテンツの箇所
                            
    <HTML>
    <HEAD>
    <TITLE>Hamlet Test Suite</TITLE>
    </HEAD>
    <BODY>
    
    <H1>Hamlet Test Suite</H1>
    <DIV>
    The following test cases test the functionality of the <I>hamlet.jar</I> library.
    It is in working condition if the output in each section is exactly repeated.
    </DIV>
    
    <H2>Replace Test</H2>
    <DIV>Hello Mr.
    



    リスト 5. Java コードに変換されたリスト 4 からの HTML
                            
    writer.print (
    "<HTML>\n" + 
    "  <HEAD>\n" + 
    "   <TITLE>Hamlet Test Suite</TITLE>\n" + 
    "  </HEAD>\n" + 
    "  <BODY>\n" + 
    "\n" + 
    "   <H1>Hamlet Test Suite</H1>\n" + 
    "   <DIV>\n" + 
    "    The following test cases test the functionality" + 
    " of the <I>hamlet.jar</I> library.\n" + 
    "    It is in working condition if the output in each section" + 
    " is exactly repeated.\n" + 
    "   </DIV>\n" + 
    "\n" + 
    "   <H2>Replace Test</H2>\n" + 
    "   <DIV>Hello Mr. "
    );
    

  • 各 <REPLACE>...</REPLACE> 部ごとに、getElementReplacement() および writer.print() のコールが生成されます。実行時に、Hamlet は、<REPLACE> タグの属性を持つ getElementReplacement() をコールし、writer.print() による出力に組み込まれる動的コンテンツを受け取ります。例えば、リスト 6 のコードは、リスト 7 のコードに変換されます。

    リスト 6. <REPLACE> 部
                            
    <REPLACE ID="Name">X</REPLACE>
    



    リスト 7. Java コードに変換されたリスト 6 の HTML
                            
    AttributesImpl atts0 = new AttributesImpl ();
    atts0.addAttribute ("", "ID", "ID", "CDATA", "Name");
    writer.print (
    handler.getElementReplacement ("Name", "REPLACE", atts0));
    

  • 各 <REPEAT>...</REPEAT> 部ごとに、1 つの for ループ文と 2 つの getElementRepeatCount() コールが生成されます。実行時に、Hamlet は、<REPEAT> タグの属性を持つ getElementRepeatCount() をコールします (繰り返し回数を取得するためにループの実行前に 1 回コールし、0 を返してループを終了するまでループ内で繰り返しコールします)。例えば、リスト 8 のコードは、リスト 9 のコードに変換されます。


    リスト 8. <REPEAT> 部
                            
    <REPEAT ID="Cols">
    ...
    </REPEAT>
    



    リスト 9. Java コードに変換されたリスト 8 の HTML
                            
    AttributesImpl atts2 = new AttributesImpl ();
    atts2.addAttribute ("", "ID", "ID", "CDATA", "Cols");
    int count1 = handler.getElementRepeatCount ("Cols", "REPEAT", atts2);
    for (int loop1 = 0; loop1 < count1; loop1++) {
    ...
    if (handler.getElementRepeatCount ("Cols", "REPEAT", atts2) == 0)
    break;
    } // for
    

  • 各 <INCLUDE/> 部ごとに、getElementIncludeSource() および RuntimeUtilities.printInclude() のコールが生成されます。実行時に、Hamlet は、<INCLUDE> タグの属性を使って getElementIncludeSource() をコールし、InputStream を取得します。次に、RuntimeUtilities.printInclude() は、この InputStream を使って、<INCLUDE> タグの SRC 属性で指定されるリソースのコンテンツを組み込みます。例えば、リスト 10 のコードは、リスト 11 のコードに変換されます。


    リスト 10. <INCLUDE/> 部
                            
    <INCLUDE SRC="BasicTestInclude.html" />
    



    リスト 11. Java コードに変換されたリスト 10 の HTML
                            
    AttributesImpl atts5 = new AttributesImpl ();
    atts5.addAttribute ("", "SRC", "SRC", "CDATA", "BasicTestInclude.html");
    RuntimeUtilities.printInclude (writer, 
    handler.getElementIncludeSource (null, "INCLUDE", atts5));
    

  • 各 <INCLUDE ID="..."/> 部ごとに、getElementAttributes()、getElementIncludeSource()、および RuntimeUtilities.printInclude() のコールが生成されます。実行時に、Hamlet は、属性を変更できるようにするために、最初に <INCLUDE> タグの属性を使って getElementAttributes() をコールします。次に、InputStream を取得するために、変更される可能性のある属性を使って getElementIncludeSource() がコールされます。最後に、RuntimeUtilities.printInclude() はこの InputStream を使用して、リソースのコンテンツを組み込みます。 例えば、リスト 12 のコードは、リスト 13 のコードに変換されます。


    リスト 12. <INCLUDE ID="..."/> 部
                            
    AttributesImpl atts5 = new AttributesImpl ();
    atts5.addAttribute ("", "SRC", "SRC", "CDATA", "BasicTestInclude.html");
    RuntimeUtilities.printInclude (writer, 
    handler.getElementIncludeSource (null, "INCLUDE", atts5));
    



    リスト 13. Java コードに変換されたリスト 12 の HTML
                            
    AttributesImpl atts6 = new AttributesImpl ();
    atts6.addAttribute ("", "ID", "ID", "CDATA", "Copyright");
    atts6.addAttribute ("", "SRC", "SRC", "CDATA", "BasicTestInclude.html");
    RuntimeUtilities.printInclude (writer, 
    handler.getElementIncludeSource ("Copyright", "INCLUDE", 
    handler.getElementAttributes ("Copyright", "INCLUDE", atts6)));
    

  • ID 属性を持つ各タグごとに、getElementAttributes() および RuntimeUtilities.printTag() のコールが生成されます。実行時に、Hamlet は、属性を変更できるようにするために、タグの属性を使って getElementAttributes() を呼び出します。 続いて RuntimeUtilities.printTag() がコールされ、 変更される可能性のある属性を使ってタグを出力に組み込みます。 例えば、リスト 14 のコードは、リスト 15 のコードに変換されます。


    リスト 14. ID 属性を持つタグ
                            
    <A ID="Ref" HREF="www.pawlitzek.com">
    



    リスト 15. Java コードに変換されたリスト 14 の HTML
                            
    AttributesImpl atts4 = new AttributesImpl ();
    atts4.addAttribute ("", "ID", "ID", "CDATA", "Ref");
    atts4.addAttribute ("", "HREF", "HREF", "CDATA", "www.pawlitzek.com");
    RuntimeUtilities.printTag (writer, "A", 
    handler.getElementAttributes ("Ref", "A", atts4));
    

ここまでが、テンプレート・コンパイラーのステージ 1 で、HTML テンプレートを Java ソース・コードに変換します。ステージ 2 では、標準の JDK Java コンパイラーが呼び出され、ステージ 1 で生成されたソースをコンパイルします。その結果生成された Java バイトコードは、実行時に Hamlet によって実行され、レスポンスに動的コンテンツを追加するために HamletHandler によって実装された、4 つのコールバック関数 (getElementReplacement()、getElementRepeatCount()、getElementAttributes()、および getElementIncludeSource()) をコールします。


テンプレート・コンパイラーを実装する

リスト 16 に示すように、コマンドラインからテンプレート・コンパイラーを起動すると、コマンドラインの引数を使って、その static main() 関数がコールされます。


リスト 16. static main() 関数は TemplateCompiler クラスのインスタンスを作成し、その perform() メソッドをコールします
                
public class TemplateCompiler {

...

public static void main (String args[]) {
try {
if (args.length == 0) {
System.out.println (
"Usage: java -cp <classpath> com.ibm.hamlet.compiler.TemplateCompiler " +
"[-debug] [-verbose] [-dstDir <directory>] <Template.html>");
Runtime.getRuntime().exit (1);
} // if
TemplateCompiler tc = new TemplateCompiler ();
for (int n = 0; n < args.length; n++) {
if ("-debug".equals (args[n]))
tc.setDebug (true);
else if ("-verbose".equals (args[n]))
tc.setVerbose (true);
else if ("-dstDir".equals (args[n]))
tc.setDstDir (args[++n]);
else
tc.setFileName (args[n]);
} // for
tc.perform ();
} catch (Exception e) {
e.printStackTrace ();
Runtime.getRuntime().exit (1);
} // try
} // main

} // TemplateCompiler

最初に、コンパイラーは引数の数をチェックします。引数がない場合、テンプレート・コンパイラーの使い方を示す短いメッセージが出力され、アプリケーションは終了します。 それ以外の場合は、TemplateCompiler クラスのインスタンス (tc) が作成され、コマンドラインの引数が解析されます。次に、実際の作業を行うために、テンプレート・コンパイラーの perform() メソッドがコールされます。このメソッドは、リスト 17 に示すように、上記の 2 つのステージ (HTML テンプレートの Java ソース・コードへの変換、およびそのソース・コードのコンパイル) を実装します。


リスト 17. perform() は HTML から Java バイトコードへ変換する 2 つのステージを実装します
                
public class TemplateCompiler {

...

public void perform () throws Exception {

// stage one: create template.java
File fin = new File (fileName);
String templateName = fin.getName ();
String className = RuntimeUtilities.getClassName (templateName);
String sourceName = dstDir + File.separator + className + ".java";
File fout = new File (sourceName);
InputStream in = new FileInputStream (fin);
OutputStream out = new FileOutputStream (fout);
SourceGenerator generator = getSourceGenerator ();
if (verbose) System.out.println ("Creating " + sourceName + " ...");
generator.perform (in, out, className);
out.close ();
in.close ();

// stage two: compile template.java
String[] args = { sourceName };
Main compiler = new Main ();
if (verbose) System.out.println ("Compiling " + sourceName + " ...");
compiler.compile (args);
// delete template.java
if (!debug) {
if (verbose) System.out.println ("Deleting " + sourceName + " ...");
fout.delete ();
} // if
if (verbose) System.out.println ("Finished.");

} // perform

...

} // TemplateCompiler

ステージ 1 では、getSourceGenerator() が SourceGenerator インターフェースを実装するオブジェクト (ジェネレーター) を取得する前に、FileInputStream および FileOutputStream が作成されます。リスト 18 は、このインターフェースを示したものです。


リスト 18. SourceGenerator インターフェース
                
public interface SourceGenerator {

public void perform (InputStream in, OutputStream out, String name) 
throws Exception;

} // SourceGenerator

generator.perform (in, out, className) をコールすることによって、入力ストリームから HTML テンプレートが読み取られ、Java ソース・コードに変換されてから、出力ストリームに書き込まれます。

ステージ 2 では、標準の JDK Java コンパイラーが、compiler.compile (args) の実行時にステージ 1 で生成された Java ソース・コードから Java バイトコードを生成します。その後テンプレート・コンパイラーは、デバッグ・モードがアクティブでなければ、使われなくなった Java ソースを削除します。


ソース・コード生成

リスト 19 に示すように、DefaultGenerator クラスは HTML テンプレートを Java コードに変換します。このクラスは SourceGenerator インターフェースを実装します。


リスト 19. DefaultGenerator の perform() メソッドは、HTML から Java ソース・コードへの 変換を開始します
                
public class DefaultGenerator extends DefaultHandler implements SourceGenerator {

...

public void perform (InputStream in, OutputStream out, String source) 
throws Exception {

XMLReader reader = null;
try {
this.source = source;
this.out = new PrintStream (out);
reader = readerPool.getReader ();
InputSource inputSource = new InputSource (in);
reader.setErrorHandler (this);
reader.setContentHandler (this);
reader.parse (inputSource);
} finally {
if (reader != null)
readerPool.returnReader (reader);
} // try

} // perform

...

} // DefaultGenerator

DefaultGenerator の perform() メソッドは、リーダーのプールから SAX リーダーを取得し、SAX のエラー・ハンドラーとコンテンツ・ハンドラーを設定して、入力ソース (テンプレート) の解析を開始します。解析が終了すると、SAX リーはーがリーダーのプールに戻されます。

テンプレートの解析時に、ジェネレーターの SAX コンテンツ・ハンドラー・メソッド (startDocument()、endDocument()、startElement()、endElement()、および characters()) がコールされます。リスト 20 は、これを実装したものです。


リスト 20. DefaultGenerator クラスは、HTML を Java ソース・コードに変換する SAX コンテンツ・ハンドラー・メソッドを実装します
                
public class DefaultGenerator extends DefaultHandler implements SourceGenerator {

...

public void startDocument () throws SAXException {
stack = new Stack ();
buf = new StringBuffer ();
generateHeader (out, source);
} // startDocument


public void endDocument () throws SAXException {
generateText (out, buf.toString ());
generateFooter (out, source);
} // endDocument


public void startElement (String namespaceURI, String localName, 
String qName, Attributes atts) throws SAXException {

// category.debug ("local name: " + localName + ", qname: " + qName);
if (REPEAT_TAG.equals (localName)) {
generateText (out, buf.toString ());
generateElementRepeatHeader (out, atts);
buf = new StringBuffer ();
} else if (REPLACE_TAG.equals (localName)) {
generateText (out, buf.toString ());
String id = atts.getValue (ID_ATTRIBUTE);
if (id != null)
generateElementReplacement (out, atts);
buf = new StringBuffer ();
ignore = true;
} else if (INCLUDE_TAG.equals (localName)) {
generateText (out, buf.toString ());
generateElementInclude (out, localName, atts);
buf = new StringBuffer ();
ignore = true;
} else {
String id = atts.getValue (ID_ATTRIBUTE);
if (id != null) {
generateText (out, buf.toString ());
generateElementAttributes (out, localName, atts);
buf = new StringBuffer ();
} else {
buf.append ("<");
buf.append (localName);
for (int i = 0; i < atts.getLength (); i++) {
buf.append (" ");
buf.append (atts.getLocalName (i));
buf.append ("=\"");
buf.append (atts.getValue (i));
buf.append ("\"");
} // for
buf.append (">");
} // if
} // if

} // startElement


public void endElement (String namespaceURI, String localName, String qName)
throws SAXException {

if (REPEAT_TAG.equals (localName)) {
generateText (out, buf.toString ());
generateElementRepeatFooter (out);
buf = new StringBuffer ();
} else if (REPLACE_TAG.equals (localName) || INCLUDE_TAG.equals (localName)) {
ignore = false;
} else {
buf.append ("</");
buf.append (localName);
buf.append (">");
} // if

} // endElement


public void characters (char[] ch, int start, int length) throws SAXException {
chars = new String (ch, start, length);
if (!ignore)
buf.append (chars);
} // characters

...

} // DefaultGenerator

テンプレートが実際に解析される前に、startDocument() メソッドがコールされます。このメソッドは、静的テンプレート・コンテンツを収集するための StringBuffer と、ネストされた <REPEAT> タグを処理するためのスタックを作成します。さらに generateHeader(out, source) がコールされ、import 文と serveDoc() メソッド定義が含まれているソース・コード・ヘッダーを生成します。

SAX パーサーは、開始タグおよび終了タグを認識すると、startElement() および endElement() をコールします。これらのメソッドは、上記の「動作」のセクションで説明した概要に従って、テンプレート・コンテンツを変換します。

例えば、SAX リーダーが <REPEAT> タグを見つけると、startElement() は現在の StringBuffer を消去します。つまり、generateText(out, buf.toString ()) は、Java ソース・コードの中に writer.print() 文を生成します。writer.print() 文の引数は、<REPEAT> タグが見つかるまでに読み込まれた、すべての静的 HTML コンテンツが含まれている文字列になります。次に、generateElementRepeatHeader(out, atts) が、for ループの先頭のソースを生成します。最後に、新しい StringBuffer インスタンスが作成され、解析が続行します。リスト 21 は、このすべてを示したものです。


                
public void startElement (String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {

if (REPEAT_TAG.equals (localName)) {
generateText (out, buf.toString ());
generateElementRepeatHeader (out, atts);
buf = new StringBuffer ();
} ...

} // startElement

SAX リーダーが、対応する </REPEAT> タグを見つけると、endElement() は、現行の StringBuffer を消去して generateElementRepeatFooter(out) をコールし、未完了の for ループの Java ソース・コードを完了させます。解析を継続する前に、再び新しい StringBuffer が作成されます。 リスト 22


リスト 22. endElement() で処理する <REPEAT> タグは、未完了の for ループを完了させる Java ソース・コードを生成します
                
public void endElement (String namespaceURI, String localName, String qName)
throws SAXException {

if (REPEAT_TAG.equals (localName)) {
generateText (out, buf.toString ());
generateElementRepeatFooter (out);
buf = new StringBuffer ();
} ...

} // endElement

<REPLACE> タグと <INCLUDE> タグは、ほとんど同じようにコードを生成します。 ID 属性を含む他のすべてのタグでもこれと同じことが言えます。

解析が終了すると endDocument() メソッドがコールされます。このメソッドは、StringBuffer を消去し、generateFooter(out, source) を使ってソース・コード・フッターを生成します。

これが、開発時に HTML テンプレートが Java クラスにコンパイルされる方法です。


コンパイル済みテンプレートを実行する

実行時に、アプリケーション・サーバーまたはサーブレット・コンテナーがユーザー要求を受け取ると、Hamlet の doGet() メソッドがコールされます。doGet() 内で Hamlet はハンドラーを作成します。このハンドラーは、HamletHandler を拡張し、デフォルトで 4 つのコールバック・メソッド (getElementReplacement()、getElementRepeatCount()、getElementAttributes()、および getElementIncludeSource()) を実装するクラスのインスタンスです。 HamletHandler を拡張したクラス (例えば、BasicTestHandler) は、この 4 つのコールバック・メソッドを上書きして、リスト 23 に示すような、特定の Hamlet 用に特別な動的コンテンツを提供します。


リスト 23. BasicTestHandler クラスの 1 つのインスタンスは、BasicTestTemplate.html 用の動的コンテンツを提供します
                public class BasicTest extends Hamlet {

...

private static class BasicTestHandler extends HamletHandler {

public String getElementReplacement (String id, String name, Attributes atts) 
throws Exception {
...
} // getElementReplacement

public int getElementRepeatCount (String id, String name, Attributes atts) 
throws Exception {
...
} // getElementRepeatCout

public Attributes getElementAttributes (String id, String name, Attributes atts)
throws Exception {
...
} // getElementAttributes

public InputStream getElementIncludeSource (String id, String name, Attributes atts)
throws Exception {
...
} // getElementIncludeSource

} // BasicTestHandler


public void doGet (HttpServletRequest req, HttpServletResponse res)
throws ServletException {

try {
HamletHandler handler = new BasicTestHandler (this);
serveDoc (req, res, "BasicTestTemplate.html", handler);
} catch (Exception e) {
category.error ("", e);
throw new ServletException (e);
} // try

} // doGet

} // BasicTest

次に Hamlet の serveDoc() メソッドがコールされ、テンプレートの名前とハンドラーが引数として (サーブレット・リクエスト (req) およびサーブレット・レスポンス (res) と一緒に) 渡されます。serveDoc() は、リスト 24 のように private serveDoc() メソッドをコールします。


リスト 24. Hamlet の private serveDoc() メソッドは、コンパイル済みテンプレートの Java バイトコードを見つけるために findTemplateClass() をコールします
                
public abstract class Hamlet extends HttpServlet implements ContentHandler {

...

private void serveDoc (PrintWriter out, String template, ContentHandler handler)
throws Exception {

findTemplateClass (template);
if (templateClass != null) {
...
} else {
...
} // if

} // serveDoc

} // Hamlet

このメソッドは、コンパイル済みテンプレートの Java バイトコードを見つけるために findTemplateClass(template) をコールします。 リスト 25 は、findTemplateClass() を実装したものです。


リスト 25. findTemplateClass() メソッドはテンプレートの Java バイトコードをロードします
                
private void findTemplateClass (String template) throws Exception {
if (!template.equals (oldTemplate)) {
String className = RuntimeUtilities.getClassName (template);
try {
category.debug ("Loading class '" + className + "' ...");
Class c = Class.forName (className);
templateClass = (Template) c.newInstance ();
category.debug ("Class '" + className + "' loaded");
} catch (ClassNotFoundException e) {
category.debug ("Cannot load class '" + className + "'");
} // try
oldTemplate = template;
} // if
} // findTemplateClass

findTemplateClass() は、テンプレートの Java バイトコードをロードする必要があるかどうかをチェックします。その必要がある場合は、RuntimeUtilities.getClassName(template) は、テンプレートのコンパイル済み Java ソース・コードを含むクラス (className) の名前を返します。Class.forName(className) を使えば、インスタンス (templateClass) が作成される前にクラスがロードされます。 リスト 26 は、Template 型のインスタンスを示しています。


リスト 26. テンプレート・インターフェースは、テンプレート・クラスのインターフェースを定義します
                
public interface Template {

public void serveDoc (PrintWriter writer, ContentHandler handler) throws Exception;

} // Template

テンプレート・コンパイラーは、各 HTML テンプレートごとに別々の Java クラスを生成します。これらの各クラスは、Template インターフェースを実装し、そのためリスト 27 に示すように serveDoc() メソッドを提供します。

テンプレートのコンパイル済み Java コードを含むクラスが見つかると、そのクラスは、templateClass.serveDoc(out, handler) をコールすることによって実行されます。そのクラスが見つからないと、Hamlet は非コンパイル・モードに戻ります。つまり、SAX を使ってテンプレートを解析するテンプレート・エンジンを取得します (これは、Hamlet のコンパイルが考えられるまでは、常に行われていたことです)。


リスト 27. Hamlet の serveDoc() メソッドは、テンプレートの Java バイトコードが存在する場合はそのバイトコードを実行し、存在しなけい場合は、テンプレート・エンジンがすぐにテンプレートを解析します。
                
private void serveDoc (PrintWriter out, String template, ContentHandler handler)
throws Exception {

findTemplateClass (template);
if (templateClass != null) {
templateClass.serveDoc (out, handler);
} else {
TemplateEngine engine = getTemplateEngine ();
InputStream in = getServletContext ().getResourceAsStream (template);
category.debug ("Parsing '" + template + "' ...");
long t1 = System.currentTimeMillis ();
engine.perform (in, handler, out);
long t2 = System.currentTimeMillis ();
category.debug ("Parsed '" + template + "' in " + (t2 - t1) + " ms.");
} // if

} // serveDoc

コンパイルすべきでしょうか、それともコンパイルすべきでないのでしょうか。どちらのケースでも、要求は満たしてくれます。コンパイル済みテンプレートを使えば、パフォーマンスは目立って向上することがわかっています。どの程度向上するかは、主にテンプレート内の静的コンテンツの量に依存します。タグが 50 個程度の非常に小さいテンプレート (BasicTestTemplate.html のように) では、コンパイル済み Hamlet の方が 1.2 倍高速になります。タグが 1,000 個程度の大きなテンプレートでは、コンパイルすると、1.75 倍高速に実行されます。



Ant からテンプレート・コンパイラーをコールする

テンプレートのコンパイルは開発時に行われます。そのため、ビルド・スクリプトの中にリスト 28 に示すような Ant タスクをもち、テンプレート・コンパイラーをコールすると便利です。


リスト 28. Ant スクリプトからテンプレート・コンパイラーをコールする
                
<project name="build" default="jar" basedir=".">

...

<target name="template" depends="compile">
<taskdef name="hamletc" classname="com.ibm.hamlet.ant.CompileTask"/>
<hamletc destdir="${dst}">
<fileset dir="${src}">
<include name="*Template.html" />
</fileset>
</hamletc>
</target>

</project>

リスト 29 は、基本的な Ant タスクを実装する方法を示しています。


リスト 29. CompileTask クラスは、テンプレート・コンパイラーをコールするために Ant タスクを実装します。
                
public class CompileTask extends Task {

...

private  String     dstDir;
private  ArrayList  fileSets = new ArrayList ();  


public void setDestdir (String dir) {
dstDir = dir;
System.out.println ("Destination directory: " + dstDir);
} // setDestdir


public void addFileset (FileSet set) {
fileSets.add (set);
} // addFileset


public void execute () {
// setup compiler
TemplateCompiler compiler = new TemplateCompiler ();
compiler.setDebug (debug);
compiler.setVerbose (verbose);
compiler.setDstDir (dstDir);
// invoke compiler for all files
Project project = getProject ();
Iterator iter = fileSets.iterator ();
while (iter.hasNext ()) {
Object elem = iter.next ();
if (elem instanceof FileSet) {
FileSet set = (FileSet) elem;
DirectoryScanner scanner = set.getDirectoryScanner (project);
String srcDir = scanner.getBasedir().getPath ();
System.out.println ("Source directory: " + srcDir);
String fileNames[] = scanner.getIncludedFiles ();
System.out.println ("Compiling " + fileNames.length + 
" template file(s) to " + dstDir);
for (int i = 0; i < fileNames.length; i++) {
String fileName = srcDir + File.separator + fileNames[i];
// System.out.println (" scanning " + fileName);
compiler.setFileName (fileName);
try {
compiler.perform ();
} catch (Exception e) {
throw new BuildException (e);
} // try
} // for
} // if
} // while
} // execute

} // CompileTask

setDestdir() と addFileset() を使ってコンパイルできるように、宛先ディレクトリーをセットし、ファイル・セットを指定した後、リスト 28 に示す Ant タスクを実行すると、com.ibm.hamlet.ant.CompileTask クラスのインスタンスが作成され、その execute() メソッドがコールされます。execute() 内では、ファイル・セットのすべてのテンプレート・ファイルを宛先ディレクトリーにコンパイルするために、テンプレート・コンパイラーはインスタンス化されます。



まとめ

この記事では、テンプレート・ファイルを Java バイトコードに変換するためのテンプレート・コンパイラーという、Hamlet フレームワークに対する小さな追加機能について紹介しました。その結果生成されたコードは Hamlet によって実行され、実行時のユーザー要求を満たしてくれます。このようなコンパイル済みテンプレートを使った Hamlet (いわゆる、コンパイル済み Hamlet) は、パフォーマンスを目立って向上させ、たくさんのユーザーが同時に利用できる Web アプリケーションの開発を可能にします。

テンプレート・コンパイラーの完全な Java ソース・コード (600 行未満) は、「ダウンロード」セクションで入手可能です。





ダウンロード

内容ファイル名サイズダウンロード形式
Complete Java source codefor template compilerwa-hamlets4/hamlet-compiler.zip6KBHTTP

ダウンロード形式について


参考文献

学ぶために

  • Introducing Hamlets,」 Rene Pawlitzek (developerWorks, 2005年3月): Hamlet プログラミングの基礎を習得し、少量のコードを使って、コンテンツとプレゼンテーションを分離する方法について学びます。

  • Programming Hamlets,」 Rene Pawlitzek (developerWorks, 2005年3 月): このチュートリアルでは、Hamlet v1.0 プログラミングのさまざまな面を学び、いくつかの実用的な Hamlet の例を検討します。

  • Implementing Hamlets,」 Rene Pawlitzek (developerWorks, 2006年2月): Hamlet v1.1 の実装および動的コンテンツを提供する別の方法である HamletHandler についてお読みください。

  • The Java Servlet API White Paper: Java サーブレットの概要を入手してください。

  • developerWorks の Web アーキテクチャー・ゾーン: Web テクノロジーに特化された記事およびチュートリアルを使って、サイト開発のスキルを高めてください。

  • developerWorks technical events and webcasts: 短期間で習得できるたくさんの情報が詰まったテクニカル・セッションで、最新の情報を入手し、難しいソフトウエア・プロジェクトの品質および結果の向上に役立ててください。

製品や技術を入手するために

  • SAX (Simple API for XML): 広く使われているこの API をチェックしてください。

  • Ant: Java テクノロジー・ベースのビルド・ツールについてたくさんの情報を入手してください。

  • Hamlet フレームワーク: IBM alphaWorks から入手できます。

  • IBM trial software: 次期開発プロジェクトの構築に、developerWorks から直接ダウンロードできる IBM の試用版ソフトウェアをご利用ください。

議論するために

著者について

Photo of Rene Pawlitzek

Rene Pawlitzek は、リヒテンシュタインの市民であり、Swiss Federal Institute of Technology (ETH Zurich) のコンピューター・サイエンスの工学学位を取得しています。スイスにある IBM Zurich Research Laboratory の Advanced Operating Environment グループ (旧 GSAL (Global Security Analysis Lab)) で、セキュリティー情報管理ソリューションにフォーカスした研究・開発のエンジニアとして働いています。IBM に入社する前は、カリフォルニアの Hewlett-Packard、WindRiver Systems、および Borland International で働いていました。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=237582
ArticleTitle=Hamlet をコンパイルする
publish-date=06202006
author1-email=rpa@zurich.ibm.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。