Friday, December 24, 2004

Reason #2 for Java 1.5: XSL Transformations ใน 1.4.2 อืด

ต่อจาก: เหตุผลข้อ 1

สมมติว่ามีไฟล์ XML ชื่อ small.xml แบบนี้

$ cat small.xml
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p align="center">
<module name="Real Module">
UEsDBBQACAAIAJCKkTEAAAAAAAAAAAAAAAAGAAAAMDEuSlBH7bsJWFPJ1i68FZXuVqGdQETAAbQV
BQEBBTF204iIQivzECJiQAxhEEIUOknbKhwFQUFAxihTgBAQAoQEIU6AMoUpCRAgKgokIUQlCbpD
kj/aw+lz7pm+77/ffc69zymevbNrWrVW1aq13lV7oxxSvgC+PnrY6TCwaPEi4IjqD1AKl5f+cDYi
JiL6bETkJguTPcC+75yOqwmBT+nrTy2ARcDi334/1S1Z+bluierS/5zX+cu82tzn/JfAr2nRZ4pL
vvo18zm3/evPua8+5T+nz5TVJJ+fjwCmwcGqmjDVfdmvvX4puZed83uJ+ee7r+r+W4nl5/vuP5d8
pvnh83OkStK/mdSmP1X/76/7LPMXv87iZ7kXH/rl+rVO//dZ+yW/4XM+9HfufxEBrbrEn9tZqCTb
A5j9Qut/SX8o+9T+U79/lj6PuuqXLr+u0drfefo8d0tWfMp+ufJX+qo5BNxVjz8AQLhKji/eAeuB
</module>
</p>
</body>
และเขียนไฟล์สำหรับแปลง XML คือ huitest.xsl แบบนี้
$ cat huitest.xsl 
<?xml version="1.0"?>
<xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:preserve-space elements="p" />
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
        <body>
        <xsl:apply-templates/>
        </body>
</xsl:template>

<xsl:template match="module">
<module>
<xsl:value-of select="./@name" />
</module>
</xsl:template>

</xsl:stylesheet>
คือ ต้องการเปลี่ยนจาก <module name=xxx>yyy</module> เป็น <module>xxx</module> ตัดส่วนที่เป็นเนื้อหา yyy ทิ้งให้หมด

งานนี้สามารถใช้คลาส javax.xml.transform.Transformer ของ Java ทำได้เลย โดยส่งไฟล์ XML ต้นทาง กับไฟล์ XSL ที่กำหนดหลักการแปลงไปให้ เช่น เอาโปรแกรมส่วนของ Transformer ที่ฝนเคยทำไว้มาแก้เป็น Transform.java

$ cat Transform.java 
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;

public class Transform {
    public static void main(String [] arg) {
        System.out.println("Transform\n");

        try {
            InputStreamReader xmlReader = 
                new InputStreamReader(new FileInputStream(arg[0]), "UTF8");
            TransformerFactory tFactory = TransformerFactory.newInstance();
            OutputStreamWriter out = 
                new OutputStreamWriter(new FileOutputStream("out-"+arg[0]), 
                                       "UTF8");
            Source xmlSource = new StreamSource(xmlReader);
            Source srcSource = new StreamSource(arg[1]);
            Transformer transformer1 = tFactory.newTransformer(srcSource);
            System.out.println("Transform: start");
            transformer1.transform(xmlSource, new StreamResult(out));
            System.out.println("Transform: complete");
            xmlReader.close();
            out.flush();
            out.close();
        } 
        catch (Exception ex) {
            System.out.println("Error: "+ex);
        }
    }
}
แล้วเรียกใช้งานโดย
$ javac Transform.java 
$ java Transform small.xml huitest.xsl 
Transform
Transform: start
Declared encoding "UTF-8" does not match actual one "UTF8"; this might not be an error.
Transform: complete

$ cat out-small.xml 
<?xml version="1.0" encoding="UTF-8"?>
<body>
<module>Real Module</module>
</body>
ซึ่งดูเหมือนจะทำงานได้เรียบร้อยดี

ปัญหา คือ ข้อมูลที่เป็น Base64 ตรงกลางระหว่าง <module> กับ </module> ในไฟล์ที่ใช้จริง มีขนาดยาวมากๆ (มาจาก Video) ประมาณ 30 MB ! คือ

$ cat large.xml
<?xml version="1.0" encoding="UTF-8"?>
<body>
<p align="center">
<module name="Real Module">
UEsDBBQACAAIAJCKkTEAAAAAAAAAAAAAAAAGAAAAMDEuSlBH7bsJWFPJ1i68FZXuVqGdQETAAbQV
BQEBBTF204iIQivzECJiQAxhEEIUOknbKhwFQUFAxihTgBAQAoQEIU6AMoUpCRAgKgokIUQlCbpD
kj/aw+lz7pm+77/ffc69zymevbNrWrVW1aq13lV7oxxSvgC+PnrY6TCwaPEi4IjqD1AKl5f+cDYi
...
... (~30MB of text)
...
JiL6bETkJguTPcC+75yOqwmBT+nrTy2ARcDi334/1S1Z+bluierS/5zX+cu82tzn/JfAr2nRZ4pL
vvo18zm3/evPua8+5T+nz5TVJJ+fjwCmwcGqmjDVfdmvvX4puZed83uJ+ee7r+r+W4nl5/vuP5d8
pvnh83OkStK/mdSmP1X/76/7LPMXv87iZ7kXH/rl+rVO//dZ+yW/4XM+9HfufxEBrbrEn9tZqCTb
A5j9Qut/SX8o+9T+U79/lj6PuuqXLr+u0drfefo8d0tWfMp+ufJX+qo5BNxVjz8AQLhKji/eAeuB
</module>
</p>
</body>
ลองคำสั่งเดิมกับไฟล์นี้ คือ java Transform large.xml huitest.xsl ปรากฏว่าเงียบหายไป ~45 นาที ! แถมไม่ได้ผลลัพท์อะไรออกมา ได้แต่ข้อความ
Exception in thread "main" java.lang.OutOfMemoryError
2810.596u 6.453s 46:57.57 99.9% 0+0k 0+0io 1pf+0w
เป็นงงว่า Transform ใน Java 1.4.2 มัวทำอะไรอยู่ ? กิน CPU เต็มตลอด 99.9% และทำไมกินหน่วยความจำมากนัก ?

ปัญหานี้เจอมาหลายเดือนแล้ว ตอนนั้นเลี่ยงไปใช้วิธีอื่น ไม่เอาข้อมูลวิดีโอใส่ลงไปในไฟล์ XML ตรงๆ พอดีเริ่มลองเล่น 1.5 ก็เลยลองอีกทีกับ 1.5 ดู ใช้ Transform.class ตัวเดิมที่คอมไพล์ไว้ด้วย javac ของ 1.4.2

$ time ~/temp/jdk1.5.0/bin/java Transform large.xml huitest.xsl
Transform
Transform: start
Transform: complete
4.555u 0.392s 0:04.02 122.8%    0+0k 0+0io 1pf+0w
ทำเสร็จได้ผลลัพท์ถูกต้องภายในเวลา 4.5 วินาที ! เร็วกว่าเดิม (Java 1.4.2_06) ประมาณ 60 เท่า !

No comments: