Quantcast

0.4 performance trouble

classic Classic list List threaded Threaded
52 messages Options
123
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

0.4 performance trouble

Marc Palmer Local
Hi guys,

I had a bit of fun this morning with one of our commercial sites,  
which is making me revisit performance. We know from the benchmarks  
that there are GSP related bottlenecks, with taglibs, GString usage etc.

This particular live server currently serves an average of 22 home  
pages per second. The same bench mark just to serve the contact form  
(not submit it) is avg 0.6 per second. For the latter, you can see  
the CPU hit 100% for several seconds and gradually come back down -  
for a single request.

This didn't seem so bad to me until I think about just say 5 or 10  
people doing it at an overlapping time. The result is the server  
grinds to a halt.

It feels to me like there may be a relatively simple "production  
only" bug happening. Either groovy is recompiling the entire GSP  
every time, or there is some serious problem with taglib performance.

I'm going to run some little tests to see if I can establish where  
the problem is, and it looks like I will need to fork 0.4.2

If anybody else could look at this I'd appreciate it. Normal the  
performance of the content is really acceptable, its just forms that  
are very slow. For my forms this means a bit more groovy script logic  
- but hardly much - and lots of taglib usage (and GStrings and  
closures).

I'm hoping its a production-mode bug where its recompiling the GSP  
every time because it thinks it has changed due to a Tomcat classpath  
quirk.

950,000 leaflets were dropped through peoples' letter boxes this  
week, mentioning our competition form....

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

kate rhodes
> If anybody else could look at this I'd appreciate it. Normal the
> performance of the content is really acceptable, its just forms that
> are very slow. For my forms this means a bit more groovy script logic
> - but hardly much - and lots of taglib usage (and GStrings and
> closures).

While i'm not suggesting you're wrong, it seems a bit suspicious that
the display of forms would be an issue. I mean, as far as gsp / groovy
/ grails is concerned it's just putting together some text. saying
<input type=... vs <div class=... is really no different.

Me thinks it's maybe what's going on in the marshalling of data for the form.

--
- kate = masukomi
http://weblog.masukomi.org/

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
Hmm.  I'm wondering if rewriting the offending tags in Java while you
analyze the cause of the performance hit might help, if you're concerned
about the impact to your live site.  Why do you think this is a
"production-only" issue?

You might try seeing what happens if you run a page which calls your
tags 100s of times.

Me, I haven't had time to check on the performance hit from usage of my
own taglib/plugin, but I would have to imagine that it would be far
worse than what you're using, as my taglib invokes templates which
invoke tags which invoke other templates which may also invoke tags; and
it's all written in Groovy.  To me, at first thought, this indicates
that it might be worthwhile for me to rewrite my tags in Java sometime.  
I wrote this taglib largely to make it a lot easier to write quick
little intranet, personal website, and other low-traffic apps. But I was
also thinking that one day I'd like to use this for something
production-quality, and that I'd need to perf-tune the taglib when that
day came along.

- Daiji
 
Marc Palmer wrote:

> Hi guys,
>
> I had a bit of fun this morning with one of our commercial sites,
> which is making me revisit performance. We know from the benchmarks
> that there are GSP related bottlenecks, with taglibs, GString usage etc.
>
> This particular live server currently serves an average of 22 home
> pages per second. The same bench mark just to serve the contact form
> (not submit it) is avg 0.6 per second. For the latter, you can see the
> CPU hit 100% for several seconds and gradually come back down - for a
> single request.
>
> This didn't seem so bad to me until I think about just say 5 or 10
> people doing it at an overlapping time. The result is the server
> grinds to a halt.
>
> It feels to me like there may be a relatively simple "production only"
> bug happening. Either groovy is recompiling the entire GSP every time,
> or there is some serious problem with taglib performance.
>
> I'm going to run some little tests to see if I can establish where the
> problem is, and it looks like I will need to fork 0.4.2
>
> If anybody else could look at this I'd appreciate it. Normal the
> performance of the content is really acceptable, its just forms that
> are very slow. For my forms this means a bit more groovy script logic
> - but hardly much - and lots of taglib usage (and GStrings and closures).
>
> I'm hoping its a production-mode bug where its recompiling the GSP
> every time because it thinks it has changed due to a Tomcat classpath
> quirk.
>
> 950,000 leaflets were dropped through peoples' letter boxes this week,
> mentioning our competition form....
>
> Marc
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>
>



 
____________________________________________________________________________________
We won't tell. Get more on shows you hate to love
(and love to hate): Yahoo! TV's Guilty Pleasures list.
http://tv.yahoo.com/collections/265 

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Fred Janon
In reply to this post by Marc Palmer Local
Can we see the source for these forms? At least the lazy one (contact)?

On 4/4/07, Marc Palmer <[hidden email]> wrote:
Hi guys,

I had a bit of fun this morning with one of our commercial sites,
which is making me revisit performance. We know from the benchmarks
that there are GSP related bottlenecks, with taglibs, GString usage etc.

This particular live server currently serves an average of 22 home
pages per second. The same bench mark just to serve the contact form
(not submit it) is avg 0.6 per second. For the latter, you can see
the CPU hit 100% for several seconds and gradually come back down -
for a single request.

This didn't seem so bad to me until I think about just say 5 or 10
people doing it at an overlapping time. The result is the server
grinds to a halt.

It feels to me like there may be a relatively simple "production
only" bug happening. Either groovy is recompiling the entire GSP
every time, or there is some serious problem with taglib performance.

I'm going to run some little tests to see if I can establish where
the problem is, and it looks like I will need to fork 0.4.2

If anybody else could look at this I'd appreciate it. Normal the
performance of the content is really acceptable, its just forms that
are very slow. For my forms this means a bit more groovy script logic
- but hardly much - and lots of taglib usage (and GStrings and
closures).

I'm hoping its a production-mode bug where its recompiling the GSP
every time because it thinks it has changed due to a Tomcat classpath
quirk.

950,000 leaflets were dropped through peoples' letter boxes this
week, mentioning our competition form....

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

   http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
I think he's not suggesting that it's forms at fault, but rather the
taglib invocation (which most people have a lot less of in non-form
views today).


kate rhodes wrote:

>> If anybody else could look at this I'd appreciate it. Normal the
>> performance of the content is really acceptable, its just forms that
>> are very slow. For my forms this means a bit more groovy script logic
>> - but hardly much - and lots of taglib usage (and GStrings and
>> closures).
>
> While i'm not suggesting you're wrong, it seems a bit suspicious that
> the display of forms would be an issue. I mean, as far as gsp / groovy
> / grails is concerned it's just putting together some text. saying
> <input type=... vs <div class=... is really no different.
>
> Me thinks it's maybe what's going on in the marshalling of data for
> the form.
>



 
____________________________________________________________________________________
No need to miss a message. Get email on-the-go
with Yahoo! Mail for Mobile. Get started.
http://mobile.yahoo.com/mail 

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local

On 4 Apr 2007, at 15:08, D T wrote:

> I think he's not suggesting that it's forms at fault, but rather the
> taglib invocation (which most people have a lot less of in non-form
> views today).
>

We already established with our benchmarks that GSP/taglibs/Gstrings  
are a peformance issue - i.e. directly rendered output from a  
controller is -much- faster.

These forms use taglibs that call other tags, do a bit of closure  
magic etc for ModelTagLib.

I'm adding some logging to my local grails 0.4.2 source to find out  
where in the request stage the problem is.

...but the amount of time being taken and % CPU used smacks of groovy  
recompile every time.

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Maurice Nicholson old
Bizarrely, no sooner did you mention these problems than I started
getting them! And this after what seemed like someone had performance
tuned my Grails app yesterday. Very annoying how it can be so variable.

So my problem sounds very similar: when rendering a form (actually an
Ajax snippet containing a form): CPU rocketed and stayed there!

I finally located the cause: an infinite loop in one of my domain model
getters. BUT: the getter is totally unrelated to the form contents yet
was nonetheless being invoked during form rendering!

How and why?

I use g:select in a few places like:

            <g:select from="${domainObjectList}" optionKey="id"
optionValue="name" value="${someObject.id}" "/>


