目次


WAS 上で JAXB を使用したアプリケーションを作成する場合の注意点

Comments
1

概要

WebSphere Application Server (以下、WAS) 上でJAXBを使用し、JavaオブジェクトをXMLデータに変換(マーシャリング)する際には、「"」「'」「<」「>」「&」の5文字は、エスケープされるとは限りません。明示的にエスケープさせるには、アプリケーションでの対応が必要になります。

「 “‘<>& 」の5文字をエスケープする方法については、以下の資料に記載されています。本記事では、Javaの単体プログラムとWAS上のサーブレットの動作の違いを確認し、以下の資料に記載されているアプリケーションでの対応方法をWAS上で検証した結果を記載します。

CoderLeaf: Controlling character escaping with jaxb

2

Javaの単体プログラムの動作確認

以下のようなTeststrクラス(Teststr.java)を作成します。「 "'<>& 」のそれぞれの文字に対して、setterとgetterメソッドが定義されています。

": setQuote()/getQuote()
': setAmpersand()/getAmpersand()
<: setLessthan()/getLessthan()
>: setGreaterthan()/getGreaterthan()
&: setAmpersand()/getAmpersand

Teststr.java
package com.ibm.jp.sample;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "Teststr")
public class Teststr {
  private String ap;
  private String qt;
  private String am;
  private String gt;
  private String lt;

  public void setApostrophe(String s){ ap = s; }
  public String getApostrophe(){ return ap; }
  public void setQuote(String s){ qt = s; }
  public String getQuote(){ return qt; }
  public void setAmpersand(String s){ am = s; }
  public String getAmpersand(){ return am; }
  public void setGreaterthan(String s){ gt = s; }
  public String getGreaterthan(){ return gt; }
  public void setLessthan(String s){ lt = s; }
  public String getLessthan(){ return lt; }
}

次に以下のように、JAXBTestクラス(JAXBTest.java)を作成し、Teststrクラスを呼び出します。Teststrクラスの各メソッドを使用して「 "'<>& 」の文字を設定し、JAXBContextのMarshallerを使用して、TeststrクラスのオブジェクトをXMLに変換します。

JAXB.java
public class JAXBTest {
  public static void main(String[] args){
    Teststr teststr = new Teststr();
    teststr.setApostrophe("'");
    teststr.setQuote("\"");
    teststr.setAmpersand("&");
    teststr.setGreaterthan(">");
    teststr.setLessthan("<");

    try {
      JAXBContext jc = JAXBContext.newInstance(Teststr.class);
      System.out.println(jc.getClass().getName());
      Marshaller m = jc.createMarshaller();
      System.out.println(m.getClass().getName());
      m.marshal(teststr, System.out);
    }
    catch (Exception e){
      throw new RuntimeException(e);
    }
  }
}

Linuxの環境で動作させることを前提に、以下のようなMakefileを作成します。

Makefile
JC=/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/javac
JAVA=/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java
PKG=com/ibm/jp/sample

OBJS=$(PKG)/Teststr.class $(PKG)/JAXBTest.class

all: $(OBJS)

$(PKG)/Teststr.class: $(PKG)/Teststr.java
  $(JC)  -cp . $(PKG)/Teststr.java

$(PKG)/JAXBTest.class: $(PKG)/JAXBTest.java
  $(JC)  -cp .:$(PKG) $(PKG)/JAXBTest.java

