The Source for Java Technology Collaboration
User: Password:



Kohsuke Kawaguchi's Blog

Kohsuke Kawaguchi Kohsuke Kawaguchi is a staff engineer at Sun Microsystems. He has been working on XML and XML schema languages since 2001, in particular RELAX NG, W3C XML Schema, JAXB, and JAXP. He also hosts many projects on java.net. More information on him can be found at http://www.kohsuke.org/.



JAXB RI 2.1.7 releas

Posted by kohsuke on May 14, 2008 at 08:43 AM | Permalink | Comments (0)

I just pushed a new version of JAXB RI, 2.1.7. This version contains several bug fixes, and it's the one used by the latest Metro 1.2.

My JavaOne highlights

Posted by kohsuke on May 12, 2008 at 10:17 PM | Permalink | Comments (3)

My JavaOne highlights
Shook hands with Jonathan Schwartz
Jonathan Schwartz and Rich Green came to the GlassFish overview session in CommunityOne, of which I was a small part. I've seen Jonathan a few times in the cafeteria, but never so closely. I thought he got a little chubbier, but maybe it's just me. Their Q&A time forced us to cut my part entirely :-(, but oh well.
People liked embeddable GlassFish
GlassFish had a good presence throughout the conference, and people liked its embeddability, too. Enabling more unit testing opporunities seem like one of the things people are interested in. We should push this further.
Bill Pugh seems to like Hudson now
Bill Pugh AKA Mr.FindBugs came to my Hudson booth, and we discussed about several things. Back when he visited Sun (maybe a year ago?) I showed him Hudson, and he appeared to be more knowledgable about Hudson now. He told me he'll mention Hudson in his "findbugs in anger" talk, and sure enough, after that, I started to see more people dropping by my booth, mentioning things like "hey, I just learned about Hudson in this conference ..."
Duke's Choice Award
I already talked about this. This year, the award ceremony wasn't a part of the James Gosling general session, and instead it was done in Tuesday evening. Unfortunately, I couldn't attend this ceremony because of a schedule conflict with the GlassFish v3 talk that Jerome and I gave.
JavaPosse came by
JavaPosse was interviewing on the pavilion floor, and I got interviewed. I told them that my daughter is a big fan of the opening Java-Java-jing-jing-jing song. It looks like the interview became a part of episode 186.
Tom Huybrechts came by
He is one of the active Hudson committers, and this was the first time I met him in person. He had a very interesting plugin that he's working on, which uses JBoss BPEL engine and allow people to automate a complex work flow that can involve humans (think of a release process, for example.) He showed me how to design a workflow in Eclipse and uploads that to Hudson. I believe this will be his GlassFish Award Program submission. Can't wait to see it released.
Hudson session on Friday
During the rehearsals, Rama and I were doing this constantly at about 50 minutes, but somehow in the real run, it took longer, and I had to rush things through toward the end. But otherwise I hope the presentation went all right (judging from a few blog postings like this and e-mails, at least it impressed a few folks.) A part of the demo in which Rama showed the Maven integration where the user needed almost no configuration got an applause.
Hudson booth
Every Duke's choice award winner gets a small booth, and thanks to this, in the first time in JavaOne, I was able to get my own booth. Jesse helped me man the booth. I liked this arrangement a lot, because now all I needed to do was to sit and wait for people to come see us. While I was able to talk to a lot of Hudson users, I also talked to many people who had no idea what Hudson was. I felt I needed more marketing.
Meet up with other Japanese developers in Thanh Long
This is a meet up for people of what I call as "Maruyama-sensei school" — they are some of the best and the brightest Japanese developers, and I have an honor to be a small part of it. This yearly dinner became an integral part of JavaOne for me, and I meet new people every year. The food was expensive but good, and it was fun and inspiring. I run out of my business cards, which is a real shame because this is probably the only time I have any use for my business cards.
JetBrains developers knew me!
I went to the JetBrains booth to ask a few questions about TeamCity, but ended up talking more about IntelliJ IDEA, because some of them knew that I did a few plugins. What an honor. I got a few ideas about how to go about plugins that I wanted to write, so that was good. The person who did the Metro support in IDEA was there.
I stayed in San Francisco for a few nights
This year, I was able to stay in San Francisco for two nights, and it really made things convenient. I was able to be in a diner until midnight talking to folks, and yet next morning I was at the general session on time. Such an act is just impossible if I had to drive back and forth everyday from San Jose. It also allows me drink, which is another integral part of JavaOne :-)
Speaking of drinking, I was counting on one free bear by Fabrizio Giudici, but I couldn't find him. Darn!

On Saturday I opened a lego creator house set, which I bought for myself and saved it until after JavaOne, and played it a whole day with my daughter (lesson to myself — next year, buy two sets to avoid conflict!) Lego creator series is a lot more playable than some other series like Star Wars series.

On Sunday I took the family to Niles California for antique shopping which my wife loves. I noticed that JavaOne is after Mother's day next year, which is probably not a good thing for our wives.

I still need to wrap up the last installment of my 5-part Hudson introduction article in Japanese, but hopefully after that, I'd be able to come back to the regular life. Sorry for those who have reported issues for Hudson that I'm not attending them in a timely fashion, but please give me one more week.

Hudson won a Duke's Choice Award

Posted by kohsuke on May 06, 2008 at 01:14 PM | Permalink | Comments (3)

This year in JavaOne, Hudson won Duke's Choice Award. Thank you very much for everyone for contributing/using Hudson!

P.S. Bill Pugh of findbugs dropped by the Hudson booth and told me that his Thursday talk "Using FindBugs in Anger" mentions Hudson. Nice!

Hudson booth at JavaOne

Posted by kohsuke on May 05, 2008 at 06:06 PM | Permalink | Comments (0)

This year, we got a small comfy booth dedicated just for Hudson, inside a section called Java playground (and I just finished setting up a booth.)

So if you are coming to JavaOne, please consider dropping by. I might not be at the booth all the time, but looking forward to seeing many of you.

Embeddable GlassFish v3 in Grails

Posted by kohsuke on May 02, 2008 at 07:49 AM | Permalink | Comments (0)

As another proof of concept for the embeddable GlassFish v3 that I discussed a few days ago, Vivek and I wrote a little addition to Grails so that you can use GlassFish v3 instead of Jetty (As Vivek noted, I was quite happy with the ease of integration.)

Vivek's posting has more details about internal working, but here's how you can use it:

  1. Pick up the latest *-overlay.zip from here. Extract this on top of your grails installation. I tested with 1.0.2.
  2. Whereas you normally run grails run-app, you can run grails run-app-gf