now when rendering the list of options in FormTagLib, we have this code:

        // create options from list
        if(from) {
            from.eachWithIndex { el,i ->
                def keyValue = null
                out << '<option '
        // ...
                    else {
                        keyValue = el.properties[optionKey]
                        out << 'value="' << keyValue << '" '
                    }
        // ...

and a bit later

                    if(optionValue instanceof Closure) {
                         out << optionValue(el).toString().encodeAsHTML()
                    }
                    else {
                        out <<
el.properties[optionValue].toString().encodeAsHTML()
        // ...

Looks innocent enough, right? Well not quite. You see that <g:select
from="${domainObjectList}" ... /> is a list of domain object instances.
Now when you do "domainObject.properties[prop]" - as is done *twice* for
each object in the from list - what actually happens is a call to
Grails' SetPropertiesDynamicProperty#get() method, which returns a Map
of properties keyed by name, therefore requiring a call to ALL of the
getters!

This is really inefficient for what should be a single property access,
and is certainly not what people will want to or assume is happening.
Changing it to el[whatever] works in my case. Will this work for all cases?

Could there be other code like this?

[...went away and double checked FormTagLib...]

Correction: FormTagLib actually checks whether the object is a domain
class for *one* of the above calls, so the
SetPropertiesDynamicProperty#get() call only happens once for domain
objects, but twice for most other object types.


Marc Palmer wrote:

>
> On 4 Apr 2007, at 15:08, D T wrote:
>
>> I think he's not suggesting that it's forms at fault, but rather the
>> taglib invocation (which most people have a lot less of in non-form
>> views today).
>>
>
> We already established with our benchmarks that GSP/taglibs/Gstrings
> are a peformance issue - i.e. directly rendered output from a
> controller is -much- faster.
>
> These forms use taglibs that call other tags, do a bit of closure
> magic etc for ModelTagLib.
>
> I'm adding some logging to my local grails 0.4.2 source to find out
> where in the request stage the problem is.
>
> ...but the amount of time being taken and % CPU used smacks of groovy
> recompile every time.
>
> Marc
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local
In reply to this post by Marc Palmer Local

On 4 Apr 2007, at 16:02, Marc Palmer wrote:

>
> On 4 Apr 2007, at 15:08, D T wrote:
>
>> I think he's not suggesting that it's forms at fault, but rather the
>> taglib invocation (which most people have a lot less of in non-form
>> views today).
>>
>
> We already established with our benchmarks that GSP/taglibs/
> Gstrings are a peformance issue - i.e. directly rendered output  
> from a controller is -much- faster.
>
> These forms use taglibs that call other tags, do a bit of closure  
> magic etc for ModelTagLib.
>
> I'm adding some logging to my local grails 0.4.2 source to find out  
> where in the request stage the problem is.
>
> ...but the amount of time being taken and % CPU used smacks of  
> groovy recompile every time.

OK well this I can replicate locally. It takes around 2s after first  
page load on my local Mac or on the lice server, in production mode  
with debug logging on.

This tallies with the benchmarking that shows circa 0.6 req/s.

I'm now breaking down the request time using the log.

Action execution and processing is lightning fast, even with an  
interceptor that retrieves user info based on a cookie. The  
bottleneck looks like taglibs/GStrings currently. Some of our tags  
can be slow to execute, although it varies over time. Perhaps  
unsurprisingly writing out the list of countries in a select tag, and  
the years for a datePicker is taking some time (we are still talking  
nanos here though).

However the results are difficult to make sense of, as parts that  
look slow in one run are fine in another. It's looking like  
concurrency is an issue.

More when I work it out...

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local

Update... its taglibs, content size, or just ModelTagLib:

Contact form with 17 g:modelXXX tags in:

$ ab -c 5 -n 1000 http://localhost:8080/Code/contact
[snip]
Document Path:          /Code/contact
Document Length:        26904 bytes

Concurrency Level:      5
Time taken for tests:   735.090 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      27276772 bytes
HTML transferred:       26904000 bytes
Requests per second:    1.36 [#/sec] (mean)
Time per request:       3675.45 [ms] (mean)
Time per request:       735.09 [ms] (mean, across all concurrent  
requests)
Transfer rate:          37.11 [Kbytes/sec] received

Connnection Times (ms)
               min  mean[+/-sd] median   max
Connect:        0     1    1.8      0    17
Processing:   876  3666  982.3   3580  7152
Waiting:      876  3666  982.3   3580  7152
Total:        876  3666  982.3   3581  7152

Ouch! Remove only the g:modelXXXX tags:

$ ab -c 5 -n 1000 http://localhost:8080/Code/contact
[snip]
Document Path:          /Code/contact
Document Length:        6434 bytes

Concurrency Level:      5
Time taken for tests:   44.597 seconds
Complete requests:      1000
Failed requests:        0
Broken pipe errors:     0
Total transferred:      6828775 bytes
HTML transferred:       6434000 bytes
Requests per second:    22.42 [#/sec] (mean)
Time per request:       222.99 [ms] (mean)
Time per request:       44.60 [ms] (mean, across all concurrent  
requests)
Transfer rate:          153.12 [Kbytes/sec] received

Connnection Times (ms)
               min  mean[+/-sd] median   max
Connect:        0     0    0.0      0     0
Processing:    62   222  104.1    221   791
Waiting:       62   222  104.1    221   791
Total:         62   222  104.1    221   791

Now that's better :)

I just need find the slow bits now...

Marc




---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local
In reply to this post by Maurice Nicholson old

On 4 Apr 2007, at 17:48, Maurice Nicholson wrote:

> Bizarrely, no sooner did you mention these problems than I started  
> getting them! And this after what seemed like someone had  
> performance tuned my Grails app yesterday. Very annoying how it can  
> be so variable.
>
> So my problem sounds very similar: when rendering a form (actually  
> an Ajax snippet containing a form): CPU rocketed and stayed there!
>
> I finally located the cause: an infinite loop in one of my domain  
> model getters. BUT: the getter is totally unrelated to the form  
> contents yet was nonetheless being invoked during form rendering!
>
> How and why?
>
> Looks innocent enough, right? Well not quite. You see that  
> <g:select from="${domainObjectList}" ... /> is a list of domain  
> object instances. Now when you do "domainObject.properties[prop]" -  
> as is done *twice* for each object in the from list - what actually  
> happens is a call to Grails' SetPropertiesDynamicProperty#get()  
> method, which returns a Map of properties keyed by name, therefore  
> requiring a call to ALL of the getters!
>

This is dailyWTF material. I think I may have seen it, commented on  
it in the source, or even perpetrated it. Fisheye "annotated" view  
will reveal all. However I think this is there for a reason - to NOT  
get an exception if the property does not exist.

> This is really inefficient for what should be a single property  
> access, and is certainly not what people will want to or assume is  
> happening. Changing it to el[whatever] works in my case. Will this  
> work for all cases?

Sure, there must be a better way.

> Could there be other code like this?
>

Very possibly, remember we are pre-optimisation :)

> [...went away and double checked FormTagLib...]
>
> Correction: FormTagLib actually checks whether the object is a  
> domain class for *one* of the above calls, so the  
> SetPropertiesDynamicProperty#get() call only happens once for  
> domain objects, but twice for most other object types.

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Maurice Nicholson old
Yeah, it _could_ be a real Groovy performance gotcha and one to watch out for in future.

I can see how it's useful to prevent an exception, but in this case the "optionKey" and "optionValue" property names are provided by the user, so if those proterties don't exist it's a programmer error and they should know about it sooner than later.


Marc Palmer Local wrote
On 4 Apr 2007, at 17:48, Maurice Nicholson wrote:

> Bizarrely, no sooner did you mention these problems than I started  
> getting them! And this after what seemed like someone had  
> performance tuned my Grails app yesterday. Very annoying how it can  
> be so variable.
>
> So my problem sounds very similar: when rendering a form (actually  
> an Ajax snippet containing a form): CPU rocketed and stayed there!
>
> I finally located the cause: an infinite loop in one of my domain  
> model getters. BUT: the getter is totally unrelated to the form  
> contents yet was nonetheless being invoked during form rendering!
>
> How and why?
>
> Looks innocent enough, right? Well not quite. You see that  
> <g:select from="${domainObjectList}" ... /> is a list of domain  
> object instances. Now when you do "domainObject.properties[prop]" -  
> as is done *twice* for each object in the from list - what actually  
> happens is a call to Grails' SetPropertiesDynamicProperty#get()  
> method, which returns a Map of properties keyed by name, therefore  
> requiring a call to ALL of the getters!
>

This is dailyWTF material. I think I may have seen it, commented on  
it in the source, or even perpetrated it. Fisheye "annotated" view  
will reveal all. However I think this is there for a reason - to NOT  
get an exception if the property does not exist.

> This is really inefficient for what should be a single property  
> access, and is certainly not what people will want to or assume is  
> happening. Changing it to el[whatever] works in my case. Will this  
> work for all cases?

Sure, there must be a better way.

> Could there be other code like this?
>

Very possibly, remember we are pre-optimisation :)

> [...went away and double checked FormTagLib...]
>
> Correction: FormTagLib actually checks whether the object is a  
> domain class for *one* of the above calls, so the  
> SetPropertiesDynamicProperty#get() call only happens once for  
> domain objects, but twice for most other object types.

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

kate rhodes
Some graphs for your consideration.

These were done on our real app this afternoon. The form that's
brought up in the Worst Case Scenario section are pretty much the most
complex form you would ever expect to see on a web page (We're
replicating a paper form) it's big, it's ugly, it spans 4 or 5 tables
and loads a lot of objects. Also to note that this was running under
J9 NOT the Sun JVM because the Sun JVM doesn't run under PPC Linux.
But, as far as we can tell, once you get past the hurdle of actually
making a grails app run under J9 (I'll post the details on that
particular evil shortly) it seems to be fine.

JMeter was running on a separate box with a gigabit connection to a
PPC blade server on the same lan which was hosting our app and nothing
else (2.5 gigs of ram i forget the processor but it's a new blade).
This is a staging version of the app so no-one else was hitting it.

I apologize but the following link will probably load quite slowly due
to the many large uncompressed screenshots.

http://www.masukomi.org/misc/grails/index.html


--
- kate = masukomi
http://weblog.masukomi.org/

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local
In reply to this post by Maurice Nicholson old

On 4 Apr 2007, at 22:02, Maurice Nicholson wrote:

>
> Yeah, it _could_ be a real Groovy performance gotcha and one to  
> watch out for
> in future.
>
> I can see how it's useful to prevent an exception, but in this case  
> the
> "optionKey" and "optionValue" property names are provided by the  
> user, so if
> those proterties don't exist it's a programmer error and they  
> should know
> about it sooner than later.

Actually that is a potential issue but we need to look at the wider  
implications. I think your fix will die for collections of mixed  
objects where there may or may not be a property with that name. A  
change was made here in 0.4 for a reason if I recall.

Anyway, I'm still VERY bored stripping down taglibs and perf testing  
to see what happens.

Basically > 90% of the time is related to a single countrySelect  
(from my i18N taglib) box and a datePicker. These are just complex  
tags that do a lot of cout writing in little bits, and looping.

I don't think closures are to blame, and I don't think it's GStrings.  
It's just... groovy - or problems in the "out" writer. I notice that  
this page is quite large in html form terms - over 15KB. I saw a  
hardcoded 8k buffer array in the GSP code earlier, so this could be a  
symptom of premature flushing, but I doubt it.

The common thread here is g:select and g:datePicker. My country tag  
just wraps up g:select. g:modelDate wraps up datePicker. If I change  
the inner loop of g:select to just: out << el instead of all the  
other conditional stuff, it comes up to 14reqs/s... a bit better than  
0.6 eh? Shame we lose all the functionality of the tag then!

Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I  
have not debugged yet whether it is a naïve implementation of HTML  
encoding or potential ExpandoMetaClass overheads.

Marc








---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
So this evening I ran a quick test using straight access to the tags,
just executing the same block of stuff on a single page hundreds of times.

I ran this with localeSelect, which calls select with a fairly large set
of options.   It's also a slower tag than dateSelect, from my measurements.

First I switched select to not call encodeAsHTML() upon the results of
the optionValue closure and the page performed around 30% faster.

I then switched select to call
org.springframework.web.util.HtmlUtils.htmlEscape() directly.

Bad news.  It ran just as fast.

It seems that Expando is Ex-Slow-Mo.

So Marc, I'd suggest replacing all encodeAsHtml() calls with direct
calls to the htmlEscape() for now.  But something should probably be
done about Expando's royal slowwwwwness.

- Daiji

Marc Palmer wrote:

>
> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote:
>
>>
>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch
>> out for
>> in future.
>>
>> I can see how it's useful to prevent an exception, but in this case the
>> "optionKey" and "optionValue" property names are provided by the
>> user, so if
>> those proterties don't exist it's a programmer error and they should
>> know
>> about it sooner than later.
>
> Actually that is a potential issue but we need to look at the wider
> implications. I think your fix will die for collections of mixed
> objects where there may or may not be a property with that name. A
> change was made here in 0.4 for a reason if I recall.
>
> Anyway, I'm still VERY bored stripping down taglibs and perf testing
> to see what happens.
>
> Basically > 90% of the time is related to a single countrySelect (from
> my i18N taglib) box and a datePicker. These are just complex tags that
> do a lot of cout writing in little bits, and looping.
>
> I don't think closures are to blame, and I don't think it's GStrings.
> It's just... groovy - or problems in the "out" writer. I notice that
> this page is quite large in html form terms - over 15KB. I saw a
> hardcoded 8k buffer array in the GSP code earlier, so this could be a
> symptom of premature flushing, but I doubt it.
>
> The common thread here is g:select and g:datePicker. My country tag
> just wraps up g:select. g:modelDate wraps up datePicker. If I change
> the inner loop of g:select to just: out << el instead of all the other
> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6
> eh? Shame we lose all the functionality of the tag then!
>
> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I
> have not debugged yet whether it is a naïve implementation of HTML
> encoding or potential ExpandoMetaClass overheads.
>
> Marc
>
>
>
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>
>



 
____________________________________________________________________________________
No need to miss a message. Get email on-the-go
with Yahoo! Mail for Mobile. Get started.
http://mobile.yahoo.com/mail 

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
Actually, on second thought, what I'd like to ascertain is whether it's
ExpandoMetaClass or the Codecs plugin that's the root cause.  I don't
know quite enough about that stuff to make a test of that very rapidly;
maybe someone else who does can do it?

Having said that, I took a quick peek at
org.codehaus.groovy.grails.plugins.CodecsGrailsPlugin.groovy anyway and
I have a few questions about how these codecs were implemented:

1) Why are these dynamic methods (encodeAs/decodeAs) written in Groovy
(instead of Java)?
2) Why does the dynamic method check whether encodeMethod is defined at
runtime (rather than when the plugin defines the dynamic method)- every
single time that it's called?
3) Why is HTMLCodec written in Groovy (even if it is just a wrapper)?

These questions apply to 0.4.x as well as the 0.5 codebase.

Now it might be true that this plugin doesn't actually contribute to the
slowdown witnessed by .encodeAsHTML().  So I don't want to jump to
conclusions about that; and a proper analysis of the performance of
Expando and the Codecs plugin is probably appropriate.

- Daiji

D T wrote:

> So this evening I ran a quick test using straight access to the tags,
> just executing the same block of stuff on a single page hundreds of times.
>
> I ran this with localeSelect, which calls select with a fairly large set
> of options.   It's also a slower tag than dateSelect, from my measurements.
>
> First I switched select to not call encodeAsHTML() upon the results of
> the optionValue closure and the page performed around 30% faster.
>
> I then switched select to call
> org.springframework.web.util.HtmlUtils.htmlEscape() directly.
>
> Bad news.  It ran just as fast.
>
> It seems that Expando is Ex-Slow-Mo.
>
> So Marc, I'd suggest replacing all encodeAsHtml() calls with direct
> calls to the htmlEscape() for now.  But something should probably be
> done about Expando's royal slowwwwwness.
>
> - Daiji
>
> Marc Palmer wrote:
>  
>> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote:
>>
>>    
>>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch
>>> out for
>>> in future.
>>>
>>> I can see how it's useful to prevent an exception, but in this case the
>>> "optionKey" and "optionValue" property names are provided by the
>>> user, so if
>>> those proterties don't exist it's a programmer error and they should
>>> know
>>> about it sooner than later.
>>>      
>> Actually that is a potential issue but we need to look at the wider
>> implications. I think your fix will die for collections of mixed
>> objects where there may or may not be a property with that name. A
>> change was made here in 0.4 for a reason if I recall.
>>
>> Anyway, I'm still VERY bored stripping down taglibs and perf testing
>> to see what happens.
>>
>> Basically > 90% of the time is related to a single countrySelect (from
>> my i18N taglib) box and a datePicker. These are just complex tags that
>> do a lot of cout writing in little bits, and looping.
>>
>> I don't think closures are to blame, and I don't think it's GStrings.
>> It's just... groovy - or problems in the "out" writer. I notice that
>> this page is quite large in html form terms - over 15KB. I saw a
>> hardcoded 8k buffer array in the GSP code earlier, so this could be a
>> symptom of premature flushing, but I doubt it.
>>
>> The common thread here is g:select and g:datePicker. My country tag
>> just wraps up g:select. g:modelDate wraps up datePicker. If I change
>> the inner loop of g:select to just: out << el instead of all the other
>> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6
>> eh? Shame we lose all the functionality of the tag then!
>>
>> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I
>> have not debugged yet whether it is a naïve implementation of HTML
>> encoding or potential ExpandoMetaClass overheads.
>>
>> Marc
>>
>>
>>
>>
>>
>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe from this list please visit:
>>
>>    http://xircles.codehaus.org/manage_email
>>
>>
>>
>>
>>    
>
>
>
>  
> ____________________________________________________________________________________
> No need to miss a message. Get email on-the-go
> with Yahoo! Mail for Mobile. Get started.
> http://mobile.yahoo.com/mail 
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>
>
>
>  



 
____________________________________________________________________________________
Bored stiff? Loosen up...
Download and play hundreds of games for free on Yahoo! Games.
http://games.yahoo.com/games/front

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
Ok, another efficiency point I just found with regard to these selects.

   out.println or out << with mixed control logic is slower than
out.println of a GString.

I was able to speed up dateSelect a bit (20-30%) by changing every block
of the style:

    out << "<option value=\"${m}\" "
    if(minute == i) out << "selected=\"selected\""
    out << '>' << m << '</option>'
    out.println()

to:

    out.println "<option value=\"${m}\" ${(i == minute) ? 'selected' :
''}>${m}</option}"

