Odd observation during debugging

Christopher Schultz <[email protected]>
Newsgroups gmane.comp.jakarta.velocity.user
Message-ID <[email protected]>
All,

I recently started getting intermittent OOMEs in my webapp that seem to
be completely recoverable. The stack traces look like this:

java.lang.OutOfMemoryError: Java heap space
        at java.util.Arrays.copyOf(Arrays.java:2882)
        at
java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
        at
java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:390
)
        at java.lang.StringBuilder.append(StringBuilder.java:119)
        at java.lang.StringBuilder.append(StringBuilder.java:115)
        at
java.util.AbstractCollection.toString(AbstractCollection.java:422)
        at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTReference.java:547)
    [...]

This happens so rarely, it's hard to catch, and it's fairly obvious what
is happening: toString is being called on a really huge List. We try not
to use really huge lists, but I suppose there is some potential danger
in rare instances. I want to find those instances.

Velocity 1.7, Tools 2.0 (plus some committed bugfixes that haven't made
it into an official release, yet).

So I thought I'd add some trickery to my VelocityContexts: We use our
own subclasss of VelocityLayoutServlet for a number of things, and I
have overridden the createContext() to wrap the Context the
super.createContext() returns in a ChainedContext (btw, that's
deprecated but doesn't document a replacement -- what should I use?). My
wrapper wraps all List and Set objects in yet another set of wrappers
which essentially pass-through everything except that their toString
methods throw an exception: there should be no reason that a List or Set
should have toString() called on it, because they should only be
iterated over, for instance.

But right on my login page, I see this exception after such instrumentation:

SEVERE: Servlet.service() for servlet velocity threw exception
org.apache.velocity.exception.VelocityException: Reference evaluation
threw an exception at /layout/header.vm[line 18, column 5]
        at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTRefe
rence.java:551)
        at
org.apache.velocity.runtime.parser.node.ASTExpression.evaluate(ASTExp
ression.java:62)
        at
org.apache.velocity.runtime.parser.node.ASTIfStatement.render(ASTIfSt
atement.java:85)
        at
org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.
java:342)
        at
org.apache.velocity.runtime.directive.Parse.render(Parse.java:260)
        at
org.apache.velocity.runtime.parser.node.ASTDirective.render(ASTDirect
ive.java:207)
        at
org.apache.velocity.runtime.parser.node.SimpleNode.render(SimpleNode.
java:342)
        at org.apache.velocity.Template.merge(Template.java:356)
        [....]
Caused by: java.lang.IllegalStateException: Request /myapp/login.vm,
called toString on key 'extraStylesheets'
        at
com.chadis.web.servlet.VelocityLayoutServlet$WrappedList.toString(VelocityLayoutServlet.java:220)
        at
org.apache.velocity.runtime.parser.node.ASTReference.evaluate(ASTReference.java:547)

If I look at everything that references the key 'extraStylesheets' in my
login page (and the header, etc.), I get only these:

login.vm only includes these references:
#set($extraStylesheets = ['/includes/css/login.css'])

I'm using VelocityLayoutServlet which is using my layouts/Default.vm (no
references to 'extraStylesheets') which includes (via #parse)
'header.vm' which has this to say:

#if($extraStylesheets)
	#foreach($stylesheet in $extraStylesheets)
    <link type="text/css" href="$link.setRelative($stylesheet)"
rel="stylesheet" />
	#end
#end

...and that's it. Given the upper stack trace it seems that the #if() is
evaluating the argument by converting it into a String for evaluation.
Is that intentional? I always used "#if($foo)" to check to see if $foo
had a value of any kind -- that is, if it was non-null (and non-false)
but it appears that the value is being converted into a String for ...
what purpose?

I know a lot of changes have occurred over the years and that
String-conversion is something that has seen some of that action. Is
there a "better" way of determining if a List (or whatever) has a value?
That is, without converting it to a String, first?

One can argue that I shouldn't have Lists of objects so large that
converting them to a String will cause an OOME, but the calling of
toString on these Lists is certainly surprising.

Thanks,
-chris
signature.asc (application/pgp-signature, 307 B)
-----BEGIN PGP SIGNATURE-----
Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
Comment: GPGTools - http://gpgtools.org
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk/9phsACgkQ9CaO5/Lv0PBkHgCfXfztvjiRCQV7U7GQ/0RrNv61
vfEAnjI78qQnn5Uzzi8v0Ine9xDS10lB
=UURM
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.