Over the past 14 years, I have written e-mail software using C++ and Java. And in those years, I think I might have used Sun's JavaMail library more than any other person besides its author Bill Shannon, but that is just a guess.
I think I've done everything with JavaMail that can be done, including:
- Producing several IMAP, POP3, and SMTP gateways with JavaMail bindings.
- Developed providers for Exchange, Subversion, Unix log files, and an in-memory provider for unit testing.
- Extended Sun's IMAP provider with intercepts for performing custom commands.
- Performing invocation from a desktop C/C++ app, and a Windows URL handler for IMAP that remote-controlled desktop MUAs.
- Developed an IIOP binding and CORBA remoting.
- I snoopervised a colleague, who wrote a nice RSS provider.
- And finally, as the mother of all tasks, I implemented a CGLIB provider proxy, for performing business rule based content transformations triggered by both rules and external transcoding services.
So, after all these years, what are my biggest headaches?
- Method signatures throw both checked MessagingExceptions, and unchecked IllegalStateExceptions. This almost guarantees that any code written against JavaMail will incorrectly handle an unchecked server disconnect, or unexpectedly closed folder. Exception handling through a system almost always requires extensive code reviews, to ensure that both the checked and unchecked exceptions get appropriate treatment. It is better when the compiler is allowed to perform this step, automatically, by using checked exceptions only in the method signatures, rather than manually via subjective periodic code reviews. As a practical reality, the customer finds these bugs.
- Lack of extensibility via SASL. JavaMail's SASL support is proprietary, and does not allow one to support a new SASL mechanism by injecting a javax.security.sasl.SaslClientFactory and javax.security.auth.callback.CallbackHandler. One would want to do this in order to perform XYMCOOKIE authentication with Yahoo SMTP, or XYMCOOKIEB64 authentication with Yahoo IMAP, for example.
- Lack of Java interfaces makes it VERY difficult to apply AOP concepts. Want to write a simple interceptor to hide a certain BodyPart of a Multipart? You have to write 3,600 lines of code tied to CGLIB, and married to specific JavaMail providers, because CGLIB requires ProxyFactory implementations for every proxied class. In other words, JavaMail still thinks it will only ever run on the desktop, and it precludes you from using dynamic proxy capabilities which have been with us since Java 1.3 (which was 1999, according to Sun's Javadocs).
- Inconsistent getSize() specification. In the MimeMessage and POP3Message JavaDocs, getSize() is specified as returning the size of the content of a message. Then, in the IMAPMessage class, getSize() is specified differently, as returning the IMAP "RFC822.SIZE", or size of the full message (header + content). Any code that calls Message.getSize() has to be surrounded by conditional logic that tests whether a Folder is an instance of some Exchange provider, or Sun's IMAP or POP3 provider, or whatever, and whether the header size has to be manually added or not. It all guarantees that providers can't be swapped, and it is more discouragement for people who want to run JavaMail in a server context.
- Disorganized declaration of FetchProfile items. JavaMail declares these in different places, including in the IMAPFolder FetchProfileItem inner class, in the UIDFolder FetchProfileItem inner class, and the FetchProfile Item inner class. 3rd party providers are on their own, having to pick a mechanism for specifying their own fetch profile items. The calling code immediately becomes provider-specific, again guaranteeing that providers can't be swapped.
- Lack of support for synchronous event dispatch. Javamail events fire asynchronously, and are queue-based. This causes bugs in a server context, where all events must be handled synchronously, in the span of a certain IMAP, POP3, SMTP, or WebDAV command. More CGLIB interceptors are required to hack Sun's IMAP and POP3 providers to perform synchronous event dispatch. Again, this library still thinks it will only ever run on a desktop, which was an obsolete vision for Java that is now more than 10 years out of date.
- IMAP IDLE support in JavaMail is provider-specific. A provider-specific API was created, which must be used to put the Sun IMAP provider in IDLE mode. This guarantees that my Exchange provider has to put itself into IDLE mode in its own proprietary way. And, it causes my server code to allocate a dedicated thread just to block on Sun's IDLE method. Again, it all leads to provider-specific calling code, that requires a highly inappropriate degree of server resources. The correct solution would have been for Sun's IMAP provider to enter IDLE mode via ... wait for it ... a period of IDLEness, in the presence of one or more registered MessageCountListeners or MessageChangedListeners. In fact, it already has a background thread that services asynchronous event dispatch that could do the job.
- Inconsistent configuration mechanisms. Some properties are registered through the Session, while others that affect MIME parsing are registered through global environment properties. This even bleeds through the API on occasion, where Multipart preambles contain platform-specific line separators, that need to manually be turned into internet CRLFs, on a Mac for example. Once again, this makes it extremely difficult to compartmentalize behavior on a per-thread basis, which further reinforces the theory that JavaMail still imagines that Java is only run on the desktop, and is not used to produce server software.
- Primitive metadata support. Exchange WebDAV, or Domino IIOP, are examples of pipes with access to a wealth of metadata that might accompany a message. WebDAV provides a clear mechanism for accessing it. Imagine JavaMail's FetchProfile mechanism being able to specify all this, and to return it through first-class interfaces, rather than through primitive keyword flags and part headers, and you begin to understand where JavaMail falls short for integration work.
Where to go from here? Well it's possible that Sun/Oracle will kick off an enterprise JavaMail library, which would be a much-needed overhaul that is about a decade overdue. And for that effort, JCR provides an interesting abstraction to think about: transactions (sessions) of namespaces (workspaces) of folders (nodes) of messages (nodes) of headers, metadata (UIDs, etags, UID validity values, flags, etc), and MIME content (nodes and properties). Its concept of a transient session, or transaction, also maps nicely to back-end multi-row transactions such as Exchange BPROPPATCH, IMAP or SMTP pipelines of commands, or database transactions. But, JCR is lacking a standardized nodetype schema for all that, and also lacks the ease-of-use provided by JavaMail's Folder, Flags, Part, Multipart, and BodyPart classes, which are hard to beat.
JavaMail versus JCR Concepts
| Javamail |
JCR |
| Session |
SessionFactory / JCR2SPI RepositoryConfig |
| Provider (connected) |
Session |
| Namespace Folder |
Root node of a Session workspace |
| Folder |
Node |
| Message |
Node |
| Part, Multipart, BodyPart, Flags |
Nodes and Properties of multi-values of various datatypes and namespace URIs |
It's also important to mention that I've seen MIME parsing used in a lot of non-messaging contexts, and in those contexts, it looked funny to have to load the MIME content into a MimeMessage object. So, that leaves more room for thought, to separate the MIME interfaces from the messaging interfaces, so that they might be called MIME documents instead, and have use in a web server, file parser, feed reader, or whatever.
Comments
Post new comment