which is uglier... but it makes sense because then you're having fewer
Groovy method invocations.

There's code like this all over FormTagLib.

This isn't the multiple-times improvement you saw by eliminating select
altogether, but it does speed up things by 30%.  Calling taglibs
indirectly also has overhead... but as far as I can tell, it's not as
bad as the issues of out and encodeAsHtml() I found here.  Anyway, I
hope my findings here are useful for you (and others).

Best,

- Daiji

D T wrote:

> Actually, on second thought, what I'd like to ascertain is whether it's
> ExpandoMetaClass or the Codecs plugin that's the root cause.  I don't
> know quite enough about that stuff to make a test of that very rapidly;
> maybe someone else who does can do it?
>
> Having said that, I took a quick peek at
> org.codehaus.groovy.grails.plugins.CodecsGrailsPlugin.groovy anyway and
> I have a few questions about how these codecs were implemented:
>
> 1) Why are these dynamic methods (encodeAs/decodeAs) written in Groovy
> (instead of Java)?
> 2) Why does the dynamic method check whether encodeMethod is defined at
> runtime (rather than when the plugin defines the dynamic method)- every
> single time that it's called?
> 3) Why is HTMLCodec written in Groovy (even if it is just a wrapper)?
>
> These questions apply to 0.4.x as well as the 0.5 codebase.
>
> Now it might be true that this plugin doesn't actually contribute to the
> slowdown witnessed by .encodeAsHTML().  So I don't want to jump to
> conclusions about that; and a proper analysis of the performance of
> Expando and the Codecs plugin is probably appropriate.
>
> - Daiji
>
> D T wrote:
>  
>> So this evening I ran a quick test using straight access to the tags,
>> just executing the same block of stuff on a single page hundreds of times.
>>
>> I ran this with localeSelect, which calls select with a fairly large set
>> of options.   It's also a slower tag than dateSelect, from my measurements.
>>
>> First I switched select to not call encodeAsHTML() upon the results of
>> the optionValue closure and the page performed around 30% faster.
>>
>> I then switched select to call
>> org.springframework.web.util.HtmlUtils.htmlEscape() directly.
>>
>> Bad news.  It ran just as fast.
>>
>> It seems that Expando is Ex-Slow-Mo.
>>
>> So Marc, I'd suggest replacing all encodeAsHtml() calls with direct
>> calls to the htmlEscape() for now.  But something should probably be
>> done about Expando's royal slowwwwwness.
>>
>> - Daiji
>>
>> Marc Palmer wrote:
>>  
>>    
>>> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote:
>>>
>>>    
>>>      
>>>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch
>>>> out for
>>>> in future.
>>>>
>>>> I can see how it's useful to prevent an exception, but in this case the
>>>> "optionKey" and "optionValue" property names are provided by the
>>>> user, so if
>>>> those proterties don't exist it's a programmer error and they should
>>>> know
>>>> about it sooner than later.
>>>>      
>>>>        
>>> Actually that is a potential issue but we need to look at the wider
>>> implications. I think your fix will die for collections of mixed
>>> objects where there may or may not be a property with that name. A
>>> change was made here in 0.4 for a reason if I recall.
>>>
>>> Anyway, I'm still VERY bored stripping down taglibs and perf testing
>>> to see what happens.
>>>
>>> Basically > 90% of the time is related to a single countrySelect (from
>>> my i18N taglib) box and a datePicker. These are just complex tags that
>>> do a lot of cout writing in little bits, and looping.
>>>
>>> I don't think closures are to blame, and I don't think it's GStrings.
>>> It's just... groovy - or problems in the "out" writer. I notice that
>>> this page is quite large in html form terms - over 15KB. I saw a
>>> hardcoded 8k buffer array in the GSP code earlier, so this could be a
>>> symptom of premature flushing, but I doubt it.
>>>
>>> The common thread here is g:select and g:datePicker. My country tag
>>> just wraps up g:select. g:modelDate wraps up datePicker. If I change
>>> the inner loop of g:select to just: out << el instead of all the other
>>> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6
>>> eh? Shame we lose all the functionality of the tag then!
>>>
>>> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I
>>> have not debugged yet whether it is a naïve implementation of HTML
>>> encoding or potential ExpandoMetaClass overheads.
>>>
>>> Marc
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe from this list please visit:
>>>
>>>    http://xircles.codehaus.org/manage_email
>>>
>>>
>>>
>>>
>>>    
>>>      
>>
>>  
>> ____________________________________________________________________________________
>> No need to miss a message. Get email on-the-go
>> with Yahoo! Mail for Mobile. Get started.
>> http://mobile.yahoo.com/mail 
>>
>> ---------------------------------------------------------------------
>> To unsubscribe from this list please visit:
>>
>>     http://xircles.codehaus.org/manage_email
>>
>>
>>
>>
>>  
>>    
>
>
>
>  
> ____________________________________________________________________________________
> Bored stiff? Loosen up...
> Download and play hundreds of games for free on Yahoo! Games.
> http://games.yahoo.com/games/front
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>     http://xircles.codehaus.org/manage_email
>
>
>
>
>  



 
____________________________________________________________________________________
Finding fabulous fares is fun.  
Let Yahoo! FareChase search your favorite travel sites to find flight and hotel bargains.
http://farechase.yahoo.com/promo-generic-14795097

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Maurice Nicholson old
In reply to this post by Marc Palmer Local


