driscoll's Blog
Executing Groovy Programs in-memory
Now that we've gone over some Groovy basics, it's time to switch back to writing in the Java language, and talk about how to run Groovy programs inside your Java programs. Like most general purpose programming languages, there's more than one way to do things in Groovy, and that's never more true when it comes to executing Scripts - I once counted 7 ways to use the String "System.exit(0)" to shut down the VM, and I'm quite sure that I missed a few. For this example, we'll use the method I consider to be the best and most extensible, which uses the class GroovyShell.
Before we get started, be sure to include the Groovy libraries in your Java class path, so that you can use the same classes as Groovy. In the current distro, it's the file named groovy-all.jar. Once that's there, you should be able to execute the following code:
import groovy.lang.GroovyShell;
import groovy.lang.Script;
public class Test {
public static void main(String[] args) {
<strong>GroovyShell shell = new GroovyShell();</strong>
<strong>shell.evaluate("println 'hello world'");</strong>
}
}
I won't bother including the surrounding cruft in the future, but wanted to illustrate that this is just a (very) simple Java program. We create a GroovyShell object, and call the evaluate method on it, passing a String. That String, in turn, is evaluated by the Groovy parser, a class is (invisibly) created, and the class is run - the end result is that "hello world" is printed to std out.
There's a problem with this code though - it's kinda slow. About 10ms on my machine (for a warm run, where all the required classes are already loaded and initialized - the initialization is actually 300ms, but that's a one time hit). Now, that might be perfectly acceptable for your application - but if you're going to be executing this thing multiple times, that could really add up in a multiuser environment. One of the things that's happening here is a full compile of the generated script code. And while compiler technology has certainly gotten faster in the last few years, it'd be nice to skip that step on occasion. The GroovyShell API offers a way to separate out the compilation and execution steps.
GroovyShell shell = new GroovyShell();
<strong>Script script = shell.parse("println 'hello world'");</strong>
<strong>script.run();</strong>
Here, we've separated out the compile phase from the run phase. The compile phase still takes 10ms, but the run phase only takes whatever the Java class run time is. In this case, it's a sub 1ms run time. This separation also opens things up for us to use other APIs as well, and we're going to use this technique going forward.
Under the hood, here's what's happening: Groovy is taking your input String, and converting it into a class, of type Script. Script contains a method, run. The contents of that run method are populated with the parsed contents of the String you pass in as an argument. So, when we call script.run(), the println method is called, which prints out the message. One last important note is that the run method actually returns Object - so we can also use these scripts to compute values. And if you don't specify a return, the final statement executed in the script will be automatically returned as the value. Thus, if the final line in your script is "x < y", the run method will return a Boolean equal to whatever the result of the comparison is. You can also define inline methods (as we did in the intro post I did), and those methods will be placed on the generated Script - you can even access those methods via reflection.
Next up, using the Groovy Binding class.
(As usual, this article is cross posted on my main blog site.)
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 1194 reads
A quick introduction to the Groovy language (part 2)
In my previous post, I started with a simple Java program (which also worked in Groovy), and slowly stripped out the cruft until I was left with the following Groovy script:
def sayHello(name) {
println("Hello $name!")
}
def name = 'world'
sayHello(name)<br />
Now, let's add a little change to use an array. Since Groovy is a rough superset of Java, you might be tempted to do something like:
def sayHello(name) {
println("Hello $name!")
}
<strong>String[] names = {"SintSi", "Kaitlyn", "Keira"}
for (String name : names) {
sayHello(name)
}</strong><strong><br /></strong>
But this won't work. This is one place where Java syntax differs from Groovy's. To create a static array, you'd instead do:
def sayHello(name) {
println("Hello $name!")
}
String[] names = <strong>["SintSi", "Kaitlyn", "Keira"]</strong>
for (String name : names) {
sayHello(name)
}
As before, we can eliminate the type declarations:
def sayHello(name) {
println("Hello $name!")
}
<strong>def</strong> names = ["SintSi", "Kaitlyn", "Keira"]
for (<strong>def</strong> name : names) {
sayHello(name)
}
But a more Groovy way of doing the same thing would be to use the "in" keyword:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
for (<strong>name in names</strong>) {
sayHello(name)
}
Note that under the hood, this code is no longer creating an array - rather, Groovy is (invisibly) creating an ArrayList. This gives us a number of new options - for instance, sorting:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
<strong>names.sort()
</strong>
for (name in names) {
sayHello(name)
}
You can also add and remove entries to the list, like so:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
<strong>names += 'Jim'
names -= 'SintSi'</strong>
names.sort()
for (name in names) {
sayHello(name)
}
But this still isn't the way that many Groovy coders would do it. They'd probably use the built in method each, which takes a closure as an argument. If you aren't already familiar with closures (I knew them already from JavaScript), they're similar in many ways to method pointers. And you might as well start learning about them, they're coming to Java (finally). To use a closure, you define it using enclosing curly braces, and you can call it with the call method, like so:
def sayHello(name) {
println("Hello $name!")
}
<strong>def clos = {name -> sayHello(name)}
clos.call('world')</strong>
This will print "Hello world!". Note that if you're trying this in groovyConsole, you'll see an additional value at the end - this is because Groovy scripts always return the last value as the return value, and groovyConsole is showing you the value of the names ArrayList. In this example, the "name ->" preamble defines a single parameter that the closure takes. Now let's use the closure with the each method:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
names += 'Jim'
names -= 'SintSi'
names.sort()
def clos = {name -> sayHello(name)}
<strong>names.each(clos)</strong>
Under the covers, names.each is iterating through the collection, and passing each value to the closure as the first parameter, just as in our previous example. We can simplify this by creating the closure in place. And since in Groovy, method parenthesis are optional when the method takes one or more parameters, we can say:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
names += 'Jim'
names -= 'SintSi'
names.sort()
<strong>names.each {name -> sayHello(name)}</strong>
Which is pretty darn readable. One more thing: by default, the first parameter of a closure is named "it". So, you could instead say:
def sayHello(name) {
println("Hello $name!")
}
def names = ["SintSi", "Kaitlyn", "Keira"]
names += 'Jim'
names -= 'SintSi'
names.sort()
names.each {<strong>sayHello(it)</strong>}
That's it for now. If you've followed along so far, you've gotten more than enough Groovy under your belt to deal with anything you might see in my coming posts, as well as being able to say "Sure, more or less", when someone says "Do you know any Groovy?"
(As usual, this article is cross posted on my main blog site.)
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 2842 reads
A quick introduction to the Groovy language (part 1)
Before I start talking about using Groovy's capabilities to create a DSL (mostly in Java), let's take a few minutes to go over what Groovy is.
Groovy is a general purpose scripting language which runs on the JVM, and can largely be viewed as a superset of Java. Take the following program:
public class Hello {
String name;
public void sayHello() {
System.out.println("Hello "+getName()+"!");
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Hello hello = new Hello();
hello.setName("world");
hello.sayHello();
}
}
Is this a Groovy program or a Java program? Yes, it is - it will compile and run in both. This basic program is a bit wordy, and we can certainly do things more simply in Java, but this contains a number of patterns that you'd commonly see, including the use of the bean pattern, as well as the use of the main method to make the class executable via the java command line program. When we run it, it simply prints out "Hello world!", as is customary in these sorts of things.
To try this out in Java, you can use your favorite IDE. You can also use an IDE to try this in Groovy, but you may just want to use the groovyConsole program, which is available as part of the GDK. Download it now, and play along via cut and paste...
Now, as I said, Groovy is a rough superset of Java - one difference is that things are public by default. That means we could just as easily say the following:
<strong>class</strong> Hello {
String name;
<strong>void</strong> sayHello() {
System.out.println("Hello "+getName()+"!");
}
<strong>void</strong> setName(String name) {
this.name = name;
}
<strong>String</strong> getName() {
return name;
}
<strong>static void</strong> main(String[] args) {
Hello hello = new Hello();
hello.setName("world");
hello.sayHello();
}
}
Accessors and mutators are automatically created for your class variables, so we can also shorten it by taking those out:
class Hello {
String name;
void sayHello() {
System.out.println("Hello "+<strong>getName()</strong>+"!");
}
static void main(String[] args) {
Hello hello = new Hello();
<strong>hello.setName("world");</strong>
hello.sayHello();
}
}
Now we're getting somewhere. In Groovy, System.out.println can be shortened to just "println" as a convenience, so you can also say:
class Hello {
String name;
void sayHello() {
<strong> println("Hello "+getName()+"!");
</strong> }
static void main(String[] args) {
Hello hello = new Hello();
hello.setName("world");
hello.sayHello();
}
}
There's also a difference in how Groovy deals with String objects - double quote strings allow for variable substitution. There's also single quote strings, which do not:
class Hello {
String name;
void sayHello() {
println(<strong>"Hello $name!"</strong>);
}
static void main(String[] args) {
Hello hello = new Hello();
hello.setName(<strong>'world'</strong>);
hello.sayHello();
}
}
Groovy also allows dot notation to get and set fields of classes, just like Java. Unlike Java, this will actually go through the getter/setter methods (which, you'll recall, are automatically generated in our current example):
class Hello {
String name;
void sayHello() {
println("Hello $name!");
}
static void main(String[] args) {
Hello hello = new Hello();
<strong>hello.name = 'world'</strong>;
hello.sayHello();
}
}
Typing information is also optional - instead of specifying a type, you can just use the keyword def. Use of def is mandatory for methods, but optional for parameters on those methods. You should also use def for class and method variables. While we're at it, let's take out those semicolons: They're optional in Groovy.
class Hello {
<strong>def</strong> sayHello(<strong>name</strong>) {
println("Hello $name!")
}
static <strong>def</strong> main(args) {
Hello hello = new Hello()
<strong>def</strong> name = 'world'
hello.sayHello(name)
}
}
OK, that's nice, but we can get this even shorter. Because Groovy is a scripting language, there's automatically a wrapping class (called Script, which will become very important to us later). This wrapping class means that we can get rid of our own wrapping class, as well as the main method, like so:
def sayHello(name) {
println("Hello $name!")
}
def name = 'world'
sayHello(name)
That's all for now, I'll do a second part to this later. But for now, if someone asks you if you know Groovy, you can now say "Sure, a bit".
Credit for the idea behind this post goes to Guillaume Laforge - I saw him give a variation of this as an intro to his talk at JavaOne, and loved it.
(As usual, this article is cross posted on my main blog site.)
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 2346 reads
DSLs with Groovy JavaOne talk
I've been neglecting my blog, but just a quick note to mention that my latest talk at JavaOne, DSLs with Groovy, is posted up on Slideshare.
The talk's designed for someone with no significant Groovy experience (unlike most Groovy DSL talks), so if it's interesting to you, check it out.
I'm hoping (but not promising) to turn the talk into a series of Blog entries in the coming weeks. So if you want, just wait, and I'll send you explanations in more digestible bits and pieces in the coming weeks.
(As usual, this entry is cross posted to my main blog site.)
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 665 reads
Testing JSF
It's been a while since I've blogged last (ok, it's been a year), but I recently came across a question that I have a little insight into, and I thought I'd tackle it briefly.
The question was simple: "How do I test my JSF application?"
I've used two different test frameworks to do so, and besides being two of the most popular, they also offer a good example of the two different patterns used for Web testing. The two programs are HTMLUnit and Selenium. (Please note, there are certainly others - FacesTester, and JSFUnit (which actually uses/extends HTMLUnit), to name just two - but I'm going to stick to the two that I've used.)
HTMLUnit is a framework which uses browser simulation to test web pages - this means that no browser is launched, which also means that you can do things like test IE7 on Linux without a copy of Windows, as well as running testing in "headless mode", meaning that you don't even need a windowing system to be running (which is handy, and also very efficient for performance). There are downsides to this, of course - for one, it's possible that the simulation may not be perfect (though I've never found a case where it wasn't). The other disadvantage is that you can't actually see what's going on during testing, which can be awkward when figuring out why your test failed. HTMLUnit supports emulating FF2, FF3, FF3.6, IE6, IE7, and IE8.
Selenium, on the other hand, uses browser automation - meaning that it's actually using the browser you're testing your web app in. But that, in turn, means you'll need different browsers installed to test - and in Windows, that means that you'll also need multiple machines to test all the myriad versions of IE. And since you're running a browser in a windowing system, the machine requirements are also a little more substantial. I've also found that configuring the browsers to be a bit tricky at times. However, any inconvenience in setup is more than made up for by the Selenium IDE, which is really just a Firefox plugin that records mouse and keyboard actions for later playback. Selenium supports automation of FF2, FF3, IE7, IE8, Safari2, and Safari3.
In both frameworks, you use their Java API to get a copy of a webpage, find components on that page (such as a button), and perform actions on those components (such as a click). As such, the usage of both is pretty easy, and I'm not going to give a introductory tutorial of either. (Selenium also offers a couple of other automation scripting methods in addition to Java, though I've never used them, so the topic is really much too big for a simple blog post.)
Which should you use?
I suspect that if you're writing your first JSF test, Selenium is really the way to go, for one simple reason: JSF id generation. As I'm sure you know already, every HTML tag can have an id, and that id needs to be unique on that page. For both APIs, the simplest way to find those tags within your code is to use the id. There's just one problem: the id that's in the generated HTML may be quite different from the id you use in your JSF page.
Take this (simplified) example:
<h:form id="frm"> <h:outputText id="out" value="#{whatever}"></h:form>
Now, say you wish to read the contents of the outputText... What id would you use? Answer: "frm:out". Because templating can be used to insert one page into another, and all id's need to be unique on the page, JSF has the idea of a "naming container" - every component inside a naming container has its naming container id prepended to the id of the component itself. A form is one such naming container, but there are many more.
Now, it's generally possible to figure out what the id will be (though not always easy, given some component libraries), so probably the easiest way to find the component's id would be to use the Selenium IDE, and use it to find the id's of the generated code. The other ways to do this would be to use View Source on your browser, or dump the page contents in the Java code that you're creating to System.out, and examine it that way.
Other factors may influence your decision, of course. HTMLUnit has much better support for Ajax, Selenium has (somewhat feeble) support for Webkit based browsers, which HTMLUnit has none.
Anyhow, as I mentioned, I'm hardly an expert in this area, but I thought I knew enough to at least be helpful to the novice. If you're not a novice, and you've got any additional comments (such as corrections or additional frameworks that folks should check out), please comment...
(Note: This post is crossposted to my personal blog)
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 2000 reads
HTML5 Semantic Tags
Over the weekend, I was reading Mark Pilgrim's great book on HTML5 - and when I got to the part about the semantic tags, I thought it might be worth a quick mention.
In case you've missed out on HTML5 in general (and don't want to take the time to read that book I linked above), the idea behind semantic tags is that many sites use div blocks to mark out the same kinds of content, over and over. Content like headers, footers, and nav bars. Changing straight <div> tags to tags like <header>, <footer>, and <nav> is granting these tags semantic meaning, hence the name - semantic tags.
Semantic tags are a great idea. They offer a lot advantages over plain vanilla divs, especially for screen readers... but support in IE is pretty broken... The essential problem is this: unlike all other major browsers, IE doesn't know how to style unknown tags. So the following code won't work:
<style>
.border {
border: solid black;
}
</style>
...
<section class="border">test3</section>
Ah, I hear the more informed folks in the audience say, there exists a library to fix this problem: the HTML5 Shiv. You can use it like so:
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
This simple script will allow styles to be placed on unknown tags in IE... So, that's a good start, but there are a few problems with it. For one thing, it relies on JavaScript, so if JavaScript is disabled, your styling will fail catastrophically. Similarly, applying print styles may not work, since JavaScript won't necessarily be run as part of the print process (note: I haven't tested this fully, but that's sure what it looks like in brief testing). There are reports that nesting seems to mess stuff up applying styles correctly, but my testing hasn't found anything broken in this way that isn't already broken in IE's CSS support.
Of course, there is a way around even that: If you are running JSF or some other server side processing on your backend, you could do User Agent detection, and emit <div>'s to IE and the semantic tags to all other browsers. Then, by styling the tags solely with classes and ID's, it should be possible to make something that gets around the client side issues. Here's a section from a component that does just that.
@FacesComponent(value = "navtag")
public class NavTag extends UIComponentBase {
@Override
public void encodeBegin(FacesContext context) throws IOException {
boolean isIE = false;
UIComponent component = getCurrentComponent(context);
String style = (String) component.getAttributes().get("style");
String styleClass = (String) component.getAttributes().get("styleClass");
ResponseWriter responseWriter = context.getResponseWriter();
String ua = context.getExternalContext().getRequestHeaderMap().get("User-Agent");
if (ua != null && ua.contains("MSIE") && !ua.contains("Opera")) {
isIE = true;
}
if (isIE) {
responseWriter.startElement("div", null);
} else {
responseWriter.startElement("nav", null);
}
responseWriter.writeAttribute("id", getClientId(context), "id");
responseWriter.writeAttribute("name", getClientId(context), "clientId");
if (styleClass != null) {
responseWriter.writeAttribute("class", styleClass, "styleClass");
}
if (style != null) {
responseWriter.writeAttribute("style", style, "style");
}
}
Should JSF add these tags to JSF 2.1? I'd love to hear your comments, below...
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 6411 reads
Progressive Enhancement with JSF
Progressive Enhancement is a philosophy of web design - start with simple pages, and build them up based on the capabilities of the browser viewing the page. It’s related to (and in some ways, the opposite of) the idea of Graceful Degradation, starting with a nice, fancy page, and dealing with any browser faults in an elegant manner.
Prehaps the simplest example to see this in action is the case of JavaScript being disabled in the browser - this is occasionally true for certain corporate clients concerned about security, and sometimes the case for very old browsers.
JSF handles this usecase pretty well - consider the following code:
1
2 <f:ajax render="grace :text">
3 <h:selectBooleanCheckbox value="#{grace.checked}"/>
4 </f:ajax>
This creates a checkbox input with an onclick event handler registered. If there’s no JavaScript enabled, it will continue to function as thought the ajax tag wasn’t there at all. But the user will need to submit the form with a button press...
There is another way to handle this: we could instead create a link, which uses view parameters:
1 <f:metadata>
2 <f:viewParam name="checked" value="#{grace.checked}"/>
3 </f:metadata>
4 <h:link value="check me">
5 <f:param name="checked" value="#{!grace.checked}"/>
6 </h:link>
That works, but isn’t as clean looking as the first, ajax method. Combining these approachs should provide a better user experience - and doing so isn’t especially difficult:
1 <f:metadata>
2 <f:viewParam name="checked" value="#{grace.checked}"/>
3 </f:metadata>
4 <h:outputText id="text" value="Checked: #{grace.checked}"/>
5 <h:form id="form">
6 <h:panelGroup id="grace" layout="block">
7 <h:panelGroup id="default">
8 <h:link value="check me">
9 <f:param name="checked" value="#{!grace.checked}"/>
10 </h:link>
11 </h:panelGroup>
12 <h:panelGroup id="enhanced" style="display: none">
13 <f:ajax render="grace :text">
14 <h:selectBooleanCheckbox value="#{grace.checked}"/>
15 </f:ajax>
16 </h:panelGroup>
17 <script type="text/javascript">
18 var def = document.getElementById("form:default");
19 var enh = document.getElementById("form:enhanced");
20 def.style.display = "none";
21 enh.style.display = "block";
22 </script>
23 </h:panelGroup>
24 </h:form>
First, create two divs, one with the link and the other with the checkbox, which is hidden by default. If JavaScript is enabled, then hide the link and show the checkbox. This is the basic idea behind Progressive Enhancement - first, create something that you’ll be happy with in any browser, then add features (in this case, an Ajaxified checkbox) as needed.
That’s all for today. One personal note: Today is my last day officially employed by Sun Microsystems - I wasn’t offered a position at Oracle, and I’m currently actively looking for something. Please feel free to checkout my resume, and let me know if you know of any openings that you think might be a fit for me.
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 4595 reads
Comments
personal note
by vladperl - 2010-02-10 22:32
Hi Jim,I'm really sorry to hear that you didn't get position at Oracle.
It's pretty stupid decision from their side. Too bad I can't help you to find a new job.
Anyway please keep it going. Your blogs written in practical manner and helpful.
I have just started to read the following book and somehow your situation has connection with view that author described directly in introduction of the book.
Linchpin: Are You Indispensable?
Good luck, Vladimir
IE, Memory Management, and You
In a recent blog, commenters took me to task for a perceived IE 6 memory leak. It wasn't actually there (they were wrong), but in attempting to prove myself right, I found a couple of memory leaks under IE in JSF's Ajax support. Since I just spent a week learning how all this functioned, I thought I'd set it down so that others could learn from my efforts.
Now, none of the information that I'll present here is new - it's been discussed among Ajax programmers for at least the last 4 years. If you're a web guru, it's likely that you're not going to learn anything new here (thought I'd welcome any additional information and corrections). But at least a couple of the points I'll illustrate below are either poorly communicated or misunderstood. I'll include a number of links at the end of this article. There are also very significant differences between IE 8 (which mostly works), IE 7 (which is bad), and IE 6 (which is just awful). I'll try to point out the differences as they matter for each.
Tools
First - use the right tool for the job: In order to spot leaks, you'll need to download a tool that can detect them. By all accounts, sIEve is the way to go. It uses IE itself, and introspects to get it's data. The UI is pretty primitive, but I can't recommend it enough - it's truely invaluable. Since it uses IE for it's work, you'll need to run it on a machine that has IE6 installed - presumably in a VM. You'll also want to have it running on a machine that has IE 7 and IE 8 as well, just to be sure. XP fits nicely on a VM that runs on my Mac, and this is how I use it.
Cyclic Leak
Now that that's out of the way, it's time to talk about the very worst of the memory leaks in IE - the dreaded cyclic reference, which the commenters thought that I'd committed. Under certain conditions, IE 6 will "leak" DOM nodes, retaining them, and the javascript objects that point to them, until the browser is either shut down, or crashes entirely due to lack of memory. Ugh! To understand how this happens, you really only need to know two things:
- IE 6 (and 7!) reportedly has very primitive garbage collection using reference counting
- There are two memory spaces in IE, one for JavaScript, and the other for the DOM, and they don't communicate well.
What could go wrong? Well, lots. The commenters thought that the rule was: A leak will occur if any reference is made in JavaScript to an element that isn't eventually set to null. That's close, but not quite correct. The real rule is: A leak will occur if the JavaScript code contains any reference to the DOM that isn't released in some way, either by going out of scope or being explicitly unset.
When IE 6 sees a JavaScript variable that is pointing to something in the DOM (typically, an element or node), it will record that reference, and not collect it - even when you surf over to a new page. And the DOM won't be collected, since there's a reference to it from JavaScript. These two objects, and all the stuff that references them, will stick around until shutdown. In IE 7, the geniuses at Microsoft saw the bug, and said "Hey, I know how to fix that, let's garbage collect everything when we leave the page.". Nice improvement, but it still doesn't fix the bug, since if you're developing a page that is designed to be used for a long period of time (like many page-as-application apps are now), it'll still crash the browser. Apparently, they saw the error of their ways eventually, since this behavior is no longer present in IE8. (All this is confirmed by my testing with sIEve.)
So, in the example that had in my previous blog, there was no memory leak, because the variable that pointed to the element eventually went out of scope. So - how to you create variables that don't go out of scope? The easiest way is to put them in an object - this was the leak that I eventually found in JSF. The fix there was to null out the object manually. But there's another, more insidious way to create an object - create a closure. That creates a function object implicitly under the window object, and that will never go out of scope. But the key thing to remember is that you need to be aware of when things go out of scope when coding in IE, and act accordingly.
But wait! There's more
If that was the only problem, life would have been fairly easy for me the last week. But that's not the only bug that the Web Wizards of Redmond chose to deliver to their unsuspecting consumers. There's another bug in IE (again, only in IE 6 and 7 - IE 8 appears to have fixed it per my testing), which also leaks DOM nodes that aren't cleaned up until you leave the page. Apparently, when the IE DOM receives a call from the removeChild or replaceChild functions, it doesn't actually, err, remove the nodes. It just leaves them there, hanging around the DOM like party guests that don't have the sense to leave after the host has started handing out coats. While these nodes will eventually be cleaned up when the user leaves the page, this still causes problems for page-as-app programs, as in the cyclic leak for IE 7, above. While the removeChild call is fairly notorious for this, I had to find out about replaceChild with my own testing (though I did find a few obscure references once I went looking for it).
That means that instead of saying node.parentNode.replaceChild(newNode, node), you instead should say something like: node.parentNode.insertBefore(newNode, node); deleteNode(node); (with an appropriate if statement for isIE(), and a deleteNode function that doesn't use removeChild). And instead of saying node.parentNode.removeChild(node); you instead are reduced to coding something like: node.outerHTML = ''; (again, with browser check). Except that when you combine that with IE's horrible problems with manipulating tables, it may fail. So instead, you're probably better off with something like this:
<pre>
var temp = document.createElement('div');
try {
temp.appendChild(node.parentNode.removeChild(node));
temp.innerHTML = ""; // Prevent leak in IE
} catch (e) {
// at least we tried
}
deleteNode(temp);
</pre>Again, possibly with an isIE() check.
Hopefully you found this description of IE's Memory "Management" useful. Here's a few of the links that I used for research, that I found the most helpful.
- sIEve - the tool you should already have.
- Quirks blog link roundup of memory leak info.
- Microsoft MSDN article on the topic. Useful, even if it insultingly implies that it's your fault the browser is leaking.
As always, I look forward to any comments. Especially about this topic - I'm far from expert in this area.
UPDATE: John Resig just posted about a very interesting looking tool. Haven't checked it out yet, but if it's got him excited...
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 4260 reads
Comments
There is also one more awful
by alexsmirnov - 2009-11-16 11:41
There is also one more awful behaviour of MS IE, at least 6 and 7 related to DOM updates. These browsers start downloading images ( and other referenced objects ) for HTML elements that have been created by JavaScript, even though for objects that are not attached to page. When these elements are inserted on actual place, browser stops loading by closing TCP connection that causes IOException on server and starts download of the same objects again. Therefore, you can see a lot of error records in the server log and unexpected network connections that slow down client application. To avoid such problem it had better to create updated content in place using inner/outerHTML properties and avoid temporary objects for DOM fragments.Mojarra 2.0.1 has shipped
Just a short post to note that we've now shipped Mojarra 2.0.1. This version fixes a very serious bug when running on Tomcat. You can pick up the files from the usual places, see the release notes for more information.
If you're using GlassFish, and already running 2.0.0 (you leading edge adopter!), there's probably no reason to upgrade - though the new v3 (b69) has the updated jar, and it will be propagated via the usual Update Center distribution.
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 4837 reads
Comments
Failed to deploy 2.0.1 to GlassFish v3 Preview
by jeffcampbell - 2009-10-28 06:47
We have a Maven war project that includes the 2.0.1-b02 impl/api jars into our war file.... we deploy our war to both Tomcat and GlassFish (we are evaluating moving to GlassFish v3 Preview). I added the following to my sun-web.xml file:class-loader delegate="false"
property name="useBundledJsf" value="true"
but, when I do this, I get the following error when I deploy my simple war file:
java.lang.Exception: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getServletRegistrations()Ljava/util/Map;
I know I don't need 2.0.1 for GlassFish v3 Preview, but I'm trying to use the SAME war file that I use in Tomcat as I use in Glassfish. Am I doing something wrong?
Thanks,
Jeff
Please file a bug - or ask webtier@glassfish.dev.java.net
by driscoll - 2009-11-02 15:47
If you come across a problem that seems to be a bug in Mojarra, please file it as a bug. Offhand, with the information you've provided, it sounds like you're also picking up an old version of the Servlet API - the odds are good that it's a bug in how you're building your war, but I don't know based on the info you've provided.
Thank you for fixing this
by zfqjava - 2009-10-26 07:53
Thank you for fixing this bugs in tomcat quickly, but the current release still have a serious bug cause it cannot be used in production, this bug has been reproduced since changed the client id to make it short, so we still use Mojarra Beta2, the Beta2 have not serious bugs except the default attribute value.Click a command link on a DataTable row, sometimes it throws the following exception :
Caused by: javax.faces.FacesException: Unexpected error restoring state for component with id j_idt27:j_idt45:j_idt72. Cause: java.lang.IndexOutOfBoundsException: Index: 0, Size: 0.
at com.sun.faces.application.view.StateManagementStrategyImpl$2.visit(StateManagementStrategyImpl.java:239)
at com.sun.faces.component.visit.FullVisitContext.invokeVisitCallback(FullVisitContext.java:147)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1443)
at javax.faces.component.UIData.visitColumnsAndRows(UIData.java:1520)
at javax.faces.component.UIData.visitTree(UIData.java:1207)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1454)
at javax.faces.component.UIForm.visitTree(UIForm.java:324)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1454)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1454)
at javax.faces.component.UINamingContainer.visitTree(UINamingContainer.java:163)
at javax.faces.component.UIComponent.visitTree(UIComponent.java:1454)
at com.sun.faces.application.view.StateManagementStrategyImpl.restoreView(StateManagementStrategyImpl.java:223)
at com.sun.faces.application.StateManagerImpl.restoreView(StateManagerImpl.java:177)
at com.sun.faces.application.view.ViewHandlingStrategy.restoreView(ViewHandlingStrategy.java:131)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.restoreView(FaceletViewHandlingStrategy.java:429)
at com.sun.faces.application.view.MultiViewHandler.restoreView(MultiViewHandler.java:143)
at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:199)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:310)
... 16 more
Please file a bug
by driscoll - 2009-11-02 15:41
Blog comments aren't really the place to talk about bugs - it's the wrong format, and it can take a while for me to notice them.
Please come up with a short, reproducable test case, and post it to the Mojarra issues list.
This is the first I've heard of this problem, and you don't nearly give enough information to reproduce the bug.
Request aggregation in JSF 2 Ajax
I've had a few requests for request aggregation, ala RichFaces queues, in JSF 2. This was deliberately not included in JSF 2.0, but it will be considered for JSF 2.1. The reason why is simple - there was simply not very much time, once all the base Ajax work was completed, to add any additional features. However, adding this functionality yourself isn't actually very hard. Here's an example of how.
For those not familiar with the idea of request aggregation, the idea is a pretty simple one: in cases where the user may generate a large number of requests (for instance, with the keyup event), you're going to want to wait and see if you can bundle the requests together, so you don't spam the server with thousands of tiny little requests.
The example will have two parts: a JSF page, and some backing JavaScript. The end result will be a program which accepts input, and echos it out to another part of the page via an Ajax request to the server. There's also a status area to show a little of what's going on during the call.
Here's the form:
1 <h:form id="form1" prependId="false">
2 <h:outputScript name="jsf.js" library="javax.faces" target="head"/>
3 <h:outputScript name="javascript/aggregate.js" target="head"/>
4
5 Output: <h:outputText id="out1" value="#{echo.str}"/>
6 <br/>
7 Input: <h:inputText id="in2" value="#{echo.str}" autocomplete="off"
8 onkeyup="aggregate('out1 in1', this)"/>
9 <br/>
10 Status:
11 <br/>
12 <textarea id="status" rows="10" cols="50" readonly="readonly"/>
13 </h:form>
And here's the backing JavaScript:
1 var increment = 1000;
2 var token;
3 function aggregate(target, element) {
4 window.clearTimeout(token);
5 addStatusMessage("cleared request, requeued");
6 var send = function send() {
7 jsf.ajax.request(element, null, {render: target});
8 };
9 token = window.setTimeout(send, increment);
10 }
11
12 function addStatusMessage(message) {
13 var statusElement = document.getElementById("status");
14 var status = statusElement.value;
15 var now = new Date();
16 var timestamp = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
17 status = timestamp + ' ' + message + '\n' + status;
18 statusElement.value = status;
19 }
20
21 jsf.ajax.addOnEvent(function(data) {
22 if (data.status === "begin") {
23 addStatusMessage("request sent");
24 }
25 });
This program is simple enough that it's function should be pretty self evident - but in case you've never worked with JavaScript timers, here's the control flow.
When you type a character into the input field, the aggregate function is called (JSF page line 8). If an outstanding request is already there, it will be canceled (JavaScript line 4), and a status message will be written (JavaScript lines 12-19) to a readonly textarea in the page (JSF page line 12). Then, a new request will be submitted, which will go off after 1 second (JavaScript line 9). If you type a new character before the second has elapsed, then the first request is canceled (JS line 4), before being submitted again with a new value (JS line 9). If a whole second goes by without a new keypress, then the request will finally be sent to the server, which will also trigger calling the event function (JS lines 21 - 24), which in turn writes out a status (JS lines 12-19), to the textarea (JSF line 12).
So, all told, the aggregation code was about 6 lines of JavaScript. And while that may be a trifle annoying, I can only assume that anyone writing a component like Autocomplete will include this into the component so you never need see it.
As always, feel free to ask questions on this post in the comments.
- Login or register to post comments
- Printer-friendly version
- driscoll's blog
- 6539 reads
Comments
How to do this?
by shock01 - 2010-01-25 08:52
Hi John, It's off topic for this blog post, but I'm interested in the following compontent: On facebook you have those more links on the homepage that display older posts. How would one implement this in Mojarra? Maybe you can create an example for this.Already tried in the past but the approach has some problems.
by ishaikovsky - 2009-10-21 07:14
Yes, we agree that this partially solves server flood problem. And we even used similar approach in the past. But looking closer to RichFaces queue we could see some features missed with the code listed in your post:If some controls can't cancel requests of each other - them should use different aggregate methods. Let imagine the input with validation ajax capabilities using onkeyup and some form button. After some keyups appears - setTimeout used for the last one, but the user already clicked the button in the same time and we need both request to be sent sequentially. In the case of using two or more aggregate functions nobody guaranties that the order of requests will be kept. RichFaces has requests similarity mechanism which group similar requests in queue and send combined requests with different similarity one by one sequentially.
RichFaces queue requests wait for the previous request to be finished. And JSF queue - not. So concurrency problems will be still there even using this approach.
Also, there is some problems with stale form data if using jsf.ajax.request() will appears. The same as described there: RonanKER Forum Post
and b.t.w. this code
6 var send = function send() {
7 jsf.ajax.request(element, event, {render: target});
8 };
9 token = window.setTimeout(send, increment);
will most likelly cause memory leaks in IE
So maybe the problem is partially solved. But complete mechanism still should be implemented in order to use safely at production environments.
A couple comments
by driscoll - 2009-10-21 10:10
Since this behavior will be implemented by the end user, it's expected that they'd know which fields they want to aggregate or serialize. The point of the example was to give a *simple* example of use, not a comprehensive solution.
But this simple code probably solves 80% of folk's needs, since the typical use for this would be keypress events.
One substantial correction: The JSF queue is serial, so that concurrency problem you mention isn't there - though as you point out, ordering may matter to you.
Stale data would typically refer to using data that's no longer in the form - which isn't a problem, since the Ajax request will collect data as it appears at the moment the request is submitted. I think you meant that in the case of multiple requests, there is no atomaticity - but that would rarely, if ever, be a problem. Caching the data would result in stale data, but solve the atomaticity problem.
As for the memory leak - IE 6 leaks memory, generally, if you use DOM0 events, so this code is the least of your problems running under IE6 - though I'm not seeing the cyclic reference in the four lines you mention. IE7 fixes this partially, of course, by making the memory get collected when you leave the page's scope, and IE8 fixes it altogether (AFAIK).
My two cents
by nick_belaevski - 2009-10-22 16:33
> Stale data would typically refer to using data that's no longer in the form - which isn't a problem, since the Ajax request will collect data as it appears at the moment the request is submitted> I think you meant that in the case of multiple requests, there is no atomaticity - but that would rarely, if ever, be a problem. Caching the data would result in stale data, but solve the atomaticity problem.
There's a case when Ajax request collects data not at the moment when the request is submitted. This happens when jsf.ajax.request() is triggered by timer, but previous request hasn't completed yet (e.g. because of not fast DB). This can cause problems described in forum post to which Ilya referred.
> As for the memory leak - IE 6 leaks memory, generally, if you use DOM0 events, so this code is the least of your problems running under IE6 - though I'm not seeing the cyclic reference in the four lines you mention.
> IE7 fixes this partially, of course, by making the memory get collected when you leave the page's scope, and IE8 fixes it altogether (AFAIK).
Cyclic reference here is: window -> send -> element -> parentNode -> ... -> document -> parentWindow -> window. IE7 is still leaking in some cases (actually much less than IE6); cannot say anything about IE8.
No, it doesn't leak
by driscoll - 2009-11-09 11:58
No, the code I posted above doesn't leak.
The IE6 memory leak doesn't work the way you think it works, apparently. I tested it with sIEve, to be sure. I did, however, find a leak in the JSF Queue impl, which is embarrassing, so it wasn't wasted effort. That fix will be in 2.0.2. And the cyclic nature of IE leaks doesn't work the way you described it - there are two memory spaces, not one.
There was an additional bug in the example I posted, when run under IE - the event object in IE in global, not specific, so you can't just pass it to the timer. But that's easily fixed by changing it to a null, since it wasn't really required. (I've changed the code above to reflect that.)
I don't think I'm using the phrase "stale data" in the same way that you are, I guess - I come from an IT DB background, so I'm using it the way that any DB guy would. The data is current as of the submission time (launching the ajax request) to the backing store - just as if the user had pressed a button or struck a key. The data is consistent on the page - it may not be updated to the backing store, but that's what the ajax request is for. Making the request reflect what the state was at the time of user request (as opposed to submission) would result in stale data.




Comments
Hey Jim - I think it could
by rogerk - 2010-02-09 10:13
Hey Jim -
I think it could be a possibility with some of the other nice features HTML5 has to offer.
I'd be curious to see what the EG and the overall community has to say about it.
-roger