The bundle currently contains the full-blown GlassFish v3, so it's bigger than it should be. But thanks to Maven, it should be easy to come up with a smaller bundle.

But in the mean time, there are several obvious benefits. This will bring you the full-brown JavaEE capabilities to your Grails app (such as JMS, web services, etc., although those are not yet in the technology preview 2.) Developers can now also run the application on the same container that they'll use in the production system, too.

Hudson community updates

Posted by kohsuke on May 01, 2008 at 11:20 AM | Permalink | Comments (1)

Some of the recent developments in Hudson:

More SCM plugins

It's amazing how many SCMs the world has developed, and it's even more amazing to see so many people signing up to write SCM plugins for Hudson. Michael Donohue recently added BitKeeper plugin and URL SCM plugin (which is to check the timestamp of URL and if it's new it copies it over.) In the mean time, Nigel Magnay wrote a Git plugin and Jesse started hacking the Mercurial plugin more and more lately.

Google Gadget

Jeff Black wrote a Google Desktop gadest for Hudson, which lets you see the status of builds in your desktop. This is neat, and the same idea should be useful for other gadget technologies.

NetBeans

The auther of Hudson NetBeans plugin have moved on to the film industry (seriously), so I talked to him and moved the plugin code to Hudson Subversion repository. Jesse almost immediately started hacking, which is great.

Speaking of NetBeans, I was impressed with the latest Maven2 support in NetBeans 6.1 (the project team kindly sent me the latest bits for an experiment — I suspect this new version will be available for JavaOne.) For example, it lets you create a Hudson plugin from the new project wizard (through archetype), and it now also understands the repository index file from Nexus, so I don't have to manually find out the exact groupId/artifactId any more.

Hudson and Japanese community

Hudson is growing among Japanese, too. (I think it helped that I started blogging in Japanese as well.) Kenji Nakamura has expanded the maven2 job type so that a bunch of plugins like port allocator or Xvnc can be used with it. I also just saw a Mantis plugin contribution from Seiji Sogabe.

There'a distribution of Trac in Japan called "Trac Lightening" (which is very popular there due to its ease of installation and out-of-box experience), and this is switching to Hudson from Continuum.

The open source community in Japan is actually pretty strong, even though it might not be visible from here.

Hudson at JavaOne

Rama and I will be doing a technical session on Hudson Friday noon (TS-6547 "Improving the Engineering Process Through Automation by Hudson".) The talk includes overview of Hudson, two demos, discussion of best practices for bigger Hudson deployments, and the orb. So don't forget to add that to your schedule.

Looking forward to seeing you all in JavaOne.

GlassFish v3 just got embeddable

Posted by kohsuke on April 28, 2008 at 02:34 PM | Permalink | Comments (14)

Embeddable GFv3

I've always liked Jetty's ability to run inside an existing JVM, just as a library of another application. This enabled Jetty to be used in many situations, like mvn jetty:run for debugging a webapp without even running an application server on its own. IMO this contributed to a part of Jetty's usefulness. Almost every Maven documents today use Jetty for web app development, or doing site:run, etc.

So naturally, we wanted to do the same for GlassFish v3, and I'm happy to report that it got to the point that it holds the water. I mean, I can now run Hudson in this embedded GFv3.

Here's how it works — GlassFish v3 can be run as an OSGi appliation as Sahoo reported earlier, but in fact it can also be run without any kind of classloader isolation system at all. Sure, you won't get the isolations, but this means you can just drop a bunch of GFv3 jars in your classpath and run it like that.

So I added a little bit of API around that to make things pretty, and now you have the embeddable GFv3 API, which can be used like this:

GlassFish glassfish = new GlassFish();
// create smallest possible HTTP set up listening on port 8080
glassfish.minimallyConfigure(8080);

GFApplication app = glassfish.deploy(new File("path/to/simple.war"));

...

app.undeploy();
glassfish.stop();

Imagine the possibilities...

Thanks to the extensibility of GFv3, when you embed GFv3 in your JVM, you can plug into any of its extensibility points and tweak the behaviors in ways that you can't do with externally launched GFv3. You could also pick any flavor of GFv3 you want; if you just need the barebone servlet container and get smaller footprint, you can do that. But if you also need EJB functionality or some of our scripting offerings, that's cool with us, too. This is where we have an edge over Jetty, I think.

This is still a work in progress, but just imagine what we can do with this kind of stuff. How about testing your web services end-to-end without ever requring your developers to install GlassFish and set up database and all that. It also gives you interesting deployment options.

Oh, did I mention the start up time? One good thing about using a single classloader to load everything is that classloading overhead becomes much smaller. On my system, the server now starts in 300ms or so, complete with a deployment of a webapp. How many seconds does it take for your application server to start?

Maven-glassfish-plugin

I also wrote Maven glassfish plugin to wrap this as a Maven plugin. This allows you to do mvn glassfish:run to run the web appliation that you are developing on Maven (the equivalent of mvn jetty:run.)

I'm also thinking about quickly putting together Ant tasks.

Conclusions

170x93_Speaker_v4.gif

As I wrote, this is still a work in progress, but please tell us what you think about where we are going.

Finally, I'll be co-speaking more about this in the upcoming JavaOne technical session "TS-5921 GlassFish Project v3 as an Extensible Server Platform." If you are coming to JavaOne, I hope you'll come to the session.

Hudson plugin for WAR/EAR deployment / Cargo support in GlassFish

Posted by kohsuke on April 23, 2008 at 04:08 PM | Permalink | Comments (4)

I recently wrote a plugin for Hudson that deploys a WAR/EAR to the appliation container. The typical intended use of this is that when you build a WAR on Hudson, you'd like to deploy it before commencing certain kind of testing.

I wanted this to be able to work with as many containers as possible, so I turned to Cargo for abstracting away the difference in how you carry out a deployment. This is a very useful project indeed, and one of a kind (I wish APIs like this were a part of some relevant JavaEE JSRs, but oh well.)

Anyway, I've contributed embedded Tomcat support in the past to Cargo, so I knew the project rather well (and BTW that would be great for anyone who wants to run tests, as embedding a servlet container enables you to capture all the output in one place, instead of splitting this between the server and the client — debugging will be a lot easier, too.)

When I initially did the Tomcat work, I also wanted to do the GlassFish support. Unfortunately, after I did a partial work (you can check out the code by "svn co http://svn.codehaus.org/cargo-contrib/glassfish"), I had to move on to do other things, and I was never able to come back.

And now I really want this feature, and there's some partial code in there. Would there be anyone interested in picking up the work and bring it to the completion? I'm a Cargo committer, so I think I should be able to sponsor the integration into the main Cargo source tree. And more importantly, the Cargo team seems to be interested in getting this feature. It's just that the current code has the following problems:

  1. It needs to satisfy the strict checkstyle rules of the Cargo project. They are very picky about whether a whitespace goes between 'if' and '(', whether '{' is on a new line, and so on.
  2. Integrate Cargo test cases to include GlassFish, which AIU is a requirement for code in the main Cargo source tree.

The optional bonus credit would be to support the remote container mode of Cargo. I'm sure it's not too hard to do that.

This could be an excellent candidate for the GlassFish Award Program. See more about this issue in JIRA at CARGO-491.

I think my daughter is on the right track...

Posted by kohsuke on April 13, 2008 at 09:31 PM | Permalink | Comments (2)

She just turned three, and she ...

  1. recognizes the Java logo and say "Java!!"
  2. recognizes Duke and say "luke!!"
  3. recognizes Hudson logo and say "Hudson!!"
  4. likes singing the "Java java jing-jing-jing" song, although the only part she can really sing is the endless repeat of "java java jing-jing-jing."

Hmm... where did I make a mistake?

Hudson's SCM is just converted from CVS to Subversion

Posted by kohsuke on April 07, 2008 at 10:52 AM | Permalink | Comments (2)

When I first created Hudson on java.net, java.net only had CVS and no Subversion. Since then, java.net had added the Subversion support so that new projects can choose to use Subversion, but the existing projects didn't really have any viable migration path.

Some time after that, this got the attention of the java.net team, and a document has been written to explain how to migrate. Unfortunately, the catch is that you have to pay a fee to do this, and I couldn't convince myself to pay $500 (or so) myself to do this, so I left it there.

As Subversion steadily replaces CVS everywhere, however, it was just a matter of time before this issue gets revisited, and that happened a couple of weeks back. Tom Huybrechts brought this up again, and I replied that "yes, we tried, and we need to do some fund raising to do this." That was when a little miracle happened. See, collab.net is now apparently using Hudson, and one of their developers, Adam Ambrose, was in the Hudson dev list. He kindly talked about this inside Collab.net, and as a result, they graciously agreed to do the conversion for free for us. How nice of them!

So this morning, I was happy to find a new Subversion repository for Hudson. I still have to make adjustments to release scripts, but documents on Wiki are already updated to reflect this change. For those of you who have Hudson source code checked out, see this document for how to do it now,

I'm a rock star programmer!

Posted by kohsuke on April 07, 2008 at 09:10 AM | Permalink | Comments (0)

cover.jpg

First, apologies for a shameless plug. Ed Burns interviewed me when we were at Sun's internal conference (this was a rather nice place near Santa Cruz), and that interview became a part of his book "Secrets of the Rock Star Programmers: Riding the IT Crest." We talked about many different things, but I enjoyed the process. What's interesting is that sometimes I learn myself by what I'm saying to others, and this was no exception.

He just gave me a copy when I met him at the server-side Java symposium, so I haven't read the whole thing yet, but I find the book enjoyable, partly because I recognize many names in the book as they are mainly from the Java community.

I resurrected my Japanese blog

Posted by kohsuke on April 04, 2008 at 05:57 PM | Permalink | Comments (0)

As Hudson improves its presence in Japan, I decided to resurrect my old blog in Japanese that I haven't touched for a long time.

Many of the postings are the translation of the posts I made here, but there are some additional posts in there as replies to the reactions that my posting caused among Japanese bloggers.

Deep dive into assembly code from Java

Posted by kohsuke on March 30, 2008 at 10:10 PM | Permalink | Comments (30)

One of the things I learned in The Server Side Java Symposium 2008 was a command-line option to print out the assembly code that JIT is producing. Since I've always been interested in seeing the final assembly code that gets produced from your Java code, I decided to give it a test drive.

First the disclaimers:

  1. I'm not a performance expert.
  2. Don't try to take this too far, like optimizing your code against what you see here.

The option in question is only available in debug builds of JDKs. You can download one from here. The binary I tested is JDK6 u10 b14.

$ java -fullversion
java full version "1.6.0_10-beta-fastdebug-b14"

First, let's try something trivial:

public class Main {
    public static void main(String[] args) {
        for(int i=0; i<100; i++)
            foo();
    }

    private static void foo() {
        for(int i=0; i<100; i++)
            bar();
    }

    private static void bar() {
    }
}

I run this like "java -XX:+PrintOptoAssembly -server -cp . Main". The -XX:+PrintOptoAssembly is the magic option, and with this option I get the following, which shows the code of the "foo" method:

000   B1: #     N1 <- BLOCK HEAD IS JUNK   Freq: 100
000     pushq   rbp
        subq    rsp, #16        # Create frame
        nop     # nop for patch_verified_entry
006     addq    rsp, 16 # Destroy frame
        popq    rbp
        testl   rax, [rip + #offset_to_poll_page]       # Safepoint: poll for GC

011     ret

You see that the entire bar() function call and the loop was optimized away. So it must have inlined the bar() method, then unrolled the loop.

Now to something more interesting:

    private static byte[] foo() {
        byte[] buf = new byte[256];
        for( int i=0; i<buf.length; i++ )
            buf[i] = 0;
        return buf;
    }

This produces the following code:

000   B1: #     B15 B2 <- BLOCK HEAD IS JUNK   Freq: 78
000     # stack bang
        pushq   rbp
        subq    rsp, #80        # Create frame
00c     # TLS is in R15
00c     movq    R8, [R15 + #120 (8-bit)]        # ptr
010     movq    R10, R8 # spill
013     addq    R10, #280       # ptr
01a     cmpq    R10, [R15 + #136 (32-bit)]      # raw ptr
021     jge,u   B15  P=0.000100 C=-1.000000
021
027   B2: #     B3 <- B1  Freq: 77.9922
027     movq    [R15 + #120 (8-bit)], R10       # ptr
02b     PREFETCHNTA [R10 + #256 (32-bit)]       # Prefetch to non-temporal cache for write
033     movq    [R8], 0x0000000000000001        # ptr
03a     PREFETCHNTA [R10 + #320 (32-bit)]       # Prefetch to non-temporal cache for write
042     movq    RDI, R8 # spill
045     addq    RDI, #24        # ptr
049     PREFETCHNTA [R10 + #384 (32-bit)]       # Prefetch to non-temporal cache for write
051     movl    RCX, #32        # long (unsigned 32-bit)
056     movq    R10, precise klass [B: 0x00002aaaab076708:Constant:exact *      # ptr
060     movq    [R8 + #8 (8-bit)], R10  # ptr
064     movl    [R8 + #16 (8-bit)], #256        # int
06c     xorl    rax, rax        # ClearArray:
        rep stosq       # Store rax to *rdi++ while rcx--
071
071   B3: #     B4 <- B16 B2  Freq: 78
071   
071     # checkcastPP of R8
071     xorl    R10, R10        # int
074     movl    R9, #256        # int
        nop     # 2 bytes pad for loops and calls

07c   B4: #     B17 B5 <- B3 B5         Loop: B4-B5 inner stride: not constant pre of N153 Freq: 19850.2
07c     cmpl    R10, #256       # unsigned
083     jge,u   B17  P=0.000001 C=-1.000000
083
089   B5: #     B4 B6 <- B4  Freq: 19850.2
089     movslq  R11, R10        # i2l
08c     movb    [R8 + #24 + R11], #0    # byte
092     incl    R10     # int
095     cmpl    R10, #8
099     jlt,s   B4  P=0.996072 C=22313.000000
099
09b   B6: #     B11 B7 <- B5  Freq: 77.9799
09b     subl    R9, R10 # int
09e     andl    R9, #-16        # int
0a2     addl    R9, R10 # int
0a5     cmpl    R10, R9
0a8     jge,s   B11  P=0.500000 C=-1.000000
0a8
0aa   B7: #     B8 <- B6  Freq: 38.9899
0aa     PXOR  XMM0,XMM0 ! replicate8B
        nop     # 2 bytes pad for loops and calls

0b0   B8: #     B10 B9 <- B7 B9         Loop: B8-B9 inner stride: not constant main of N85 Freq: 9925.09
0b0     movslq  R11, R10        # i2l
0b3     MOVQ  [R8 + #24 + R11],XMM0     ! packed8B
0ba     movl    R11, R10        # spill
0bd     addl    R11, #16        # int
0c1     movslq  R10, R10        # i2l
0c4     MOVQ  [R8 + #32 + R10],XMM0     ! packed8B
0cb     cmpl    R11, R9
0ce     jge,s   B10  P=0.003928 C=22313.000000
0ce
0d0   B9: #     B8 <- B8  Freq: 9886.1
0d0     movl    R10, R11        # spill
0d3     jmp,s   B8
0d3
0d5   B10: #    B11 <- B8  Freq: 38.9899
0d5     movl    R10, R11        # spill
0d5
0d8   B11: #    B14 B12 <- B6 B10  Freq: 77.9799
0d8     cmpl    R10, #256
0df     jge,s   B14  P=0.500000 C=-1.000000
        nop     # 3 bytes pad for loops and calls

0e4   B12: #    B17 B13 <- B11 B13      Loop: B12-B13 inner stride: not constant post of N153 Freq: 9922.54
0e4     cmpl    R10, #256       # unsigned
0eb     jge,us  B17  P=0.000001 C=-1.000000
0eb
0ed   B13: #    B12 B14 <- B12  Freq: 9922.53
0ed     movslq  R11, R10        # i2l
0f0     movb    [R8 + #24 + R11], #0    # byte
0f6     incl    R10     # int
0f9     cmpl    R10, #256
100     jlt,s   B12  P=0.996072 C=22313.000000
100
102   B14: #    N1 <- B13 B11  Freq: 77.9698
102     movq    RAX, R8 # spill
105     addq    rsp, 80 # Destroy frame
        popq    rbp
        testl   rax, [rip + #offset_to_poll_page]       # Safepoint: poll for GC

110     ret
110
111   B15: #    B18 B16 <- B1  Freq: 0.00780129
111     movq    RSI, precise klass [B: 0x00002aaaab076708:Constant:exact *      # ptr
11b     movl    RDX, #256       # int
120     nop     # 3 bytes pad for loops and calls
123     call,static  wrapper for: _new_array_Java
        # Main::foo @ bci:3  L[0]=_ L[1]=_
        # 
128
128   B16: #    B3 <- B15  Freq: 0.00780114
        # Block is sole successor of call
128     movq    R8, RAX # spill
12b     jmp     B3
12b
130   B17: #    N1 <- B12 B4  Freq: 1e-06
130     movl    RSI, #-28       # int
135     movq    RBP, R8 # spill
138     movl    [rsp + #0], R10 # spill
13c     nop     # 3 bytes pad for loops and calls
13f     call,static  wrapper for: uncommon_trap(reason='range_check' action='make_not_entrant')
        # Main::foo @ bci:17  L[0]=RBP L[1]=rsp + #0 STK[0]=RBP STK[1]=rsp + #0 STK[2]=#0
        # AllocatedObj(0x0000000040c31880)

144     int3    # ShouldNotReachHere
144
151   B18: #    N1 <- B15  Freq: 7.80129e-08
151     # exception oop is in rax; no code emitted
151     movq    RSI, RAX        # spill
154     addq    rsp, 80 # Destroy frame
        popq    rbp

159     jmp     rethrow_stub

Just to recap, R8-R15 are additional general-purpose 64bit registers new in the amd64.

The first part (00c-027) is allocating an array, and this is already interesting. As the comment indicates, R15 is apparently used as a pointer to a thread-local storage of the current thread, and R15[120] is the pointer to the head of the heap sub-space dedicated for this thread.

So the byte[] is allocated from this thread-local space by simply reserving 256+32 byte space. If there's not enough space (the limit is set at R15[136]), then it uses the slower allocation code at B15 — this code must involve in reserving a new chunk from the eden space and allocate a new object there.

Once the pointer to the new array is set to R8 at 00c, the initialization follows (033-071.) The first 24 bytes of the newly allocated space is used for metadata (the first 8 byte is probably lock or GC-related, followed by a pointer to the class object, then another 8 bytes for the size of the array.) 06c zero-clears the array. In theory the zero-clear shouldn't have been necessary, as we are then filling the array to zero again, but JIT failed to take advantage of that.

But note that the zero-clear is done by 8 bytes at a time, so it did recognize that the array size is multiple of 8.

I don't quite understand what those prefetch instructions (at 02b, 03a, and 049) are meant for. Presumably they are to make sure that the next time an object allocation happens, that part of the memory is in cache, but why 256, 320, and 384? Does anyone have a clue?

Now as of 074, R8 is the pointer to 'buf' and R9 is the length of the array. Note that JIT knows that buf.length is always 256 here, so this is movl R9,256 and not movl R9,[R8+16]. Also note that this computation is outside the for loop. So this tells us that there's no need to explicitly assign the array length to a temporary variable in a tight loop, because JIT does the equivalent anyway:

int len = buf.length;
for( int i=0; i<len; i++ )
  ...

Similarly there's no need to reverse the direction of the loop to avoid buf.length computation.

The way the loop is compiled is very interesting. First there's the 'warm up' part (07c-099) that presumably does the array filling until it reaches the 8-byte boundary, then the 'fast loop' portion (09b-0d3) that zero-fills 8 bytes per loop by using an MMX register, then the final 'cool down' part (0d5-100) that handles the last remaining part that doesn't fit 8 byte boundary. In this case, in theory it could have figured out that the whole thing nicely fits 8-byte boundary, so the warm up and cool down was unnecessary, but it appears that JIT didn't realize this.

I don't know what kind of computation happens behind the scene here, but overall this loop unrolling is rather clever. The original code was byte-by-byte assignment to 0, but in the final code, one loop iteration clears 8 byte at a time.

I also noticed that there's no array boundary check in the fast loop portion, which is nice.

OK, most of you have hopefully heard that in JDK6 they do lock coarsening and lock elision. So let's see that in action.

For that, I compiled the following code and executed in the same fashion:

    private static void foo() {
        Vector v = new Vector();
        v.add("abc");
        v.add("def");
        v.add("ghi");
    }

This gives me the following:

000   B1: #     B10 B2 <- BLOCK HEAD IS JUNK   Freq: 20168
000     # stack bang
        pushq   rbp
        subq    rsp, #80        # Create frame
00c     # TLS is in R15
00c     movq    RAX, [R15 + #120 (8-bit)]       # ptr
010     movq    R10, RAX        # spill
013     addq    R10, #40        # ptr
017     # TLS is in R15
017     cmpq    R10, [R15 + #136 (32-bit)]      # raw ptr
01e     jge,u   B10  P=0.000100 C=-1.000000
01e
024   B2: #     B3 <- B1  Freq: 20166
024     # TLS is in R15
024     movq    [R15 + #120 (8-bit)], R10       # ptr
028     PREFETCHNTA [R10 + #256 (32-bit)]       # Prefetch to non-temporal cache for write
030     movq    R10, precise klass java/util/Vector: 0x00002aaaf2649f38:Constant:exact *        # ptr
03a     movq    R11, [R10 + #176 (32-bit)]      # ptr
041     movq    [RAX], R11      # ptr
044     movq    [RAX + #8 (8-bit)], R10 # ptr
048     movq    [RAX + #16 (8-bit)], #0 # long
050     movq    [RAX + #24 (8-bit)], #0 # long
058     movq    [RAX + #32 (8-bit)], #0 # long
058
060   B3: #     B12 B4 <- B11 B2  Freq: 20168
060     
060     movq    RBP, RAX        # spill
063     # checkcastPP of RBP
063     # TLS is in R15
063     movq    R11, [R15 + #120 (8-bit)]       # ptr
067     movq    R10, R11        # spill
06a     addq    R10, #104       # ptr
06e     # TLS is in R15
06e     cmpq    R10, [R15 + #136 (32-bit)]      # raw ptr
075     jge,u   B12  P=0.000100 C=-1.000000
075
07b   B4: #     B5 <- B3  Freq: 20166
07b     # TLS is in R15
07b     movq    [R15 + #120 (8-bit)], R10       # ptr
07f     PREFETCHNTA [R10 + #256 (32-bit)]       # Prefetch to non-temporal cache for write
087     movq    [R11], 0x0000000000000001       # ptr
08e     PREFETCHNTA [R10 + #320 (32-bit)]       # Prefetch to non-temporal cache for write
096     movq    RDI, R11        # spill
099     addq    RDI, #24        # ptr
09d     PREFETCHNTA [R10 + #384 (32-bit)]       # Prefetch to non-temporal cache for write
0a5     movq    R10, precise klass [Ljava/lang/Object;: 0x00002aaaf264e928:Constant:exact *     # ptr
0af     movq    [R11 + #8 (8-bit)], R10 # ptr
0b3     movl    [R11 + #16 (8-bit)], #10        # int
0bb     movl    RCX, #10        # long (unsigned 32-bit)
0c0     xorl    rax, rax        # ClearArray:
        rep stosq       # Store rax to *rdi++ while rcx--
0c5
0c5   B5: #     B16 B6 <- B13 B4  Freq: 20168
0c5     
0c5     # checkcastPP of R11
0c5     movq    [RBP + #32 (8-bit)], R11        # ptr ! Field java/util/Vector.elementData
0c9     movq    R10, RBP        # ptr -> long
0cc     shrq    R10, #9
0d0     movq    RDX, java/lang/String:exact *   # ptr
0da     movq    R11, 0x00002a959c9da580 # ptr
0e4     movb    [R11 + R10], #0 # byte
0e9     movq    RSI, RBP        # spill
0ec     nop     # 3 bytes pad for loops and calls
0ef     call,static  java.util.Vector::add
        # Main::foo @ bci:11  L[0]=RBP
        # AllocatedObj(0x0000000040b30680)

0f4
0f4   B6: #     B15 B7 <- B5  Freq: 20167.6
        # Block is sole successor of call
0f4     movq    RDX, java/lang/String:exact *   # ptr
0fe     movq    RSI, RBP        # spill
101     nop     # 2 bytes pad for loops and calls
103     call,static  java.util.Vector::add
        # Main::foo @ bci:18  L[0]=RBP
        # AllocatedObj(0x0000000040b30680)

108
108   B7: #     B14 B8 <- B6  Freq: 20167.2
        # Block is sole successor of call
108     movq    RDX, java/lang/String:exact *   # ptr
112     movq    RSI, RBP        # spill
115     nop     # 2 bytes pad for loops and calls
117     call,static  java.util.Vector::add
        # Main::foo @ bci:25  L[0]=_
        # 
11c
11c   B8: #     N1 <- B7  Freq: 20166.8
        # Block is sole successor of call
11c     addq    rsp, 80 # Destroy frame
        popq    rbp
        testl   rax, [rip + #offset_to_poll_page]       # Safepoint: poll for GC
        
127     ret

(slow path omitted)

The allocation of a Vector object (00c-058) is almost identical to the array allocation code we've seen before (except the additional field initializations at 048-058.) The array allocation for Vector.elementData follows (060-0C0.)

Note that the Vector constructors are defined in highly nested fashion like this:

    public Vector(int initialCapacity, int capacityIncrement) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }

    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }

    public Vector() {
        this(10);
    }

... but the whole thing is inlined, so the end result is just as fast as the following code. This is great.

    public Vector() {
        this.elementData = new Object[10];
        this.capacityIncrement = 0;
    }

But wait, after that, you see that there's three call instructions for Vector.add. So there's no lock elision nor lock coarsening, despite the fact that this Vector object never escapes the stack.

I thought perhaps that's because Vector.add is too complex to be inlined, so I tried the following code, in the hope of seeing the lock elision:

    private static void foo() {
        Foo foo = new Foo();
        foo.inc();
        foo.inc();
        foo.inc();
    }

    private static final class Foo {
        int i=0;

        public synchronized void inc() {
            i++;
        }
    }
This produced the following code:
000   B1: #     B6 B2 <- BLOCK HEAD IS JUNK   Freq: 19972
000     # stack bang
        pushq   rbp
        subq    rsp, #80        # Create frame
00c     # TLS is in R15
00c     movq    RBP, [R15 + #120 (8-bit)]       # ptr
010     movq    R10, RBP        # spill
013     addq    R10, #24        # ptr
017     cmpq    R10, [R15 + #136 (32-bit)]      # raw ptr
01e     jge,u   B6  P=0.000100 C=-1.000000
01e
024   B2: #     B3 <- B1  Freq: 19970
024     movq    [R15 + #120 (8-bit)], R10       # ptr
028     PREFETCHNTA [R10 + #256 (32-bit)]       # Prefetch to non-temporal cache for write
030     movq    R10, precise klass Main$Foo: 0x00002aaaf2646e58:Constant:exact *        # ptr
03a     movq    R11, [R10 + #176 (32-bit)]      # ptr
041     movq    [RBP], R11      # ptr
045     movq    [RBP + #8 (8-bit)], R10 # ptr
049     movq    [RBP + #16 (8-bit)], #0 # long
049
051   B3: #     B8 B4 <- B7 B2  Freq: 19972
051     
051     # checkcastPP of RBP
051     leaq    R11, [rsp + #64]        # box lock
056     fastlock RBP,R11,RAX,R10
135     jne     B8  P=0.000001 C=-1.000000
135
13b   B4: #     B9 B5 <- B8 B3  Freq: 19972
13b     MEMBAR-acquire (prior CMPXCHG in FastLock so empty encoding)
13b     movl    R11, [RBP + #16 (8-bit)]        # int ! Field Main$Foo.i
13f     incl    R11     # int
142     movl    [RBP + #16 (8-bit)], R11        # int ! Field Main$Foo.i
146     MEMBAR-release
146     MEMBAR-acquire
146     movl    R11, [RBP + #16 (8-bit)]        # int ! Field Main$Foo.i
14a     incl    R11     # int
14d     movl    [RBP + #16 (8-bit)], R11        # int ! Field Main$Foo.i
151     MEMBAR-release
151     MEMBAR-acquire
151     movl    R11, [RBP + #16 (8-bit)]        # int ! Field Main$Foo.i
155     incl    R11     # int
158     movl    [RBP + #16 (8-bit)], R11        # int ! Field Main$Foo.i
15c     MEMBAR-release (a FastUnlock follows so empty encoding)
15c     leaq    RAX, [rsp + #64]        # box lock
161     fastunlock RBP, RAX, R10
218     jne,s   B9  P=0.000001 C=-1.000000
218
21a   B5: #     N1 <- B9 B4  Freq: 19972
21a     addq    rsp, 80 # Destroy frame
        popq    rbp
        testl   rax, [rip + #offset_to_poll_page]       # Safepoint: poll for GC
        
225     ret

(slow path omitted)

We are all familar with the memory allocation by now, so we can skip that.

The 'fastlock' pseudo-instruction (AFAIK there's no such operation in amd64, and a single machine code can't possibly occupy 223 bytes!) must be the lock code. Here you see that the lock coarsening has indeed happened (yay!), and three increments happen in a single block (MEMBAR-acquire/release must be another pseudo-instruction, which became no-op in this scenario — see that the length of those instructions are 0).

Note that JVM still fails to eliminate a lock here, despite the fact that this object doesn't escape the stack. I tried various things to see the effect of escape analysis and lock elision kick in, but couldn't find a way to do it. It looks like this feature is not quite in JDK yet, although it's equally possible that I'm doing something stupid.

Also note that presumably because of the memory barrier associated with this, each increments write back to memory. This is unfortunate because in theory three increments could have been combined into one, given the the lock was coarsened.

Indeed if I remove the 'synchronized' keyword, I get the following substantially simpler version:

000   B1: #     B4 B2 <- BLOCK HEAD IS JUNK   Freq: 27066
000     # stack bang
        pushq   rbp
        subq    rsp, #16        # Create frame
00c     # TLS is in R15
00c     movq    RAX, [R15 + #120 (8-bit)]       # ptr
010     movq    R10, RAX        # spill
013     addq    R10, #24        # ptr
017     cmpq    R10, [R15 + #136 (32-bit)]      # raw ptr
01e     jge,us  B4  P=0.000100 C=-1.000000
01e
020   B2: #     B3 <- B1  Freq: 27063.3
020     movq    [R15 + #120 (8-bit)], R10       # ptr
024     PREFETCHNTA [R10 + #256 (32-bit)]       # Prefetch to non-temporal cache for write
02c     movq    R10, precise klass Main$Foo: 0x00002aaaf25dfbc8:Constant:exact *        # ptr
036     movq    R11, [R10 + #176 (32-bit)]      # ptr
03d     movq    [RAX], R11      # ptr
040     movq    [RAX + #8 (8-bit)], R10 # ptr
040
044   B3: #     N1 <- B5 B2  Freq: 27066
044     movq    [RAX + #16 (8-bit)], #3 # long
04c     
04c     addq    rsp, 16 # Destroy frame
        popq    rbp
        testl   rax, [rip + #offset_to_poll_page]       # Safepoint: poll for GC
        
057     ret

(slow path omitted)

So not only three inc()s but the initializer also got collapsed into single "movq rax[16],3" call. Wow!

All in all, modern JVMs seem pretty good at generating optimal code. In various situations, the resulting assembly code is far from the straight-forward instruction-by-instruction translation. OTOH, the escape analysis doesn't really seem to do anything useful yet.

This was a long post, but I hope you enjoyed this as much as I did.

Introducing java.net SCM/issue tracker cross-linking daemon

Posted by kohsuke on March 29, 2008 at 12:02 PM | Permalink | Comments (2)

I've further updated the java.net tasks library so that I can programatically interact with the java.net issue tracker, then build a new daemon on it.

This new daemon, scm-issue-link, monitors SCM commits. If commit messages contain references to issues, the daemon then updates those issues, creating back-links to the SCM commits. A similar feature can be seen in other issue trackers, like Trac or JIRA. This makes it easier for developers to later go back the project history and determine what changes are made for a particular issue. With this daemon, this feature is finally available for any java.net projects.

To use this, first add the daemon user (scm_issue_link) as a developer on your project. This tells daemon that you'd like him to update issues in your project. The daemon has to periodically poll java.net to see what project he's invited on, so it will take up to an hour for this initial phase to complete.

Once the daemon is hooked, simply reference issues when you make commits. The daemon understands several syntaxes, like "issue #50" "Issue 100" or "HUDSON-123". Within a few minutes, the daemon will update issues with back references.

See a sample issue updated by this daemon, and if you have any suggestions/ideas about how the daemon can be improved, let me know. If you are interested in hacking the daemon, that is welcome, too!

Kohsuke at The Server-Side Java Symposium

Posted by kohsuke on March 25, 2008 at 11:04 PM | Permalink | Comments (0)

This week I'm in Las Vegas to give a talk in TSSJS about how to build fast and scalable web services in Metro. If you are also in town and interested in meeting up, let me know!

(or alternatively, you can just watch out for a guy in Hudson T-shirt...)

Hudson hits 1.200

Posted by kohsuke on March 24, 2008 at 10:45 AM | Permalink | Comments (14)

Hudson finally hit 1.200 last Friday. It's not 1.2 nor 1.2.0.0 — it's 1.200, the 201st release of Hudson since it's 1.0 release, which was a little over 3 years ago.

A few weeks back when I knew that it's approaching 200 mark, I briefly thought about giving it a longer soak time to make it a very stable release, or perhaps adding some major RFE in this release. Or even call it 2.0. But in the end I just kept doing what I've been doing all along, and next thing I knew it was already 1.200!

I'd like to take this opportunity to thank all the contributers and users for making Hudson better. Hudson came a long way, but it still got a long way to go, too. So I hope you guys will stick around.

Birthday_candles.jpg


Fast-open IntelliJ plugin updated

Posted by kohsuke on March 20, 2008 at 10:44 PM | Permalink | Comments (7)

I upgraded to IntelliJ IDEA 7.0.3 recently, and so I took some time to enhance my plugins to scratch my itches.

The "major" improvement is in the fast-open IntelliJ plugin. Being a CLI guy who hates using mouse (and since I work on so many projects), the main idea of this plugin was how to open projects in IDE efficiently.

In the earlier version, I was doing this by clipboard. First I run a shell script to send the directory/file name into clipboard, then I go to IDE and type Alt+F,F to open it. While this works nicely and indeed a tremendous time-saver over the conventional "open project" dialog, it's still a two-step process.

In the new version, the plugin listens to a TCP port and just by sending directory/file name to this port, IDEA will open the project. So I now only need to run a shell script and boom, IDEA is ready to work!

I also enhanced the plugin so that it can properly open pom.xml. The plugin can be installed from IDEA's plugin center.

Tips on creating a small JNI jar on Windows

Posted by kohsuke on March 19, 2008 at 11:25 AM | Permalink | Comments (2)

I created a small library to manipulate Windows processes the other day, and someone showed interest in how to create a small footprint DLL in Windows. So this blog is to explain you what I did.

The main idea behind cutting down the size of DLL is to avoid linking the Visual C++ runtime. In many cases, you'd only use JNI to talk to other DLLs (most often to kernel32.dll or ntdll.dll — IOW, system calls), and in those situations, this is quite feasible.

For this to work, you'll do the following:

  1. Add /NODEFAULTLIB to the linker option, indicating you don't want to link to the C runtime.
  2. Add /GS- /GX- and /GR- as these C++ features rely on the C runtime.
  3. Don't use any of the functions that are exposed by the C runtime. I have yet to figure out how to mechanically determine exactly what's in C-runtime and what's not, but generally speaking all-lower case function calls like strcpy, malloc or memcpy are out (but then, Kernel exposes some all-lower case functions, like lstrcpy so it's really case-by-case.) All C++ features, like runtime exception handling, runtime type information, and the new/delete operators are gone, too.
  4. Add _DllMainCRTStartup instead of DllMain, so that you don't rely on the start-up code in C-runtime. See the following:
    extern "C" BOOL WINAPI _DllMainCRTStartup(HANDLE  hDllHandle, DWORD   dwReason, LPVOID  lpreserved) {
        return TRUE;
    }
    

That's it. If the linker runs successfully, you should see a substantially smaller DLL. When you accidentally end up using functions from the C runtime library, the linker often fails with an error saying "unresolved symbol: _main". When that happens, you need to turn on the /VERBOSE linker option and see which dependency is causing the C runtime to kick in.

Kernel does offer the basic string manipulation libraries, so those are useful when you don't have the C runtime. Ditto for memory allocation functions, but those are often somewhat awkward.

I've also read somewhere that you can pack the PE header by using the smaller alignment, but I couldn't figure out how to convince the linker to do that, or whether that is really legal or not.

In conclusion, if you are using the C runtime, then you should seriously question if some 20KB improvement in the footprint is worth your productivity drop, but if you are not using the C runtime, these relatively mechanical steps would give you the satisfaction of a compact DLL (plus the satisfaction of getting one step close to the inner working on Windows.)

Introducing Winp: Windows Process Manipulation Library

Posted by kohsuke on March 16, 2008 at 06:23 PM | Permalink | Comments (7)

I wrote a library called winp that lets you do lower-level process managements on Windows. So far it includes things like killing random processes (not just the ones you launched), killing a process recursively, or finding out the environment variables and command line arguments given to the process.

My short-term goal is to use this in Hudson so that I can clean up the run-away processes (which are often left by jobs that deal with daemons like application servers), but I thought this kind of features would hopefully be useful for other applications.

In this library, I created a DLL without linking to the C runtime library, which helps me keep the DLL file size rather small (currently around 6K.) To use this library, just put winp.jar in your classpath — As with all my other projects that deal with JNI (like com4j), there's no need to deal with a DLL separately.

Hudson is now a part of FreeBSD ports

Posted by kohsuke on March 14, 2008 at 11:16 PM | Permalink | Comments (0)

Cactusman reports in Japanese that Hudson just became a part of the FreeBSD ports collection. I don't know much about FreeBSD, but AIU this means FreeBSD users can install Hudson even more easily. The version of Hudson in FreeBSD is 1.190,

Hudson is also available as a part of OpenSUSE packages, and one person volunteered and is working on making it available in OpenSolaris.

Hudson for GlassFish is finally online

Posted by kohsuke on March 12, 2008 at 11:09 AM | Permalink | Comments (3)

Dinesh and I set up a publicly-accessible Hudson at http://glassfishbuildtools.sun.com/ (I think we need to get a better alias, like http://hudson.glassfish.org/. For developers and contributers, you can come see how/when your changes are incorporated into builds, and for users, you can come pick up the bleeding edge bits for your bug fixes.

All the builds are happening inside our firewall (and we have a build/test cluster of more than two dozen machines, so it's hard to move outside, too), and it uses yet-to-be-released Hudson build publisher plugin, which contributed by JBoss.

If you know of projects that we should be posting there, please let us know.

Announcing java.net issue police

Posted by kohsuke on March 06, 2008 at 10:22 AM | Permalink | Comments (6)

One of the common problems of java.net project owners is the e-mails that people send to issues@yourproject.dev.java.net. This list is normally reserved for the automatic notifications from the issue tracker (thus it gets lower level of attention), but mistaken users often think this is the list to report any problems, and send in e-mails directly.

The "issue police" daemon monitors the issues list, and if it finds such an e-mail, it will politely remind the user that the issues list is for notifications only. It's very easy to use, too — just add issue_police@dev.java.net as a subcriber to your issues list.

(And while I have your attention, if you run java.net projects, you'll probably find my other projects like autoresponder, role manager, java.net tasks, and logger useful.)

Progress on Hudson's i18n/l10n

Posted by kohsuke on March 03, 2008 at 09:47 AM | Permalink | Comments (0)

A few months ago, I blogged about the enhancement in Hudson to enable i18n. The progress since then has been rather amazing.

Japanese was the first language to be added to Hudson, thanks to a new committer "cactusman". Now, this work really actually has two parts in it — first to properly i18n the code, and then to add locale-specific data (AKA l10n.) Since he was the first to work on this area, he had to do i18n as well as Japanese l10n. So he had done more work than other l10n contributers. If you are Japanese, Cactusman has posted his talk about Hudson on his blog. His perspective on becoming a contributer in that talk is worth a watch.

The next language on board was French, done by Eric Lefevre. Eric has been also doing some evangelism too.

Then Simon Wiest joined and he started doing German translation. I just saw another big drop from him, and it appears to me that perhaps he has completed the translation of the entire Hudson core.

Another person recently joined the development team and he said he's going to do Brazilian Portuguese.

On top of the Maven plugin to assist l10n, I wrote an IntelliJ IDEA plugin to assist i18n. I'm actually wondering if we can do more to simplify l10n — like perhaps a plugin that talks to Google translate and suggest that as the default option, or perhaps a plugin that looks at other message files to find related translations, because the same word tends to appear in many places.

Anyway, even today the whole process is (I think) well documented in Wiki, so if you like Hudson and is willing to contribute, please join the development team!

Introducing java.net issue tracker stats

Posted by kohsuke on March 02, 2008 at 08:41 PM | Permalink | Comments (6)

For various reasons, I haven't touched my java.net tasks project that much lately, but I'm thinking about putting a bit more effort in doing more java.net automation, especially around issue tracker. Toward that, I wrote a tool to generate graphs out of the issue tracker — I'm calling it "issue tracker stats".

The tool is probably more useful for the management to get insights into projects, but just looking at them is rather fun and revealing.

For example, the following picture shows the number of resolved issues (green) and the number of open issues (red) over time in the glassfish project. What's truly noteworthy here is that the team managed to actually reduce the number of issues during early last year. This is rather unusual, because it's normaly for a project to receive certain amount of long-term RFEs/bugs that cannot be fixed right away.

The following picture looks at the same information in slightly different way. Instead of plotting the total number of issues in Y-axis, this graph shows "incoming" issues (newly filed and reopened issues, red) and "outgoing" issues (resolved issues, green) per month, over time.

You can see the correlation between this graph and the graph above and infer a lot. For example, the dent in the number of open issues is now visible as the region where the green bar goes above red bars, but now you see that that time period is preceeded by a surge of bug reports (Aug,2006-Jan,2007), so perhaps what happened was that the team went off to do feature developments, and then decided to come back to the bugs later.

A quick check of the Aquarium reveals that GlassFish v2 released September 2007, and if you look at the graph with that knowlege, you can now see that the team has been in the "bug scrub" mode for almost 3 quarters. The number of incoming issues didn't jump after the release, so that probably suggests that the quality of the code was good (or maybe people aren't trying newly released v2.)

Yet another way to look at the information is to see what happens to issues after they are reported, which is what the next graph is all about.

The graph is again taken from the glassfish project, and it shows what happened to issues X days after the report. For example, the yellow region hits 50% at around 15 days and green hits 50% around 45 days, so you can tell that by 2 weeks, 50% of all the reported issues get started, and within 6 weeks about half of the issues are resolved.

You can also see that more than 15% of the issues aren't even confirmed after 100 days — that is, many issues aren't even getting evaluated.

Comparing these graphs between projects is also very interesting. Here is the same created/resolved graph from my Hudson project:

Now you can see that resolved issues are always outnumbered significantly by incoming bugs/RFEs. This clearly shows that the project is getting more feedback than it can keep up with, so it probably need more developers.

The following graph is the expected longevity graph from Hudson. Contrast that with glassfish, and you can tell a lot. First, initial declines are a lot steeper (note that the scale of X-axis is different) — in fact 50% of the issues get attended within 3 days, and most of the issues, if they ever get fixed, get fixed within 2 weeks. So the turn-around time is lot faster compared To glassfish, although more than one third of the total issues remain in the issue tracker.

Or if you look at the following graph, which is taken from Metro, you can see the declining incoming issues, which is hopefully a sign of increased stability in the code. You can also see that the bug fixes are spikey, probably indicating the release cycles of the development team.

The tool itself is available on java.net, so try it on your favorite projects. If you have more ideas about the kind of statistics that are useful, let me know (or better yet, join the project as a developer.)

"Emotional Hudson" plugin

Posted by kohsuke on February 26, 2008 at 11:05 AM | Permalink | Comments (4)

In his recent blog, he announced a plugin that changes the facial expression of Hudson the butler depending on how your builds are going.

As you can see in his screenshot below, if tests fail, Mr.Hudson gets disappointed, and if a build fails, he becomes visibly frustrated.

Its practicality aside, I thought this is pretty funny.

May 2008
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31


Search this blog:
  

Categories
Community
Community: Global Education and Learning Community
Community: Java Communications
Community: Java Enterprise
Community: Java Tools
Community: Java User Groups
Community: Java Web Services and XML
Community: JavaDesktop
Community: JDK
J2SE
Archives

May 2008
April 2008
March 2008
February 2008
January 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
March 2006
February 2006
January 2006
December 2005
November 2005
October 2005
September 2005
August 2005
July 2005
June 2005
May 2005
April 2005
March 2005
February 2005
January 2005

Recent Entries

JAXB RI 2.1.7 releas

My JavaOne highlights

Hudson won a Duke's Choice Award



Powered by
Movable Type 3.01D


 Feed java.net RSS Feeds