Marc Palmer wrote:

>
> On 4 Apr 2007, at 22:02, Maurice Nicholson wrote:
>
>>
>> Yeah, it _could_ be a real Groovy performance gotcha and one to watch
>> out for
>> in future.
>>
>> I can see how it's useful to prevent an exception, but in this case the
>> "optionKey" and "optionValue" property names are provided by the
>> user, so if
>> those proterties don't exist it's a programmer error and they should
>> know
>> about it sooner than later.
>
> Actually that is a potential issue but we need to look at the wider
> implications. I think your fix will die for collections of mixed
> objects where there may or may not be a property with that name. [snip]
>

Which the programmer should know about and is why I'm advocating the
potential-exception-throwing property access here. Otherwise the output
could be "undefined", as they like to say in C textbooks.


> Anyway, I'm still VERY bored stripping down taglibs and perf testing
> to see what happens.
>
> Basically > 90% of the time is related to a single countrySelect (from
> my i18N taglib) box and a datePicker. These are just complex tags that
> do a lot of cout writing in little bits, and looping.
>
> I don't think closures are to blame, and I don't think it's GStrings.
> It's just... groovy - or problems in the "out" writer. I notice that
> this page is quite large in html form terms - over 15KB. I saw a
> hardcoded 8k buffer array in the GSP code earlier, so this could be a
> symptom of premature flushing, but I doubt it.
>
> The common thread here is g:select and g:datePicker. My country tag
> just wraps up g:select. g:modelDate wraps up datePicker. If I change
> the inner loop of g:select to just: out << el instead of all the other
> conditional stuff, it comes up to 14reqs/s... a bit better than 0.6
> eh? Shame we lose all the functionality of the tag then!
>

Have you tried my fix to see if that helps?

In my case I had domain objects with several derived property getters -
not normal instance attributes - which were computationally expensive.
Of course I followed the getXxx naming convention for Java/Groovy bean
convenience elsewhere, but I may rename some of these now so they aren't
assumed to be "properties".

> Also encodeAsHTML() is slowing it - obviously - but by quite a bit. I
> have not debugged yet whether it is a naïve implementation of HTML
> encoding or potential ExpandoMetaClass overheads.
>
> Marc
>
>
>
>
>
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

Marc Palmer Local

On 5 Apr 2007, at 10:47, Maurice Nicholson wrote:

>> Anyway, I'm still VERY bored stripping down taglibs and perf  
>> testing to see what happens.
>>
>> Basically > 90% of the time is related to a single countrySelect  
>> (from my i18N taglib) box and a datePicker. These are just complex  
>> tags that do a lot of cout writing in little bits, and looping.
>>
>> I don't think closures are to blame, and I don't think it's  
>> GStrings. It's just... groovy - or problems in the "out" writer. I  
>> notice that this page is quite large in html form terms - over  
>> 15KB. I saw a hardcoded 8k buffer array in the GSP code earlier,  
>> so this could be a symptom of premature flushing, but I doubt it.
>>
>> The common thread here is g:select and g:datePicker. My country  
>> tag just wraps up g:select. g:modelDate wraps up datePicker. If I  
>> change the inner loop of g:select to just: out << el instead of  
>> all the other conditional stuff, it comes up to 14reqs/s... a bit  
>> better than 0.6 eh? Shame we lose all the functionality of the tag  
>> then!
>>
>
> Have you tried my fix to see if that helps?
>

It doesn't. In my usage the el.properties[x] is never called as I am  
passing in an optionValue closure.

> In my case I had domain objects with several derived property  
> getters - not normal instance attributes - which were  
> computationally expensive. Of course I followed the getXxx naming  
> convention for Java/Groovy bean convenience elsewhere, but I may  
> rename some of these now so they aren't assumed to be "properties".

Yes, but please raise an issue for this - we need to sort out the  
SetPropertiesDynamicMethod to not be dailyWTF fodder.

Marc


---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

D T-2
In reply to this post by Marc Palmer Local
Marc,

Lemme know if the changes I suggested help.

Here's my patched-up version of FormTagLib to eliminate part of the
issues I mentioned.  If you rename FormTagLib.groovy in the core plugin
and stick this somewhere you'll get the revised tags.

- Daiji

Marc Palmer wrote:

