tags:

views:

262

answers:

3

I have a framework that generates the XML, based on the HTTP request and the current session state. I may test in HTML, but production output will be VXML - perhaps one or two "flavors" for different reasons.

Here's the slow part of my HttpServlet:

jsp InputStream ms = new java.io.ByteArrayInputStream(sb.toString().getBytes());
Source xmlSource = new javax.xml.transform.stream.StreamSource(ms);
String filePath = getServletContext().getRealPath(("/GetNextEvent-").
        concat(req.getSession().getAttribute("client").toString().toUpperCase()).concat(".xsl"));
Source xsltSource = new javax.xml.transform.stream.StreamSource(filePath);
Result result = new javax.xml.transform.stream.StreamResult(resp.getWriter());
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer(xsltSource);
t.transform(xmlSource, result);

This currently takes ~ 200ms. I'd like for it to be much quicker. Perhaps < 10ms?

  1. Suggestions for caching? - seeing that the xsl files stay the same throughout the deployment, the Transformer objects can be cached indefinitely. I'm thinking of caching it in the Session level, so each session (1000's simultaneous) has their own. Any suggestions? Should I use any frameworks for caching, for any reason?
  2. Is there a faster way to transform the xml to the response stream?
  3. Should I scrap this and go another route? If you noticed the sb.toString, I am using a StringBuilder to get the XML representation of the objects (objects use a stringbuilder to create XML string). It takes about 1 millisecond to create the XML document using the StringBuilders, so I'm not concerned about it at the moment.

Edit:

Here's the XSL document. The XML document is usually very small. Just a couple of elements. XML sample is below XSL:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:regexp="http://exslt.org/regular-expressions"
    xmlns:str="http://exslt.org/strings" xmlns:twc="http://twc.com/2009/01/ivr/framework"
    exclude-result-prefixes="twc regexp str" extension-element-prefixes="str">
    <xsl:output method="xml" encoding="ISO-8859-1" />
    <xsl:template match="/">
        <vxml xmlns="http://www.w3.org/2001/vxml" version="2.1" xml:lang="en-US"
            application="root.xml">
            <xsl:attribute name="xml:lang"><xsl:value-of
                select="//twc:response/@language" /></xsl:attribute>
            <form id="ivrFramework">
                <var name="logDebug">
                    <xsl:attribute name="expr"><xsl:value-of
                        select="//twc:response/@debug" /></xsl:attribute>
                </var>
                <var name="event" expr="'OK'" />
                <var name="lastResult" expr="''" />
                <var name="lastResultMode" expr="''" />
                <var name="lastResultValue" expr="''" />
                <var name="srConfidence" expr="'1000'" />

                <xsl:apply-templates select="//twc:command" />
                <xsl:if test="count(//twc:command)=0">
                    <block>
                        <log cond="logDebug" expr="'No more commands.  Exiting.'" />
                        <exit />
                    </block>
                </xsl:if>
            </form>
        </vxml>
    </xsl:template>

    <xsl:template
        match="twc:command[@type='prompt' and contains(text(), 'TransferDialog')]">
        <transfer name="quicktransfer"  type="consultation">
            <xsl:attribute name="destexpr"><xsl:choose>
                <xsl:when test="//twc:parameter[twc:name='destination']">'<xsl:value-of
                select="//twc:parameter[twc:name='destination']/twc:value" />'</xsl:when>
                <xsl:otherwise>'tel:1136300'</xsl:otherwise>
            </xsl:choose>
            </xsl:attribute>
            <xsl:if test="//twc:parameter[twc:name='initial']">
                <prompt>
                    <xsl:call-template name="process_prompt">
                        <xsl:with-param name="prompt_type" select="'initial'" />
                    </xsl:call-template>
                </prompt>
            </xsl:if>
        </transfer>
    </xsl:template>

    <xsl:template
        match="twc:command[@type='prompt' and contains(text(), 'BasicDialog')]">
        <xsl:choose>
            <xsl:when test="//twc:parameter[twc:name='grammar']/twc:value">
                <field>
                    <xsl:attribute name="name"><xsl:value-of
                        select="//twc:parameter[twc:name='variable']/twc:value" /></xsl:attribute>
                    <noinput count="3">
                        <assign name="event" expr="'noinput'" />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </noinput>
                    <nomatch count="3">
                        <assign name="event" expr="'invalid'" />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </nomatch>

                    <xsl:for-each select="//twc:parameter[twc:name='grammar']/twc:value">
                        <grammar>
                            <xsl:attribute name="src"><xsl:if test="//twc:response/@base!=''"><xsl:value-of select="//twc:response/@base" /></xsl:if><xsl:value-of
                                select="." /></xsl:attribute>
                        </grammar>
                    </xsl:for-each>
                    <xsl:if test="//twc:parameter[twc:name='help']">
                        <help>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'help'" />
                            </xsl:call-template>
                        </help>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='noinput1']">
                        <noinput count="1">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'noinput1'" />
                            </xsl:call-template>
                        </noinput>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='noinput2']">
                        <noinput count="2">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'noinput2'" />
                            </xsl:call-template>
                        </noinput>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='invalid1']">
                        <nomatch count="1">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'invalid1'" />
                            </xsl:call-template>
                        </nomatch>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='invalid2']">
                        <nomatch count="2">
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'invalid2'" />
                            </xsl:call-template>
                        </nomatch>
                    </xsl:if>
                    <xsl:if test="//twc:parameter[twc:name='initial']">
                        <prompt>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'initial'" />
                            </xsl:call-template>
                        </prompt>
                    </xsl:if>
                    <filled>
                        <log cond="logDebug" expr="'Filled.'" />
                        <assign name="event" expr="'OK'" />
                        <assign name="lastResult" expr="application.lastresult$.utterance" />
                        <assign name="lastResultMode" expr="application.lastresult$.inputmode" />
                        <assign name="lastResultValue" expr="application.lastresult$.interpretation" />
                        <assign name="srConfidence" expr="application.lastresult$.confidence " />
                        <submit next="GetNextEvent2.jsp"
                            namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                    </filled>

                </field>
            </xsl:when>
            <xsl:when test="//twc:parameter[twc:name='initial']/twc:value">
                <block>
                    <xsl:if test="//twc:parameter[twc:name='initial']">
                        <prompt>
                            <xsl:call-template name="process_prompt">
                                <xsl:with-param name="prompt_type" select="'initial'" />
                            </xsl:call-template>
                        </prompt>
                    </xsl:if>
                    <submit next="GetNextEvent2.jsp"
                        namelist="event lastResult lastResultMode lastResultValue srConfidence" />
                </block>
            </xsl:when>
            <xsl:otherwise>
                <block>
                    <log cond="logDebug" expr="'Didn't find values for grammar or initial.  Exiting.'" />
                    <exit />
                </block>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="process_prompt">
        <xsl:param name="prompt_type" />
        <xsl:for-each select="//twc:parameter[twc:name=$prompt_type]/twc:value">
            <xsl:if test="contains(., '::')">
                <audio>
                    <xsl:for-each select="str:split(., '::')">
                        <xsl:if test="position()=1">
                            <xsl:attribute name="src"><xsl:if test="//twc:response/@base!=''"><xsl:value-of select="//twc:response/@base" /></xsl:if><xsl:value-of
                                select="." /></xsl:attribute>
                        </xsl:if>
                        <xsl:if test="position()=2">
                            <xsl:value-of select="." />
                        </xsl:if>
                    </xsl:for-each>
                </audio>
            </xsl:if>
            <xsl:if test="contains(., 'Date:')">
                <say-as interpret-as="date" format="ymd">
                    <xsl:for-each select="str:split(., ':')">
                        <xsl:if test="position()=2">
                            <xsl:value-of select="." />
                        </xsl:if>
                    </xsl:for-each>
                </say-as>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Here's some XML:

<?xml version="1.0"?>
<response xmlns="http://twc.com/2009/01/ivr/framework" language="en-us" debug="true"
    base="/IVRFrameworkResources/Outage/">
    <command type="prompt"> BasicDialog <parameter>
            <name>initial</name>
            <value>en-us/prompts/OutageCleared.wav::Hello.  I'm letting you know the
                incident that caused your outage has been fixed. </value>
        </parameter>
    </command>
</response>
A: 

I have met some tool that converts XSLT file into the java parsing code, but cannot find any reference right now. Sorry for incomplete answer, but I'm interesting to find it too.

Dewfy
This tool is Saxon and Michael Kay just asked the saxon-help list if anybody needed it, so that he'd know whether to continue to maintain it. Post your reply there.
Dimitre Novatchev
@Dimitre Novatchev - yep
Dewfy
+9  A: 

It is difficult to diagnose performance issues without seeing the XSLT or knowing how large/comlex the XML and XSLT are.

You could be paying the cost of parsing the file(s), either the XSLT or the XML and/or you could have a very inefficient XSLT stylesheet.

For instance:

  1. Lots of // XPATH statements, which if not needed can hurt performance for very large XML files.
  2. Logic buried inside of templates that could be moved up into the template @match criteria, which provides the opportunity for XSLT engines to optimize.

There are XSLT profilers that you can use to see where the bottlenecks are in your XSLT. For instance, oXygen has a very nice debugger/profiler: alt text alt text

If you will be running the XSLT many times, then you should cache the transformer object . That way, you only pay the cost to load and instantiate it once and re-use many times.

For instance, move the instantiation of your XSLT Template object into your severlet init()

 TransformerFactory transFact = TransformerFactory.newInstance();
 Templates cachedXSLT = transFact.newTemplates(xsltSource);

and then where you perform the transform, use the cached TransformerFactory obj:

Transformer t= cachedXSLT.newTransformer();
t.transform(xmlSource, result);
Mads Hansen
+1 for using Templates. If using Xalan as a XSLT transformer try using the XSLTC version which compiles stylesheets and is much faster. See http://xml.apache.org/xalan-j/xsltc_usage.html#apiIf using Xalan I'd check the DOM used, TinyTree has some major advantages over the normal Java DOM in some cases (results may vary, but memory usage is much better for TinyTree).
deathy
+3  A: 

Even with caching, the reason for unacceptable performance is often in the XSLT code itself -- which you haven't shown at all.

In my experience there have been cases when I was able to change an inefficient XSLT implementation in such a way that it was speeded up thousands of times.

Quite often the author implements an O(N^2) algorithm or worse, when an O(N) or even O(log(N)) algorithms exist.

Specify to us the problem being solved and provide the XSLT code that solves it. Then it might be possible for someone to give you a better performing solution.

Dimitre Novatchev
That's for pointing it out. I have to give then answer credit to @Mads. I will post the XSLT.
ericp
+1, full ACK to your point.
Tomalak