clean:
  rm -f $(PKG)/*.class

run:
  $(JAVA) -cp . $(PKG)/JAXBTest

version:
  $(JAVA) -version

作成したMakefileを使用し、以下のようにmakeコマンドでプログラムをコンパイルします。

$ make
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/javac  -cp . com/ibm/jp/sample/Teststr.java
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/javac  -cp .:com/ibm/jp/sample com/ibm/jp/sample/JAXBTest.java

プログラムを以下のコマンドで実行(JAXBTestクラスを実行)すると、下記のような出力が得られます。

$ make run
/opt/IBM/WebSphere/AppServer/java_1.7_64/bin/java -cp . com/ibm/jp/sample/JAXBTest
com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl
com.sun.xml.internal.bind.v2.runtime.MarshallerImpl
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Teststr><ampersand>&amp;</ampersand><apostrophe>'</apostrophe><greaterthan>&gt;</greaterthan><lessthan>&lt;</lessthan><quote>&quot;</quote></Teststr>

「 "'<>& 」の5文字に対して、下記のように「'」だけはエスケープされないことが確認できます。また、JAXBとして、com.sun.xml.internal.bind.v2.runtime.JAXBContextImplが使用されていることが確認できます。

": &quot;
': '
<: &lt;
>: &gt;
&: &amp;
3

WAS上のサーブレットの動作確認

WAS上で以下のようなサーブレット(DoActionServlet.java)を作成し、そのサーブレットの中から、前述のTeststrクラスを呼び出し、オブジェクトをマーシャリングします。マーシャリングされたXMLの出力は、WASのログ(SystemOut.log)に出力されます。

DoActionServlet.java
package com.ibm.jp.sample;
import java.io.IOException;
import java.io.*;
import javax.inject.Inject;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import javax.xml.bind.JAXB;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.*;
import com.ibm.jtc.jax.tools.xjc.runtime.*;
import com.ibm.jp.sample.Teststr;

@WebServlet("/doAction")
public class DoActionServlet extends HttpServlet {
  public DoActionServlet() {
    super();
  }
  
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws  ServletException, IOException {
    String req_uri = request.getRequestURI();
    StringBuffer req_url = request.getRequestURL();

try {
      JAXBContext jc = JAXBContext.newInstance("com.ibm.jp.sample");
      System.out.println(jc.getClass().getName());
      Marshaller m = jc.createMarshaller();
      System.out.println(m.getClass().getName());
      Teststr teststr = new Teststr();
      teststr.setApostrophe("'");
      teststr.setQuote("\"");
      teststr.setAmpersand("&");
      teststr.setGreaterthan(">");
      teststr.setLessthan("<");
      m.marshal(teststr, System.out);
    }
    catch (Exception e){
      throw new RuntimeException(e);
    }

    response.setCharacterEncoding("Cp943C");
    response.setHeader("Content-Language", "jp");
    PrintWriter out = response.getWriter();
    out.println("<html><head><title>JAXB test</title></head>");
    out.println("<body>Look at SystemOut.log</body></html>");
  }
}

以下は、プログラムの実行後のSystemOut.logの出力結果です。

[2/4/19 7:28:39:788 JST] 0000006b SystemOut     O com.ibm.xml.xlxp2.jaxb.JAXBContextImpl
[2/4/19 7:28:39:790 JST] 0000006b SystemOut     O com.ibm.xml.xlxp2.jaxb.marshal.MarshallerImpl
[2/4/19 7:28:39:794 JST] 0000006b SystemOut     O <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Teststr><apostrophe>'</apostrophe><lessthan>&lt;</lessthan><quote>"</quote><greaterthan>&gt;</greaterthan><ampersand>&amp;</ampersand></Teststr>

「 "'<>& 」の5文字に対して、下記のように「'"」の2文字がエスケープされないことが確認できます。また、JAXBとして、WASが提供するcom.ibm.xml.xlxp2.jaxb.JAXBContextImplが使用されていることが確認できます。

": "
': '
<: &lt;
>: &gt;
&: &amp;
4

アプリケーションでの対応方法

WASやJavaの実装に依存せず、「"'<>& 」の5文字をJAXBでXMLに変換する際に、「  "'<>& 」に明示的にエスケープするには、アプリケーション側で実装する必要があります。

  1. Javaで提供されるJAXBを使用する場合には、com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler を実装したクラスを作成して、その中で対象の文字をエスケープします。
  2. WAS上で稼働するアプリケーションの場合は、com.ibm.jtc.jax.xml.bind.marshaller.CharacterEscapeHandler の実装したクラスを作成して、その中で対象の文字をエスケープします。

以下は、WAS上のサーブレットでの実装例です。前述のDoActionServlet.javaに対して以下のimport文を追加します。

import com.ibm.jtc.jax.xml.bind.marshaller.CharacterEscapeHandler;
import com.ibm.jp.sample.CompleteCharacterEscapeHandler;

次に、作成するマーシャラーに対して、以下のようにプロパティーを設定します。

Marshaller m = jc.createMarshaller();
m.setProperty(
       "com.ibm.jtc.jax.xml.bind.marshaller.CharacterEscapeHandler",
        new CompleteCharacterEscapeHandler());

以下は、独自実装したハンドラー(CompleteCharacterEscapeHandler.java)の例です。

CompleteCharacterEscapeHandler.java
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.DatatypeConverter;
import com.ibm.jtc.jax.tools.xjc.runtime.*;
import com.ibm.jtc.jax.xml.bind.marshaller.CharacterEscapeHandler;

public class CompleteCharacterEscapeHandler implements CharacterEscapeHandler {

  public CompleteCharacterEscapeHandler() {
    super();
  }

/**
 * @param ch The array of characters.
 * @param start The starting position.
 * @param length The number of characters to use.
 * @param isAttVal true if this is an attribute value literal.
*/
  public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
    int limit = start + length;
    for (int i = start; i < limit; i++) {
      char c = ch[i];
      if (c == '&' || c == '<' || c == '>' || c == '\"' || c == '\'') {
        if (i != start) {
          out.write(ch, start, i - start);
        }
        start = i + 1;
        switch (ch[i]) {
          case '&':
            out.write("&amp;");
            break;
          case '<':
            out.write("&lt;");
            break;

          case '>':
            out.write("&gt;");
            break;

          case '\"':
            out.write("&quot;");
            break;

          case '\'':
            out.write("&apos;");
            break;
        }
      }
    }
    if (start != limit) {
      out.write(ch, start, limit - start);
    }
  }
}