>
> On 5 Apr 2007, at 10:47, Maurice Nicholson wrote:
>>> Anyway, I'm still VERY bored stripping down taglibs and perf testing
>>> to see what happens.
>>>
>>> Basically > 90% of the time is related to a single countrySelect
>>> (from my i18N taglib) box and a datePicker. These are just complex
>>> tags that do a lot of cout writing in little bits, and looping.
>>>
>>> I don't think closures are to blame, and I don't think it's
>>> GStrings. It's just... groovy - or problems in the "out" writer. I
>>> notice that this page is quite large in html form terms - over 15KB.
>>> I saw a hardcoded 8k buffer array in the GSP code earlier, so this
>>> could be a symptom of premature flushing, but I doubt it.
>>>
>>> The common thread here is g:select and g:datePicker. My country tag
>>> just wraps up g:select. g:modelDate wraps up datePicker. If I change
>>> the inner loop of g:select to just: out << el instead of all the
>>> other conditional stuff, it comes up to 14reqs/s... a bit better
>>> than 0.6 eh? Shame we lose all the functionality of the tag then!
>>>
>>
>> Have you tried my fix to see if that helps?
>>
>
> It doesn't. In my usage the el.properties[x] is never called as I am
> passing in an optionValue closure.
>
>> In my case I had domain objects with several derived property getters
>> - not normal instance attributes - which were computationally
>> expensive. Of course I followed the getXxx naming convention for
>> Java/Groovy bean convenience elsewhere, but I may rename some of
>> these now so they aren't assumed to be "properties".
>
> Yes, but please raise an issue for this - we need to sort out the
> SetPropertiesDynamicMethod to not be dailyWTF fodder.
>
> Marc
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>
>



 
____________________________________________________________________________________
Need Mail bonding?
Go to the Yahoo! Mail Q&A for great tips from Yahoo! Answers users.
http://answers.yahoo.com/dir/?link=list&sid=396546091
/* Copyright 2004-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import org.springframework.validation.Errors;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU;
import org.springframework.web.util.HtmlUtils

 /**
 *  A  tag lib that provides tags for working with form controls
 *
 * @author Graeme Rocher
 * @since 17-Jan-2006
 */

class PatchedFormTagLib {
        def out // to facilitate testing

        /**
         * Creates a new text field
         */
        def textField = { attrs ->
                attrs.type = "text"  
                attrs.tagName = "textField"
                field(attrs)
        }
        /**
         * Creates a hidden field
         */
        def hiddenField = { attrs ->
                attrs.type = "hidden"
                attrs.tagName = "hiddenField"
                field(attrs)
        }
        /**
         * Creates a submit button
         */
        def submitButton = { attrs ->
                attrs.type = "submit"
                attrs.tagName = "submitButton"
                field(attrs)
        }
        /**
         * A general tag for creating fields
         */
        def field = { attrs ->  
        resolveAttributes( attrs)

                out << "<input type=\"${attrs.remove('type')}\" "
        outputAttributes(attrs)
                out << "/>"
        }

        /**
         * A general tag for creating textareas
         */
        def textArea = { attrs ->
            resolveAttributes(attrs)

        // Pull out the value to use as content not attrib
        def value = attrs.remove( 'value')
        def escapeHtml = true
                if(attrs.escapeHtml) escapeHtml = Boolean.valueOf(attrs.remove('escapeHtml'))

                out << "<textarea "
        outputAttributes(attrs)
                out << ">" << (escapeHtml ? HtmlUtils.htmlEscape(value) : value) << "</textarea>"
        }

    /**
     * Check required attributes, set the id to name if no id supplied, extract bean values etc.
     */
    void resolveAttributes(attrs)
    {
        if(!attrs.name && !attrs.field) {
            throwTagError("Tag [$tagName] is missing required attribute [name] or [field]")
        }
        attrs.remove('tagName')

        attrs.id = (!attrs.id ? attrs.name : attrs.id)

        def val = attrs.remove('bean')
        if(val) {
            if(attrs.name.indexOf('.'))
                attrs.name.split('\\.').each { val = val?."$it" }
            else {
                val = val[name]
            }
            attrs.value = val
        }
        attrs.value = (attrs.value ? attrs.value : "")
    }

    /**
     * Dump out attributes in HTML compliant fashion
     */
    void outputAttributes(attrs)
    {
        attrs.each { k,v ->
            out << k << "=\"" << HtmlUtils.htmlEscape(v) << "\" "
        }
    }

    /**
     *  General linking to controllers, actions etc. Examples:
     *
     *  <g:form action="myaction">...</gr:form>
     *  <g:form controller="myctrl" action="myaction">...</gr:form>
     */
    def form = { attrs, body ->
        out << "<form action=\""
        // create the link
        createLink(attrs)

        out << '\" '
        // default to post
        if(!attrs['method']) {
            out << 'method="post" '
        }
        // process remaining attributes
        outputAttributes(attrs)

        out << ">"
        // output the body
        body()

        // close tag
        out << "</form>"
    }
    /**
     * Creates a submit button that submits to an action in the controller specified by the form action
     * The value of the action attribute is translated into the action name, for example "Edit" becomes
     * "edit" or "List People" becomes "listPeople"
     *
     *  <g:actionSubmit value="Edit" />
     *
     */
    def actionSubmit = { attrs ->
        out << '<input type="submit" name="_action" '
        def value = attrs.remove('value')
        if(value) {
             out << "value=\"${value}\" "
        }
        // process remaining attributes
        outputAttributes(attrs)

        // close tag
        out << '/>'

    }
    /**
     * Creates a an image submit button that submits to an action in the controller specified by the form action
     * The value of the action attribute is translated into the action name, for example "Edit" becomes
     * "edit" or "List People" becomes "listPeople"
     *
     *  <g:actionSubmitImage src="/images/submitButton.gif" action="Edit" />
     *
     */
    def actionSubmitImage = { attrs ->
        out << '<input type="image" name="_action" '
        def value = attrs.remove('value')
        if(value) {
             out << "value=\"${value}\" "
        }
        def src = attrs.remove('src')
        if(src) {
             out << "src=\"${src}\" "
        }
        // process remaining attributes
        outputAttributes(attrs)

        // close tag
        out << '/>'

    }

