Thursday, July 17, 2008

Keep terminated launch consoles in Eclipse

This one bugs me every time I upgrade to a new release of Eclipse. I often like to look back at the consoles from main methods/junits/tomcat launches I've terminated. I guess I expect to find a preference right in the context menu preferences for the Console view, or for it to match "terminated" in the prefs search - no luck.

So that I never have to search for this again, this preference can be found under
Run/Debug => Launching => "Remove terminated launches when a new launch is created"

Wednesday, July 16, 2008

Wednesday Afternoon Reflections

What does this print?

import java.lang.reflect.Method;

interface IMessage {
public CharSequence getMessage();
}

public class SayWhat implements IMessage {
public String getMessage() {
return "Hi!";
}

public static void main(String[] _args) {
try {
SayWhat instance = new SayWhat();
for (Method m : SayWhat.class.getMethods()) {
if (m.getName().equals("getMessage")) {
System.out.println("SayWhat.main: " + m.invoke(instance));
}
}
}
catch (Throwable e) {
System.out.println("SayWhat.main: " + e);
}
}
}

The answer is
Hi!
Hi!

That's right, the getMessage method appears twice in SayWhat.class.getMethods(). The key here is that the SayWhat class is overriding IMessage with a covariant return type (CharSequence vs. String). Looks like the compiler makes covariant return types possible by creating a behind-the-scenes "synthetic" implementation of getMessage which just calls through to my implementation.

If you find yourself looping through a list of Method objects looking for a match, be sure to check isSynthetic on each Method.

Monday, March 24, 2008

Download sources for javax libraries

If you are lucky like me, you often find yourself wanting to see the source code for various javax libraries (JavaMail, JSTL, EL, etc). My success in tracking down the complete zips of these sources varied from project to project. Today, I stumbled across this:
http://download.java.net/maven/1/

This looks like a good starting place if you are after javax sources or jars.

Tuesday, March 11, 2008

ical4j, JavaMail, Exchange and Outlook secret sauce

Last summer, I was tasked with sending out Outlook "meeting invites" for events in our product. This meant integrating with iCal4j. iCal4j itself has a truly bizarre API, but it wasn't bad to create a new invite, as there are so many examples out there. What really stumped me for a while was getting the headers right so that Outlook would pop up an "Accept/Decline" dialog for automatically adding the event to their calendar. I should have blogged about this last summer, as I've now forgotten what I finally did to make it work - I believe the real coup was getting the headers right. I remember finding lots of people with similar problems, so I wanted to get this code out there.

This code requires iCal4j and JavaMail 1.4. I believe the JavaMail 1.4-only ByteArrayDataSource is crucial for getting this to work. This code might not even work for you, I just wanted to get it out there as a starting point.

public MimeMessage createMimeMessage(Session _javaMailsession) throws Exception {
MimeMessage mimeMessage = new MimeMessage(_javaMailsession);
mimeMessage.setSubject(getSubject());
mimeMessage.addFrom(getFromInternetAddresses());
mimeMessage.addRecipients(Message.RecipientType.TO, getFromInternetAddresses());
Multipart multipart = new MimeMultipart();
MimeBodyPart iCalAttachment = new MimeBodyPart();
byte[] invite = createICalInvitation(getMeetingID(), getSubject(), getContent(), getMeetingStart(), getMeetingEnd(), getMeetingTimeZone());
iCalAttachment.setDataHandler(new DataHandler(new ByteArrayDataSource(new ByteArrayInputStream(invite), "text/calendar;method=REQUEST;charset=\"UTF-8\"")));
multipart.addBodyPart(iCalAttachment);
mimeMessage.setContent(multipart);
return mimeMessage;
}

private byte[] createICalInvitation(String _meetingID, String _subject, String _content, Date _start, Date _end, TimeZone _tz) throws Exception {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);

VEvent vEvent = new VEvent();
vEvent.getProperties().add(new Uid(_meetingID));
vEvent.getProperties().add(new Summary(_subject));
vEvent.getProperties().add(new Description(_content));
vEvent.getProperties().add(new DtStart(new DateTime(_start)));
vEvent.getProperties().add(new DtEnd(new DateTime(_end)));

net.fortuna.ical4j.model.Calendar cal = new net.fortuna.ical4j.model.Calendar();
cal.getProperties().add(new ProdId("-//iloveoutlook//iCal4j 1.0//EN"));
cal.getProperties().add(net.fortuna.ical4j.model.property.Version.VERSION_2_0);
cal.getProperties().add(CalScale.GREGORIAN);
cal.getProperties().add(net.fortuna.ical4j.model.property.Method.REQUEST);
TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
VTimeZone tz = registry.getTimeZone(_tz.getID()).getVTimeZone();
cal.getComponents().add(tz);
cal.getComponents().add(vEvent);

ByteArrayOutputStream bout = new ByteArrayOutputStream();
CalendarOutputter outputter = new CalendarOutputter();
outputter.output(cal, bout);
return bout.toByteArray();
}

Friday, February 22, 2008

Firefox 3

I've been playing around with beta 3 of Firefox. It's definitely stable enough to use all day, I can't say I've had any real problems yet. The speed is dramatically improved. I'm also enjoying much smarter auto-complete in the address bar.

One must-have config tweak if you plan on using Google Reader with ff3, via crazybob:

To load all new Firefox tabs in the background, set "browser.tabs.loadDivertedInBackground" to "true" in "about:config".
...
My last tweet makes the "v" shortcut in Google Reader imminently more useful.

Wednesday, February 06, 2008

Tuesday auto-boxing nugget

What does the following print?

public static void main(String[] _args) {
System.out.println(boolean.class.isAssignableFrom(Boolean.class));
System.out.println(Boolean.class.isAssignableFrom(boolean.class));
System.out.println(Boolean.class.isInstance(true));
System.out.println(boolean.class.isInstance(true));
}

The answer:
false
false
true
false

This makes sense when you think about it, but it can definitely trip you up when you've become accustomed to auto-boxing. Incidentally, this is my first true "gotcha" w/ auto-boxing in the 2.5 years I've been using it.








Tuesday, February 05, 2008

Analyzing huge HeapDumpOnOutOfMemoryError heap files

I previously mentioned a JVM flag that has the potential to really clarify things when you encounter a OutOfMemoryError in production.

When I first utilized this flag, my heap dump file was probably about 500M. It's a year later, and we just had another OutOfMemoryError (OOM). This time, the heap dump size was more like 1.1G. If you've ever struggled with Java OOMs on 32-bit Windows in the past, you know 1.1 is effectively the largest heap size you can get away with. I don't necessarily agree with -Xmx1.1G in the least, but that's a different post.

It appears that to analyze a 1.1G heap dump file, jhat requires greater than 1.1G of heap allocated for the analysis. In other words, if you max out heap on your application, you won't be able to analyze the heap dump on a similar machine. In other words, if you max out heap on a 32-bit OS, you'll need a 64-bit OS to analyze it.

I also tried SAP Memory Analyzer and YourKit on my 32-bit machine, just to see. Same experience - the OOM analysis tools crash with an OOM. Finally, I tried SAP Memory Analyzer on a 64 bit machine, and it worked well. After seeing the contents of the heap, it was very easy to diagnose the problem. I'm not sure I ever would have solved the problem w/o it.

I've since found out the YourKit 7.1 EAP can handle massive heap dumps on 32 bit machines: I was able to analyze the file by passing in -Xmx700M to YourKit. Overall, I was really impressed w/ SAP and YourKit in terms of polish and responsiveness from support.