以下は、プログラムの実行後のSystemOut.logの出力結果です。

[2/4/19 7:41:58:931 JST] 0000005a SystemOut     O com.ibm.xml.xlxp2.jaxb.JAXBContextImpl
[2/4/19 7:41:58:933 JST] 0000005a SystemOut     O com.ibm.xml.xlxp2.jaxb.marshal.MarshallerImpl
[2/4/19 7:41:58:933 JST] 0000005a SystemOut     O <?xml version="1.0" encoding="UTF-8" standalone="yes"?><Teststr><ampersand>&amp;</ampersand><apostrophe>&apos;</apostrophe><greaterthan>&gt;</greaterthan><lessthan>&lt;</lessthan><quote>&quot;</quote></Teststr>

上記の出力から、「 "'<>& 」の5文字すべてがエスケープされていることが確認できます。

": &quot;
': &apos;
<: &lt;
>: &gt;
&: &amp;

参考: 添付ファイルについて

本記事で記載したプログラムは、添付ファイル「WAS_JAXB_escape_chars_sample_programs_20190520A.tgz」に格納されております。

上記のファイルを展開 (tar ztvf コマンド使用) すると、以下のディレクトリーが作成されます。

1) d_jaxb_standalone_with_javax
2) d_jaxb_test_webapp_on_was

"1)" のディレクトリー配下の"test01"ディレクトリーに、上記本文の"Javaのスタンドアロンプログラムの実行例"のプログラムがあります。プログラムのコンパイル、実行方法は、"1)" のディレクトリー直下のREADMEファイルに記載しております。

"2)"のディレクトリー配下の"test01"ディレクトリーに、上記法文の"WAS上のサーブレットで実行した例"のプログラムがあります。また、"対応"に記載した、WAS上での対応方法のプログラムは、"2)"のディレクトリー配下の"test02"ディレクトリーにあります。"2)"のディレクトリー配下のtest01, test02のプログラムのコンパイル方法、実行方法は、"2)"のディレクトリー直下のREADMEファイルに記載しております。


ダウンロード可能なリソース


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=WebSphere, Java technology
ArticleID=1066781
ArticleTitle=WAS 上で JAXB を使用したアプリケーションを作成する場合の注意点
publish-date=09132019