   /**
     * A simple date picker that renders a date as selects
     * eg. <g:datePicker name="myDate" value="${new Date()}" />
     */
    def datePicker = { attrs ->
        def xdefault = attrs['default']
                if (xdefault == null) {
                        xdefault = new Date()
                } else if (xdefault != 'none') {
                        xdefault = DateFormat.getInstance().parse(xdefault)
                } else {
                        xdefault = null
                }

        def value = (attrs['value'] ? attrs['value'] : xdefault)
        def name = attrs['name']
                def noSelection = attrs['noSelection']
                if (noSelection != null)
                {
                    noSelection = noSelection.entrySet().iterator().next()
                }

                def years = attrs['years']

//        final PRECISION_RANKINGS = ["year":0, "month":10, "day":20, "hour":30, "minute":40]
        final PRECISION_RANKINGS = [year:0, month:10, day:20, hour:30, minute:40]
        def precision = (attrs['precision'] ? PRECISION_RANKINGS[attrs['precision']] : PRECISION_RANKINGS["minute"])

        def day = 0
        def month = 0
        def year = 0
        def hour = 0
        def minute = 0
        def dfs = new java.text.DateFormatSymbols(RCU.getLocale(request))

        def c = null
        if(value instanceof Calendar) {
            c = value
        }
        else if (value != null) {
            c = new GregorianCalendar();
            c.setTime(value)
        }

                if (c != null) {
                day = c.get(GregorianCalendar.DAY_OF_MONTH)
                month = c.get(GregorianCalendar.MONTH)
                year = c.get(GregorianCalendar.YEAR)
                hour = c.get(GregorianCalendar.HOUR_OF_DAY)
                minute = c.get(GregorianCalendar.MINUTE)
                }

                if (years == null) {
                        def tempyear
                        if (year == null) {
                                // If no year, we need to get current year to setup a default range... ugly
            def tempc = new GregorianCalendar()
            tempc.setTime(new Date())
                tempyear = tempc.get(GregorianCalendar.YEAR)
                        } else {
                                tempyear = year
                        }
                        years = (tempyear-100)..(tempyear+100)
                }

        out << "<input type=\"hidden\" name=\"${name}\" value=\"struct\" />"

        // create day select
        if (precision >= PRECISION_RANKINGS["day"]) {
//        if (precision >= 20) {
            out.println "<select name=\"${name}_day\">"

            if (noSelection) {
            renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in 1..31) {
                out.println "<option value=\"${i}\" ${(i == day) ? 'selected' : ''}>${i}</option}"
/*
                out.println "<option value=\"${i}\""
                                if (i == day) {
                                        out.println " selected=\"selected\""
                                }
                                out.println ">${i}</option>"
                out << "<option value=\"${i}\""
                                if (i == day) {
                                        out << " selected=\"selected\""
                                }
                                out << ">${i}</option>"
                */
            }
            out.println '</select>'
        }

        // create month select
        if (precision >= PRECISION_RANKINGS["month"]) {
//        if (precision >= 10) {
            out.println "<select name=\"${name}_month\">"

            if (noSelection) {
            renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            dfs.months.eachWithIndex { m,i ->
                if(m) {
                    out.println "<option value=\"${i + 1}\" ${(i == month) ? 'selected' : ''}>${m}</option}"
                    /*
                    def monthIndex = i + 1
                    out << "<option value=\"${monthIndex}\""
                    if(month == i) out << " selected=\"selected\""
                    out << '>'
                    out << m
                    out.println '</option>'
                    */
                }
            }
            out.println '</select>'
        }

        // create year select
        if (precision >= PRECISION_RANKINGS["year"]) {
//        if (precision >= 0) {
            out.println "<select name=\"${name}_year\">"

            if (noSelection) {
    renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in years) {
//            for(i in 1907..2107) {
                out.println "<option value=\"${i}\" ${(i == year) ? 'selected' : ''}>${i}</option>"
/*
                out.println "<option value=\"${i}\""
                                if (i == year) {
                                        out.println " selected=\"selected\""
                                }
                                out.println ">${i}</option>"
                                out << "<option value=\"${i}\""
                                if (i == year) {
                                        out << " selected=\"selected\""
                                }
                                out << ">${i}</option>"
*/

            }
            out.println '</select>'
        }

        // do hour select
        if (precision >= PRECISION_RANKINGS["hour"]) {
            out.println "<select name=\"${name}_hour\">"

            if (noSelection) {
            renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in 0..23) {
//                def h = '' + i
//                if(i < 10) h = '0' + h
                def h
                if(i < 10) {
                    h = '0' + i
                } else {
                    h = i
                }
                out.println "<option value=\"${h}\" ${(i == hour) ? 'selected' : ''}>${h}</option}"
                /*
                out << "<option value=\"${h}\" "
                if(hour == i) out << "selected=\"selected\""
                out << '>' << h << '</option>'
                out.println()
                */
            }
            out.println '</select> :'

            // If we're rendering the hour, but not the minutes, then display the minutes as 00 in read-only format
            if (precision < PRECISION_RANKINGS["minute"]) {
//            if (precision < 40) {
                out.println '00'
            }
        }

        // do minute select
        if (precision >= PRECISION_RANKINGS["minute"]) {
//        if (precision >= 40) {
            out.println "<select name=\"${name}_minute\">"

            if (noSelection) {
            renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in 0..59) {
                def m
                if(i < 10) {
                    m = '0' + i
                } else {
                    m = i
                }
                out.println "<option value=\"${m}\" ${(i == minute) ? 'selected' : ''}>${m}</option}"
                /*
                out << "<option value=\"${m}\" "
                if(minute == i) out << "selected=\"selected\""
                out << '>' << m << '</option>'
                out.println()
                */
            }
            out.println '</select>'
        }
    }

        def renderNoSelectionOption = { noSelectionKey, noSelectionValue, value ->
                // If a label for the '--Please choose--' first item is supplied, write it out
        out.println "<option value=\"${(noSelectionKey == null ? "" : noSelectionKey)}\" ${(noSelectionKey == value) ? 'selected' : ''}>${HtmlUtils.htmlEscape(noSelectionValue)}</option}"
        /*
        out << '<option value="' << (noSelectionKey == null ? "" : noSelectionKey) << '"'
        if(noSelectionKey == value) {
            out << ' selected="selected" '
        }
        out << '>' << HtmlUtils.htmlEscape(noSelectionValue) << '</option>'
        */
        }

    /**
     *  A helper tag for creating TimeZone selects
     * eg. <g:timeZoneSelect name="myTimeZone" value="${tz}" />
     */
    def timeZoneSelect = { attrs ->
        attrs['from'] = TimeZone.getAvailableIDs();
        attrs['value'] = (attrs['value'] ? attrs['value'].ID : TimeZone.getDefault().ID )

        // set the option value as a closure that formats the TimeZone for display
        attrs['optionValue'] = {
            TimeZone tz = TimeZone.getTimeZone(it);
            def shortName = tz.getDisplayName(tz.inDaylightTime(date),TimeZone.SHORT);
            def longName = tz.getDisplayName(tz.inDaylightTime(date),TimeZone.LONG);

            def offset = tz.rawOffset;
            def hour = offset / (60*60*1000);
            def min = Math.abs(offset / (60*1000)) % 60;

            return "${shortName}, ${longName} ${hour}:${min}"
        }

        // use generic select
        select( attrs )
    }

    /**
     *  A helper tag for creating locale selects
     *
     * eg. <g:localeSelect name="myLocale" value="${locale}" />
     */
    def localeSelect = {attrs ->
        attrs['from'] = Locale.getAvailableLocales()
        attrs['value'] = (attrs['value'] ? attrs['value'] : RCU.getLocale(request) )
        // set the key as a closure that formats the locale
        attrs['optionKey'] = { "${it.language}_${it.country}" }
        // set the option value as a closure that formats the locale for display
        attrs['optionValue'] = { "${it.language}, ${it.country},  ${it.displayName}" }

        // use generic select
        select( attrs )
    }

    /**
     * A helper tag for creating currency selects
     *
     * eg. <g:currencySelect name="myCurrency" value="${currency}" />
     */
    def currencySelect = { attrs, body ->
        if(!attrs['from']) {
            attrs['from'] = ['EUR', 'XCD','USD','XOF','NOK','AUD','XAF','NZD','MAD','DKK','GBP','CHF','XPF','ILS','ROL','TRL']
        }
                try {
                def currency = (attrs['value'] ? attrs['value'] : Currency.getInstance( RCU.getLocale(request) ))
                attrs.value = currency.currencyCode
                }
                catch(IllegalArgumentException iae) {
                    attrs.value = null
                }
        // invoke generic select
        select( attrs )
    }

    /**
     * A helper tag for creating HTML selects
     *
     * Examples:
     * <g:select name="user.age" from="${18..65}" value="${age}" />
     * <g:select name="user.company.id" from="${Company.list()}" value="${user?.company.id}" optionKey="id" />
     */
    def select = { attrs ->
        def from = attrs.remove('from')
        def keys = attrs.remove('keys')
        def optionKey = attrs.remove('optionKey')
        def optionValue = attrs.remove('optionValue')
        def value = attrs.remove('value')
                def noSelection = attrs.remove('noSelection')
        if (noSelection != null) {
            noSelection = noSelection.entrySet().iterator().next()
        }

        out << "<select name=\"${attrs.remove('name')}\" "
        // process remaining attributes
        outputAttributes(attrs)

        out << '>'
        out.println()

        if (noSelection) {
                    renderNoSelectionOption(noSelection.key, noSelection.value, value)
            out.println()
        }

        // create options from list
        if(from) {
            from.eachWithIndex { el,i ->
                out << '<option '
                if(keys) {
                    out << 'value="' << keys[i] << '" '
                    if(keys[i] == value) {
                        out << 'selected="selected" '
                    }
                }
               else if(optionKey) {
                    def keyValue = null
                    if(optionKey instanceof Closure) {
                        keyValue = optionKey(el)
                         out << 'value="' << keyValue << '" '
                    }
                    else if(el !=null && optionKey == 'id' && grailsApplication.getGrailsDomainClass(el.getClass().name)) {
                        keyValue = el.ident()
                        out << 'value="' << keyValue << '" '
                    }
                    else {
                        keyValue = el.properties[optionKey]
                        out << 'value="' << keyValue << '" '
                    }

                    if(keyValue == value) {
                        out << 'selected="selected" '
                    }
                }
                else {
                    out << "value=\"${el}\" "
                    if(el == value) {
                        out << 'selected="selected" '
                    }
                }
                out << '>'
                if(optionValue) {
                    if(optionValue instanceof Closure) {
                        out << HtmlUtils.htmlEscape(optionValue(el).toString())
                    }
                    else {
                        out << HtmlUtils.htmlEscape(el.properties[optionValue].toString())
                    }
                }
                else {
                    def s = el.toString()
                    if(s) out << HtmlUtils.htmlEscape(s)
                }
                out << '</option>'
                out.println()
            }
        }
        // close tag
        out << '</select>'
    }

    /**
     * A helper tag for creating checkboxes
     **/
    def checkBox = { attrs ->
          def value = attrs.remove('value')
          def name = attrs.remove('name')
          if(!value) value = false
          out << '<input type="hidden" '
          out << "name=\"_${name}\" />"
          out << '<input type="checkbox" '
          out << "name=\"${name}\" "
          if(value) {
                out << 'checked="checked" '
          }
          out << "value=\"true\" "
        // process remaining attributes
        outputAttributes(attrs)

        // close the tag, with no body
        out << ' />'

    }

    /**
     * A helper tag for creating radio buttons
     */
     def radio = { attrs ->
          def value = attrs.remove('value')
          def name = attrs.remove('name')
          def checked = (attrs.remove('checked') ? true : false)
          out << '<input type="radio" '
          out << "name=\"${name}\" "
          if(checked) {
                out << 'checked="checked" '
          }
          out << "value=\"${HtmlUtils.htmlEscape(value.toString())}\" "
        // process remaining attributes
        outputAttributes(attrs)

        // close the tag, with no body
        out << ' ></input>'
     }
}

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: 0.4 performance trouble

JamesPage
> It feels to me like there may be a relatively simple "production
> only" bug happening

Can you confirm if this only happening in Tomcat and not in Jetty? Are you testing production in Tomcat.

DT optimizations are writing to 'out' less times. I am wondering if the Writer is blocking.  The reason I feel this is you say that you are getting the performance hit at different times, and different places. 2 seconds is a very long time compared .6 seconds even if the code is not optimized.

The quick thing to check also is that you are running java with the server option -- just to check that it is not garbage collection, getting in the way of your tests. Or even setting the gc collection off for the test.

Could you send me the headers that your page is generating, or tell me if the server is returning the CONTENT-LENGTH and the CHUNKED header..

The reason for looking at these headers is to see if the Server is Writing out at the same time that you are Writing to the writer. In the past I have seen many issues with Threads and Output streams.

If it is the writer blocking there is code I can look at that I wrote in the past that should get around this problem.

I was going to write this later for our site, but if this is causing issues now. I will write an implementation  now, and post it to the mailing list. 

James

On 4/5/07, D T <[hidden email]> wrote:
Marc,

Lemme know if the changes I suggested help.

Here's my patched-up version of FormTagLib to eliminate part of the
issues I mentioned.  If you rename FormTagLib.groovy in the core plugin
and stick this somewhere you'll get the revised tags.

- Daiji

Marc Palmer wrote:

>
> On 5 Apr 2007, at 10:47, Maurice Nicholson wrote:
>>> Anyway, I'm still VERY bored stripping down taglibs and perf testing
>>> to see what happens.
>>>
>>> Basically > 90% of the time is related to a single countrySelect
>>> (from my i18N taglib) box and a datePicker. These are just complex
>>> tags that do a lot of cout writing in little bits, and looping.
>>>
>>> I don't think closures are to blame, and I don't think it's
>>> GStrings. It's just... groovy - or problems in the "out" writer. I
>>> notice that this page is quite large in html form terms - over 15KB.
>>> I saw a hardcoded 8k buffer array in the GSP code earlier, so this
>>> could be a symptom of premature flushing, but I doubt it.
>>>
>>> The common thread here is g:select and g:datePicker. My country tag
>>> just wraps up g:select. g:modelDate wraps up datePicker. If I change
>>> the inner loop of g:select to just: out << el instead of all the
>>> other conditional stuff, it comes up to 14reqs/s... a bit better
>>> than 0.6 eh? Shame we lose all the functionality of the tag then!
>>>
>>
>> Have you tried my fix to see if that helps?
>>
>
> It doesn't. In my usage the el.properties[x] is never called as I am
> passing in an optionValue closure.
>
>> In my case I had domain objects with several derived property getters
>> - not normal instance attributes - which were computationally
>> expensive. Of course I followed the getXxx naming convention for
>> Java/Groovy bean convenience elsewhere, but I may rename some of
>> these now so they aren't assumed to be "properties".
>
> Yes, but please raise an issue for this - we need to sort out the
> SetPropertiesDynamicMethod to not be dailyWTF fodder.
>
> Marc
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list please visit:
>
>    http://xircles.codehaus.org/manage_email
>
>
>
>





____________________________________________________________________________________
Need Mail bonding?
Go to the Yahoo! Mail Q&A for great tips from Yahoo! Answers users.
http://answers.yahoo.com/dir/?link=list&sid=396546091
/* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.springframework.validation.Errors;
import org.springframework.context.NoSuchMessageException;
import org.springframework.web.servlet.support.RequestContextUtils as RCU;
import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU;
import org.springframework.web.util.HtmlUtils

/**
*  A  tag lib that provides tags for working with form controls
*
* @author Graeme Rocher
* @since 17-Jan-2006
*/

class PatchedFormTagLib {
        def out // to facilitate testing

        /**
         * Creates a new text field
         */
        def textField = { attrs ->
                attrs.type = "text"
                attrs.tagName = "textField"
                field(attrs)
        }
        /**
         * Creates a hidden field
         */
        def hiddenField = { attrs ->
                attrs.type = "hidden"
                attrs.tagName = "hiddenField"
                field(attrs)
        }
        /**
         * Creates a submit button
         */
        def submitButton = { attrs ->
                attrs.type = "submit"
                attrs.tagName = "submitButton"
                field(attrs)
        }
        /**
         * A general tag for creating fields
         */
        def field = { attrs ->
        resolveAttributes( attrs)

                out << "<input type=\"${attrs.remove('type')}\" "
        outputAttributes(attrs)
                out << "/>"
        }

        /**
         * A general tag for creating textareas
         */
        def textArea = { attrs ->
            resolveAttributes(attrs)

        // Pull out the value to use as content not attrib
        def value = attrs.remove( 'value')
        def escapeHtml = true
                if(attrs.escapeHtml ) escapeHtml = Boolean.valueOf(attrs.remove('escapeHtml'))

                out << "<textarea "
        outputAttributes(attrs)
                out << ">" << (escapeHtml ? HtmlUtils.htmlEscape(value) : value) << "</textarea>"
        }

    /**
     * Check required attributes, set the id to name if no id supplied, extract bean values etc.
     */
    void resolveAttributes(attrs)
    {
        if(!attrs.name && !attrs.field) {
            throwTagError("Tag [$tagName] is missing required attribute [name] or [field]")
        }
        attrs.remove('tagName')

        attrs.id = (!attrs.id ? attrs.name : attrs.id)

        def val = attrs.remove('bean')
        if(val) {
            if(attrs.name.indexOf('.'))
                attrs.name.split('\\.').each { val = val?."$it" }
            else {
                val = val[name]
            }
             attrs.value = val
        }
        attrs.value = (attrs.value ? attrs.value : "")
    }

    /**
     * Dump out attributes in HTML compliant fashion
     */
    void outputAttributes(attrs)
    {
        attrs.each { k,v ->
            out << k << "=\"" << HtmlUtils.htmlEscape(v) << "\" "
        }
    }

    /**
     *  General linking to controllers, actions etc. Examples:
     *
     *  <g:form action="myaction">...</gr:form>
     *  <g:form controller="myctrl" action="myaction">...</gr:form>
     */
    def form = { attrs, body ->
        out << "<form action=\""
        // create the link
        createLink(attrs)

        out << '\" '
        // default to post
        if(!attrs['method']) {
            out << 'method="post" '
        }
        // process remaining attributes
        outputAttributes(attrs)

        out << ">"
        // output the body
        body()

        // close tag
        out << "</form>"
    }
    /**
     * Creates a submit button that submits to an action in the controller specified by the form action
     * The value of the action attribute is translated into the action name, for example "Edit" becomes
     * "edit" or "List People" becomes "listPeople"
     *
     *  <g:actionSubmit value="Edit" />
     *
     */
    def actionSubmit = { attrs ->
        out << '<input type="submit" name="_action" '
        def value = attrs.remove('value')
        if(value) {
             out << "value=\"${value}\" "
        }
        // process remaining attributes
        outputAttributes(attrs)

        // close tag
        out << '/>'

    }
    /**
     * Creates a an image submit button that submits to an action in the controller specified by the form action
     * The value of the action attribute is translated into the action name, for example "Edit" becomes
     * "edit" or "List People" becomes "listPeople"
     *
     *  <g:actionSubmitImage src="/images/submitButton.gif" action="Edit" />
     *
     */
    def actionSubmitImage = { attrs ->
        out << '<input type="image" name="_action" '
        def value = attrs.remove('value')
        if(value) {
             out << "value=\"${value}\" "
        }
        def src = attrs.remove('src')
        if(src) {
             out << "src=\"${src}\" "
        }
        // process remaining attributes
        outputAttributes(attrs)

        // close tag
        out << '/>'

    }

   /**
     * A simple date picker that renders a date as selects
     * eg. <g:datePicker name="myDate" value="${new Date()}" />
     */
    def datePicker = { attrs ->
        def xdefault = attrs['default']
                if (xdefault == null) {
                        xdefault = new Date()
                } else if (xdefault != 'none') {
                        xdefault = DateFormat.getInstance().parse(xdefault)
                } else {
                        xdefault = null
                }

        def value = (attrs['value'] ? attrs['value'] : xdefault)
        def name = attrs['name']
                def noSelection = attrs['noSelection']
                if (noSelection != null)
                {
                    noSelection = noSelection.entrySet ().iterator().next()
                }

                def years = attrs['years']

//        final PRECISION_RANKINGS = ["year":0, "month":10, "day":20, "hour":30, "minute":40]
        final PRECISION_RANKINGS = [year:0, month:10, day:20, hour:30, minute:40]
        def precision = (attrs['precision'] ? PRECISION_RANKINGS[attrs['precision']] : PRECISION_RANKINGS["minute"])

        def day = 0
        def month = 0
        def year = 0
        def hour = 0
        def minute = 0
        def dfs = new java.text.DateFormatSymbols(RCU.getLocale(request))

        def c = null
        if(value instanceof Calendar) {
            c = value
        }
        else if (value != null) {
            c = new GregorianCalendar();
            c.setTime(value)
        }

                if (c != null) {
                day = c.get(GregorianCalendar.DAY_OF_MONTH)
                month = c.get(GregorianCalendar.MONTH)
                year = c.get(GregorianCalendar.YEAR)
                hour = c.get(GregorianCalendar.HOUR_OF_DAY )
                minute = c.get(GregorianCalendar.MINUTE)
                }

                if (years == null) {
                        def tempyear
                        if (year == null) {
                                // If no year, we need to get current year to setup a default range... ugly
                def tempc = new GregorianCalendar()
                tempc.setTime(new Date())
                        tempyear = tempc.get(GregorianCalendar.YEAR)
                        } else {
                                tempyear = year
                        }
                        years = (tempyear-100)..(tempyear+100)
                }

        out << "<input type=\"hidden\" name=\"${name}\" value=\"struct\" />"

        // create day select
        if (precision >= PRECISION_RANKINGS["day"]) {
//        if (precision >= 20) {
            out.println "<select name=\"${name}_day\">"

            if (noSelection) {
                        renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in 1..31) {
                out.println "<option value=\"${i}\" ${(i == day) ? 'selected' : ''}>${i}</option}"
/*
                out.println "<option value=\"${i}\""
                                if (i == day) {
                                        out.println " selected=\"selected\""
                                }
                                 out.println ">${i}</option>"
                out << "<option value=\"${i}\""
                                if (i == day) {
                                        out << " selected=\"selected\""
                                }
                                out << ">${i}</option>"
                */
            }
            out.println '</select>'
        }

        // create month select
        if (precision >= PRECISION_RANKINGS["month"]) {
//        if (precision >= 10) {
            out.println "<select name=\"${name}_month\">"

            if (noSelection) {
                        renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            dfs.months.eachWithIndex { m,i ->
                if(m) {
                    out.println "<option value=\"${i + 1}\" ${(i == month) ? 'selected' : ''}>${m}</option}"
                    /*
                    def monthIndex = i + 1
                    out << "<option value=\"${monthIndex}\""
                    if(month == i) out << " selected=\"selected\""
                    out << '>'
                    out << m
                    out.println '</option>'
                    */
                }
            }
             out.println '</select>'
        }

        // create year select
        if (precision >= PRECISION_RANKINGS["year"]) {
//        if (precision >= 0) {
            out.println "<select name=\"${name}_year\">"

            if (noSelection) {
                        renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println ()
            }

            for(i in years) {
//            for(i in 1907..2107) {
                out.println "<option value=\"${i}\" ${(i == year) ? 'selected' : ''}>${i}</option>"
/*
                out.println "<option value=\"${i}\""
                                if (i == year) {
                                        out.println " selected=\"selected\""
                                }
                                out.println ">${i}</option>"
                                out << "<option value=\"${i}\""
                                if (i == year) {
                                        out << " selected=\"selected\""
                                }
                                out << ">${i}</option>"
*/

            }
            out.println '</select>'
        }

        // do hour select
        if (precision >= PRECISION_RANKINGS["hour"]) {
            out.println "<select name=\"${name}_hour\">"

            if (noSelection) {
                        renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println ()
            }

            for(i in 0..23) {
//                def h = '' + i
//                if(i < 10) h = '0' + h
                def h
                if(i < 10) {
                    h = '0' + i
                } else {
                    h = i
                }
                out.println "<option value=\"${h}\" ${(i == hour) ? 'selected' : ''}>${h}</option}"
                /*
                out << "<option value=\"${h}\" "
                if(hour == i) out << "selected=\"selected\""
                out << '>' << h << '</option>'
                out.println()
                */
            }
            out.println '</select> :'

            // If we're rendering the hour, but not the minutes, then display the minutes as 00 in read-only format
            if (precision < PRECISION_RANKINGS["minute"]) {
//            if (precision < 40) {
                out.println '00'
            }
        }

        // do minute select
        if (precision >= PRECISION_RANKINGS["minute"]) {
//        if (precision >= 40) {
            out.println "<select name=\"${name}_minute\">"

            if (noSelection) {
                        renderNoSelectionOption( noSelection.key, noSelection.value, '')
                out.println()
            }

            for(i in 0..59) {
                def m
                if(i < 10) {
                    m = '0' + i
                } else {
                    m = i
                }
                out.println "<option value=\"${m}\" ${(i == minute) ? 'selected' : ''}>${m}</option}"
                /*
                out << "<option value=\"${m}\" "
                if(minute == i) out << "selected=\"selected\""
                out << '>' << m << '</option>'
                out.println()
                */
            }
            out.println '</select>'
        }
    }

        def renderNoSelectionOption = { noSelectionKey, noSelectionValue, value ->
                // If a label for the '--Please choose--' first item is supplied, write it out
        out.println "<option value=\"${(noSelectionKey == null ? "" : noSelectionKey)}\" ${(noSelectionKey == value) ? 'selected' : ''}>${ HtmlUtils.htmlEscape(noSelectionValue)}</option}"
        /*
        out << '<option value="' << (noSelectionKey == null ? "" : noSelectionKey) << '"'
        if(noSelectionKey == value) {
            out << ' selected="selected" '
        }
        out << '>' << HtmlUtils.htmlEscape(noSelectionValue) << '</option>'
        */
        }

    /**
     *  A helper tag for creating TimeZone selects
     * eg. <g:timeZoneSelect name="myTimeZone" value="${tz}" />
     */
    def timeZoneSelect = { attrs ->
        attrs['from'] = TimeZone.getAvailableIDs();
        attrs['value'] = (attrs['value'] ? attrs['value'].ID : TimeZone.getDefault().ID )

        // set the option value as a closure that formats the TimeZone for display
        attrs['optionValue'] = {
            TimeZone tz = TimeZone.getTimeZone(it);
            def shortName = tz.getDisplayName(tz.inDaylightTime(date),TimeZone.SHORT);
            def longName = tz.getDisplayName (tz.inDaylightTime(date),TimeZone.LONG);

            def offset = tz.rawOffset;
            def hour = offset / (60*60*1000);
            def min = Math.abs(offset / (60*1000)) % 60;

            return "${shortName}, ${longName} ${hour}:${min}"
        }

        // use generic select
        select( attrs )
    }

    /**
     *  A helper tag for creating locale selects
     *
     * eg. <g:localeSelect name="myLocale" value="${locale}" />
     */
    def localeSelect = {attrs ->
        attrs['from'] = Locale.getAvailableLocales()
        attrs['value'] = (attrs['value'] ? attrs['value'] : RCU.getLocale(request) )
        // set the key as a closure that formats the locale
        attrs['optionKey'] = { "${it.language}_${it.country}" }
        // set the option value as a closure that formats the locale for display
        attrs['optionValue'] = { "${it.language}, ${it.country},  ${it.displayName}" }

        // use generic select
        select( attrs )
    }

    /**
     * A helper tag for creating currency selects
     *
     * eg. <g:currencySelect name="myCurrency" value="${currency}" />
     */
    def currencySelect = { attrs, body ->
        if(!attrs['from']) {
            attrs['from'] = ['EUR', 'XCD','USD','XOF','NOK','AUD','XAF','NZD','MAD','DKK','GBP','CHF','XPF','ILS','ROL','TRL']
        }
                try {
                def currency = (attrs['value'] ? attrs['value'] : Currency.getInstance( RCU.getLocale(request) ))
                attrs.value = currency.currencyCode
                }
                catch(IllegalArgumentException iae) {
                        attrs.value = null
                }
        // invoke generic select
        select( attrs )
    }

    /**
     * A helper tag for creating HTML selects
     *
     * Examples:
     * <g:select name="user.age" from="${18..65}" value="${age}" />
     * <g:select name=" user.company.id" from="${Company.list()}" value="${user?.company.id}" optionKey="id" />
     */
    def select = { attrs ->
        def from = attrs.remove('from')
        def keys = attrs.remove('keys')
        def optionKey = attrs.remove('optionKey')
        def optionValue = attrs.remove('optionValue')
        def value = attrs.remove('value')
                def noSelection = attrs.remove('noSelection')
        if (noSelection != null) {
            noSelection = noSelection.entrySet().iterator().next()
        }

        out << "<select name=\"${attrs.remove('name')}\" "
        // process remaining attributes
        outputAttributes(attrs)

        out << '>'
        out.println()

        if (noSelection) {
                    renderNoSelectionOption(noSelection.key, noSelection.value, value)
            out.println()
        }

        // create options from list
        if(from) {
            from.eachWithIndex { el,i ->
                out << '<option '
                if(keys) {
                    out << 'value="' << keys[i] << '" '
                    if(keys[i] == value) {
                        out << 'selected="selected" '
                    }
                }
               else if(optionKey) {
                    def keyValue = null
                    if(optionKey instanceof Closure) {
                        keyValue = optionKey(el)
                         out << 'value="' << keyValue << '" '
                    }
                    else if(el !=null && optionKey == 'id' && grailsApplication.getGrailsDomainClass(el.getClass().name)) {
                        keyValue = el.ident()
                        out << 'value="' << keyValue << '" '
                    }
                    else {
                        keyValue = el.properties[optionKey]
                        out << 'value="' << keyValue << '" '
                    }

                    if(keyValue == value) {
                        out << 'selected="selected" '
                    }
                }
                else {
                    out << "value=\"${el}\" "
                    if(el == value) {
                        out << 'selected="selected" '
                    }
                }
                out << '>'
                if(optionValue) {
                    if(optionValue instanceof Closure) {
                        out << HtmlUtils.htmlEscape(optionValue(el).toString())
                    }
                    else {
                        out << HtmlUtils.htmlEscape(el.properties[optionValue].toString())
                    }
                }
                else {
                    def s = el.toString()
                    if(s) out << HtmlUtils.htmlEscape(s)
                }
                out << '</option>'
                out.println()
            }
        }
        // close tag
        out << '</select>'
    }

    /**
     * A helper tag for creating checkboxes
     **/
    def checkBox = { attrs ->
          def value = attrs.remove('value')
          def name = attrs.remove('name')
          if(!value) value = false
          out << '<input type="hidden" '
          out << "name=\"_${name}\" />"
          out << '<input type="checkbox" '
          out << "name=\"${name}\" "
          if(value) {
                out << 'checked="checked" '
          }
          out << "value=\"true\" "
        // process remaining attributes
        outputAttributes(attrs)

        // close the tag, with no body
        out << ' />'

    }

    /**
     * A helper tag for creating radio buttons
     */
     def radio = { attrs ->
          def value = attrs.remove('value')
          def name = attrs.remove('name')
          def checked = (attrs.remove('checked') ? true : false)
          out << '<input type="radio" '
          out << "name=\"${name}\" "
          if(checked) {
                out << 'checked="checked" '
          }
          out << "value=\"${ HtmlUtils.htmlEscape(value.toString())}\" "
        // process remaining attributes
        outputAttributes(attrs)

        // close the tag, with no body
        out << ' ></input>'
     }
}

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

123
Loading...