<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3874086622685697943</id><updated>2012-02-10T02:34:47.997-05:00</updated><title type='text'>Jack's Java Code Depot</title><subtitle type='html'>Advanced java code dealing with real world problems.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>15</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-8957410248922570828</id><published>2010-09-24T13:54:00.000-04:00</published><updated>2010-09-24T13:54:11.380-04:00</updated><title type='text'>Java COBOL data exchange - Quick Start  Guide</title><content type='html'>Java has rapidly grown into enterprise landscape and the needs for  integrating with mainframe world through JMS/MQ or CICS Transaction  Gateway has become a critical piece to many enterprise IT stacks.&lt;br /&gt;&lt;br /&gt;JavaCobolExchanger - an open source google code project is developed to address the needs.&lt;br /&gt;&lt;br /&gt;Here is the link to the project: http://code.google.com/a/eclipselabs.org/p/java-cobol-exchanger/&lt;br /&gt;&lt;br /&gt;To get started first we need to understand COBOL copy books. A copy book is a data description of data layout in fixed format. For example:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;01 MY-EXCHANGE-RECORD.&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; 05 USER-ID&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC X(20).&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; 05 USER-AGE&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC 9(3).&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; 05 USER_NAME.&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 FIRST-NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC X(20).&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 LAST-NAME&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC X(20).&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 MIDDLE_INITIAL PIC X(1).&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; 05 AMOUNT-RECEIVED&amp;nbsp; PIC 9(5).99.&lt;/div&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; 05 DATE-REGISTERED.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; &amp;nbsp; 10 DATE-CCYY &amp;nbsp; &amp;nbsp;&amp;nbsp; PIC 9(4).&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 DATE-SEP1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC X.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 DATE-MM&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC 9(2).&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 DATE-SEP2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC X.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10 DATE-DD&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PIC 9(2).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This copy book tells us that the first 20 characters are reserved for user Id, the next 3 characters are reserved for user age and it must be numeric. The next 41 characters are reserved for a group called USER-NAME and the group contains three elements. Followed after the group is a decimal element with equivalent decimal format of&amp;nbsp; "00000.00". The last 10 characters are reserved for a date element, which again is a group consisting of five elements, with equivalent date format of "yyyy-MM-dd".&lt;br /&gt;&lt;br /&gt;To construct a java exchange instance of above COBOL copy book:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public class CobolCopybook extends ExchangeRecord {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public &lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Cobol&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Copybook() {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // define user name group&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; BaseElement userNameGroup[] = {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; new StringElement("firstName", 20),&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; new StringElement("lastName", 20),&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; new StringElement("middleInitial",1),&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; // define the exchange record&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.add(new StringElement("userId", 20));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.add(new IntegerElement("userAge", 3));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.add(&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;new StructElement("userName", userNameGroup)&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;);&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.add(new DecimalElement("amount", "00000.00"));&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; list.add(new DateTimeElement("date", "yyyy-MM-dd"));&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;}&lt;br /&gt;&lt;br /&gt;To load the instance with data, simply add the following code:&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ExchangeRecord bean = new CobolCopybook();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bean.getElement("userId").setValue("test user");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bean.getElement("userAge").setValue("35");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;StructElement userNameGroup = (StructElement) bean.getElement("userName");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;userNameGroup&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;.getElement("firstName").setValue("John");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;userNameGroup&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;.getElement("lastName").setValue("Smith");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt; &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bean.getElement("amount").setValue("199.99");&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bean.getElement("date").setValue("2010-01-01");&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;Now you can export the instance to a fixed format string which can then be consumed by a COBOL program&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;String cobolString = bean.exportToString();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;// XXX: add your code here to send it to mainframe&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The data exchange can be performed in either direction, we just demonstrated a data exchange from java to COBOL. Now let's see how we handle exchange from a COBOL output:&lt;br /&gt;&lt;br /&gt;First we need create an java exchange instance:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;ExchangeRecord bean = new ComplexCopybook();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Then we import the COBOL output to the instance:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bean.importFromString(&lt;cobol output="" string=""&gt;);&lt;/cobol&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And now we can access the COBOL data using getValue method:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;String userId = bean.getElement("userId").getValue();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;Integer userAge = (Integer) bean.getElement("userAge").getValue(); &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-8957410248922570828?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/8957410248922570828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2010/09/java-cobol-data-exchange-quick-start.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/8957410248922570828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/8957410248922570828'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2010/09/java-cobol-data-exchange-quick-start.html' title='Java COBOL data exchange - Quick Start  Guide'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-3688286626176918848</id><published>2010-09-02T12:03:00.000-04:00</published><updated>2010-09-02T12:03:02.153-04:00</updated><title type='text'>A fast XML schema validator</title><content type='html'>JAXP 1.3 introduced a SchemaFactory by which you can compile a schema from a xsd file, and use the compiled schema to create a Validator that can be used to validate a XML document. Since the schema compilation takes some time, it would be beneficial in a service oriented environment to reuse the compiled schemas for future requests. Presented here is a simple SchemaValidator class that implements the idea with a hash table.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;/*&lt;br /&gt;&amp;nbsp;* blog/javaclue/xml/SchemaValidator.java&lt;br /&gt;&amp;nbsp;* &lt;br /&gt;&amp;nbsp;* Copyright (C) 2009 JackW&lt;br /&gt;&amp;nbsp;* &lt;br /&gt;&amp;nbsp;* This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt;&amp;nbsp;* GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt;&amp;nbsp;* of the License, or (at your option) any later version.&lt;br /&gt;&amp;nbsp;* &lt;br /&gt;&amp;nbsp;* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt;&amp;nbsp;* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&amp;nbsp; See the GNU&lt;br /&gt;&amp;nbsp;* Lesser General Public License for more details.&lt;br /&gt;&amp;nbsp;* &lt;br /&gt;&amp;nbsp;* You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt;&amp;nbsp;* If not, see &lt;http: licenses="" www.gnu.org=""&gt;.&lt;br /&gt;&amp;nbsp;*/&lt;/http:&gt;&lt;br /&gt;package blog.javaclue.xml;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.util.Hashtable;&lt;br /&gt;import java.util.Map;&lt;br /&gt;&lt;br /&gt;import javax.xml.XMLConstants;&lt;br /&gt;import javax.xml.parsers.ParserConfigurationException;&lt;br /&gt;import javax.xml.transform.Source;&lt;br /&gt;import javax.xml.transform.dom.DOMSource;&lt;br /&gt;import javax.xml.transform.stream.StreamSource;&lt;br /&gt;import javax.xml.validation.Schema;&lt;br /&gt;import javax.xml.validation.SchemaFactory;&lt;br /&gt;import javax.xml.validation.Validator;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;import org.w3c.dom.Document;&lt;br /&gt;import org.w3c.dom.Element;&lt;br /&gt;import org.xml.sax.SAXException;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;&amp;nbsp;* Validate XML against schema using SchemaFactory (JAXP 1.3).&lt;br /&gt;&amp;nbsp;* Schema compilation usually takes time, this class saves the compiled schemas&lt;br /&gt;&amp;nbsp;* in a hash table so they can be reused by future requests and other instances.&lt;br /&gt;&amp;nbsp;*/&lt;br /&gt;public class SchemaValidator {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;protected static Logger logger = Logger.getLogger(SchemaValidator.class);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;protected static boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private final String schema_path;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private final String schema_file;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private static final Map&lt;string, schema=""&gt; schemaPool = new Hashtable&lt;string, schema=""&gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public SchemaValidator(String schema_path, String schema_file) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;this.schema_path = schema_path;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;this.schema_file = schema_file;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;/**&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; * Validate xmlStream against schema&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; * &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; * @throws IOException&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; * @throws SAXException&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; * @throws ParserConfigurationException&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; */&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public void validate(Document xml_doc) throws ParserConfigurationException, SAXException,&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;IOException {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;validate(xml_doc.getDocumentElement());&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public void validate(Element element) throws SAXException, IOException {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Schema schema = compileSchema(schema_path + schema_file);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;// create a Validator instance, which can be used to validate an&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;// instance document&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Validator validator = schema.newValidator();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;validator.setErrorHandler(new SaxErrorHandler());&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;// validate the DOM tree&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;validator.validate(new DOMSource(element));&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;private static Schema compileSchema(String schemaFile) throws SAXException {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (!schemaPool.containsKey(schemaFile)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (isDebugEnabled) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;logger.info("compileSchema() - compile schema file: " + schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;// create a SchemaFactory capable of understanding W3C schemas&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;factory.setErrorHandler(new SaxErrorHandler());&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;ClassLoader loader = Thread.currentThread().getContextClassLoader();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;URL url = loader.getResource(schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (url == null) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;throw new RuntimeException("Could not find Schema file: " + schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;InputStream is = loader.getResourceAsStream(schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;// load a schema, represented by a Schema instance&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Source schemasrc = new StreamSource(is, url.getPath());&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Schema schema = factory.newSchema(schemasrc);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;schemaPool.put(schemaFile, schema);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;return schemaPool.get(schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static synchronized void removeFromPool(String schemaFile) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;schemaPool.remove(schemaFile);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static synchronized void clearPool() {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;schemaPool.clear();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;public static void main(String[] args) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;SchemaValidator test = new SchemaValidator("Schemas/", "MySampleSchema.xsd");&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;try {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;Document doc = XMLHelper.loadDocument("Test_xmls/MySampleXml.xml");&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;test.validate(doc);&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;catch (Exception e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;e.printStackTrace();&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/string,&gt;&lt;/string,&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-3688286626176918848?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/3688286626176918848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2010/09/fast-xml-schema-validator.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3688286626176918848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3688286626176918848'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2010/09/fast-xml-schema-validator.html' title='A fast XML schema validator'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-2672561664567295988</id><published>2010-09-02T10:43:00.000-04:00</published><updated>2010-09-02T10:43:59.721-04:00</updated><title type='text'>Simple IBM MQ Message Age Monitor</title><content type='html'>For those that are still running IBM MQ (or Websphere MQ) on mainframes, presented here is a simple java message age monitor program that polls a queue  periodically for how long the oldest message has been living in  the queue. This sample code will output a message to the console when the oldest message in the queue has been living in there for more that 60 seconds at the moment of polling. You can change it to send out an email or snmp alert instead.&lt;br /&gt;In order to compile and run this program, PCF package (MS0B) is needed and can be &lt;a href="http://www-01.ibm.com/support/docview.wss?rs=171&amp;amp;uid=swg24000668&amp;amp;loc=en_US&amp;amp;cs=utf-8&amp;amp;lang=en"&gt;downloaded from IBM&lt;/a&gt;. You will also need "com.ibm.mq.jar" and IBM's "j2ee.jar". They can be found from IBM's WSAD or RAD IDE development tools.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/ibmmq/MessageAgeMonitor.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.ibmmq;&lt;br /&gt;&lt;br /&gt;import java.util.GregorianCalendar;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;import com.ibm.mq.MQC;&lt;br /&gt;import com.ibm.mq.MQEnvironment;&lt;br /&gt;import com.ibm.mq.MQException;&lt;br /&gt;import com.ibm.mq.MQGetMessageOptions;&lt;br /&gt;import com.ibm.mq.MQMessage;&lt;br /&gt;import com.ibm.mq.MQQueue;&lt;br /&gt;import com.ibm.mq.MQQueueManager;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Simple message age monitor program that uses IBM MQ Java to read PCF-format&lt;br /&gt; * messages from a queue.&lt;br /&gt; */&lt;br /&gt;public class MessageAgeMonitor implements Runnable {&lt;br /&gt; protected static Logger logger = Logger.getLogger(MessageAgeMonitor.class);&lt;br /&gt; protected static boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt; &lt;br /&gt; final private String qmgrName;&lt;br /&gt; final private String host;&lt;br /&gt; final private int port;&lt;br /&gt; final private String queueName;&lt;br /&gt; final private int alertAge; // in seconds&lt;br /&gt; final private String channel;&lt;br /&gt; &lt;br /&gt; final static int Polling_Freq = 30 * 1000; // 30 seconds&lt;br /&gt;&lt;br /&gt; MessageAgeMonitor(String qmgrName, String host, int port, String queueName, String channel,&lt;br /&gt;   int alertAge) {&lt;br /&gt;  this.qmgrName = qmgrName;&lt;br /&gt;  this.host = host;&lt;br /&gt;  this.port = port;&lt;br /&gt;  this.channel = channel;&lt;br /&gt;  this.queueName = queueName;&lt;br /&gt;  this.alertAge = alertAge;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public void run() {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Starting Message Age monitor for " + queueName + "...");&lt;br /&gt;  while (true) {&lt;br /&gt;   checkAge();&lt;br /&gt;   try {&lt;br /&gt;    Thread.sleep(Polling_Freq); // sleep for 30 seconds&lt;br /&gt;   }&lt;br /&gt;   catch (InterruptedException e) {&lt;br /&gt;    logger.info("The monitor has been interrupted, exit...");&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void checkAge() {&lt;br /&gt;  MQQueueManager qm = null;&lt;br /&gt;  MQQueue queue = null;&lt;br /&gt;  if (isDebugEnabled) {&lt;br /&gt;   logger.debug("Connecting to " + qmgrName + " at " + host + ":" + port + " over " + channel);&lt;br /&gt;  }&lt;br /&gt;        try {&lt;br /&gt;   // Turn off unnecessary output before we start&lt;br /&gt;   MQEnvironment.disableTracing ();&lt;br /&gt;         MQException.log = null;&lt;br /&gt;   MQEnvironment.hostname = host;&lt;br /&gt;   MQEnvironment.port = port;&lt;br /&gt;   MQEnvironment.channel = channel;&lt;br /&gt;   qm = new MQQueueManager ("");&lt;br /&gt;        queue = qm.accessQueue (queueName, MQC.MQOO_BROWSE | MQC.MQOO_FAIL_IF_QUIESCING);&lt;br /&gt;            MQMessage message = new MQMessage ();&lt;br /&gt;            MQGetMessageOptions gmo = new MQGetMessageOptions ();&lt;br /&gt;            &lt;br /&gt;            gmo.options = MQC.MQGMO_BROWSE_FIRST | MQC.MQGMO_NO_WAIT | MQC.MQGMO_CONVERT;&lt;br /&gt;         &lt;br /&gt;         message.messageId = null;&lt;br /&gt;         message.correlationId = null;&lt;br /&gt;         queue.get (message, gmo);&lt;br /&gt;         // get message put date time&lt;br /&gt;         GregorianCalendar cal = message.putDateTime;&lt;br /&gt;         long ageInMillis = new java.util.Date().getTime() - cal.getTime().getTime();&lt;br /&gt;         int ageInSeconds = (int) ageInMillis/1000;&lt;br /&gt;         if (isDebugEnabled)&lt;br /&gt;    logger.debug("Put Date: " + cal.getTime() + " age in seconds: " + ageInSeconds);&lt;br /&gt;   if (ageInSeconds &amp;gt; alertAge) {&lt;br /&gt;    logger.info(qmgrName + "/" + queueName + " age = " + ageInSeconds&lt;br /&gt;      + ", exceeded alert threshold: " + alertAge);&lt;br /&gt;    // XXX: add your code here to send out alert&lt;br /&gt;   }&lt;br /&gt;        }&lt;br /&gt;        catch (MQException mqe) {&lt;br /&gt;         if (mqe.reasonCode == MQException.MQRC_NO_MSG_AVAILABLE) {&lt;br /&gt;          if (isDebugEnabled) {&lt;br /&gt;           logger.debug("Queue " + qmgrName + "/" + queueName + " is empty.");&lt;br /&gt;          }&lt;br /&gt;         }&lt;br /&gt;         else {&lt;br /&gt;          logger.error("MQException caught", mqe);&lt;br /&gt;         }&lt;br /&gt;        }&lt;br /&gt;        finally {&lt;br /&gt;         if (queue != null &amp;amp;&amp;amp; queue.isOpen()) {&lt;br /&gt;          try {&lt;br /&gt;           queue.close();&lt;br /&gt;          }&lt;br /&gt;          catch (Exception e) {&lt;br /&gt;     logger.error("Exception caught during queue.close()", e);&lt;br /&gt;          }&lt;br /&gt;         }&lt;br /&gt;   if (qm != null) {&lt;br /&gt;    if (qm.isOpen()) {&lt;br /&gt;     try {&lt;br /&gt;      qm.close();&lt;br /&gt;     }&lt;br /&gt;     catch (Exception e) {&lt;br /&gt;      logger.error("Exception caught during qm.close()", e);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    if (qm.isConnected()) {&lt;br /&gt;     try {&lt;br /&gt;      qm.disconnect();&lt;br /&gt;     }&lt;br /&gt;     catch (Exception e) {&lt;br /&gt;      logger.error("Exception caught during qm.disconnect()", e);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt; public static void main (String [] args) {&lt;br /&gt;  String qmgrName = "QMGR";&lt;br /&gt;  String host = "localhost";&lt;br /&gt;  int port = 1450;&lt;br /&gt;  String channel = "SYSTEM.DEF.SVRCONN";&lt;br /&gt;  String queueName = "TEST_QUEUE";&lt;br /&gt;  &lt;br /&gt;  MessageAgeMonitor monitor = new MessageAgeMonitor(qmgrName, host, port, queueName, channel, 60);&lt;br /&gt;  new Thread(monitor).start();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-2672561664567295988?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/2672561664567295988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/simple-ibm-mq-message-age-monitor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2672561664567295988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2672561664567295988'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/simple-ibm-mq-message-age-monitor.html' title='Simple IBM MQ Message Age Monitor'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-5990586579010772016</id><published>2009-11-30T16:43:00.000-05:00</published><updated>2009-11-30T16:43:22.102-05:00</updated><title type='text'>Use wss4j with Axis1.4 for message encryption and signing</title><content type='html'>&lt;div style="text-align: center;"&gt;&lt;span style="font-size: x-large;"&gt;Use wss4j with Axis1.4 for message encryption and signing.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Axis 1.4 and wss4j have been out for quite a while, but the detailed documentations about using them together is hard to find. Detailed documentations are abundant for using Rampant with Axis 2. But if you are like me stuck to the Axis 1 and want to encrypt and sign your messages without modifying your existing code, wss4j is still the best option out there.&lt;br /&gt;&lt;br /&gt;The Apache wss4j web site provides some wonderful documentations about Axis deployment tutorial and samples. The hard part is how to put everything together. What I am about to offer here is the detailed steps about putting everything together so that you can implement the message encryption and signing with your existing web services using wss4j with Axis 1.4.&lt;br /&gt;&lt;br /&gt;I will take a different approach here, let's tackle the key stores first.&lt;br /&gt;&lt;br /&gt;To be able to handle encrypted messages, you'll need a pair of keys, a public key that is used to encrypt messages which the client will use, and a private key that is used to decrypt messages which you keep it safe in your server.&amp;nbsp; To generate self-signed key stores using java keytool, issue following command in a dos window or a shell prompt:&lt;br /&gt;&lt;br /&gt;keytool -genkey -dname "CN=Server, OU=Encryption, O=JacksBlog, L=Raleigh, S=NC, C=US" -alias serverkey -keypass serverpass -validity 9999 -keyalg RSA -sigalg SHA1withRSA -keystore server.keystore -storepass nosecret&lt;br /&gt;&lt;br /&gt;This will create a file called server.keystore which contains a private and public key pair for encryption purpose.&lt;br /&gt;&lt;br /&gt;Next we will create a key pair for message signing:&lt;br /&gt;&lt;br /&gt;keytool -genkey -dname "CN=Client, OU=Signing, O=JacksBlog, L=Raleigh, S=NC, C=US" -alias clientkey -keypass clientpass -validity 9999 -keyalg RSA -sigalg SHA1withRSA -keystore client.keystore -storepass nosecret&lt;br /&gt;&lt;br /&gt;This will create a file called client.keystore that contains a key pair for message signing.&lt;br /&gt;&lt;br /&gt;In order for the client to trust the server, we need to export the public key from server.keystore and import it to client.keystore:&lt;br /&gt;&lt;br /&gt;keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer&lt;br /&gt;&lt;br /&gt;keytool -import -alias serverkey -keystore client.keystore -storepass nosecret -file servercert.cer&lt;br /&gt;&lt;br /&gt;In order for the server to trust the client, we need to export the public key from client.keystore and import it to server.keystore:&lt;br /&gt;&lt;br /&gt;keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer&lt;br /&gt;&lt;br /&gt;keytool -import -alias clientkey -keystore server.keystore -storepass nosecret -file clientcert.cer&lt;br /&gt;&lt;br /&gt;Now we are ready for Axis configurations, please refer to wss4j web site for how to install wss4j on Axis 1.4. I'll highlight a couple of key points here:&lt;br /&gt;&lt;br /&gt;1) Download the binary distribution from Apache wss4j web site, and unzip it into a folder. Make sure to read through the README.txt file, and download all the required jar files listed in the README.txt file.&lt;br /&gt;&lt;br /&gt;2) Copy all the required jar files along with wss4j jar file to your Axis's WEB-INF/lib directory.&lt;br /&gt;&lt;br /&gt;Now the detailed steps:&lt;br /&gt;&lt;br /&gt;3) Create a password callback class in your Axis project and compile it to your class path:&lt;br /&gt;&lt;br /&gt;public class PasswordCallBackHandler implements CallbackHandler {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void handle(Callback[] callbacks) throws IOException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (int i = 0; i &amp;lt; callbacks.length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String id = pwcb.getIdentifier();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int usage = pwcb.getUsage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (usage == WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // used to retrieve password for private key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ("serverkey".equals(id)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pwcb.setPassword("serverpass");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; else if ("clientkey".equals(id)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pwcb.setPassword("clientpass");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;4) Create a folder called Keys under WEB-INF/classes and copy the server.keystore file to the folder.&lt;br /&gt;&lt;br /&gt;5) Create a crypto.properties file and copy it to WEB-INF/classes folder:&lt;br /&gt;&lt;br /&gt;org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.type=jks&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.password=nosecret&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.alias=serverkey&lt;br /&gt;org.apache.ws.security.crypto.merlin.file=Keys/server.keystore&lt;br /&gt;&lt;br /&gt;6) Pick up an existing web service from your Axis project (or simply create a new one) that you want to have messages encrypted and signed, and add the required entries to your server-config.wsdd file. For example I picked up a service call MyService and added "requestFlow" entry into the wsdd:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;service name="MyService" provider="java:MSG"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;requestFlow&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handler type="java:org.apache.ws.axis.security.WSDoAllReceiver"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="action" value="Signature Encrypt"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="signaturePropFile" value="./WEB-INF/classes/crypto.properties" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="passwordCallbackClass" value="PasswordCallBackHandler"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/handler&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/requestFlow&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="className" value="blog.MyService" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="allowedMethods" value="process" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/service&amp;gt;&lt;br /&gt;&lt;br /&gt;7) Restart your server and the MyService should be ready to serve encrypted and signed service calls.&lt;br /&gt;&lt;br /&gt;Now let's modify the client code to call the service.&lt;br /&gt;&lt;br /&gt;1) Copy the client.keystore file to the root path of your client project. &lt;br /&gt;&lt;br /&gt;2) Create a crypto.properties file and copy it to the root class path:&lt;br /&gt;&lt;br /&gt;org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.type=jks&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.password=nosecret&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.alias=clientkey&lt;br /&gt;org.apache.ws.security.crypto.merlin.file=client.keystore&lt;br /&gt;&lt;br /&gt;3) Create a wsdd file called client_deploy.wsdd and copy it to the root class path (please change the "passwordCallbackClass" value accordingly):&lt;br /&gt;&lt;br /&gt;&amp;lt;deployment xmlns="http://xml.apache.org/axis/wsdd/"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;transport name="http" pivot="java:org.apache.axis.transport.http.HTTPSender" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;globalConfiguration&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;requestFlow&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;handler type="java:org.apache.ws.axis.security.WSDoAllSender"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="user" value="clientkey" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="encryptionUser" value="serverkey"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="action" value="Signature Encrypt" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="signaturePropFile" value="crypto.properties" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;parameter name="passwordCallbackClass" value="blog.ServiceClient" /&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/handler&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/requestFlow&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/globalConfiguration&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/deployment&amp;gt;&lt;br /&gt;4) Modify the client code, add this line before the service call is initialized:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; System.setProperty("axis.ClientConfigFile", "client_deploy.wsdd");&lt;br /&gt;&lt;br /&gt;5) Add the following method to your client code, make sure the client class implements CallbackHandler:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void handle(Callback[] callbacks) throws IOException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (int i = 0; i &amp;lt; callbacks.length; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; WSPasswordCallback pwcb = (WSPasswordCallback)callbacks[i];&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String id = pwcb.getIdentifier();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int usage = pwcb.getUsage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (usage == WSPasswordCallback.DECRYPT || usage == WSPasswordCallback.SIGNATURE) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // used to retrieve password for private key&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ("clientkey".equals(id)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; pwcb.setPassword("clientpass");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;That should be it. Compile and run your client class and keep your fingers crossed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-5990586579010772016?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/5990586579010772016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/5990586579010772016'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/11/use-wss4j-with-axis14-for-message.html' title='Use wss4j with Axis1.4 for message encryption and signing'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-5622174328077347377</id><published>2009-09-10T14:34:00.000-04:00</published><updated>2009-09-10T14:34:46.649-04:00</updated><title type='text'>Build a reliable email reader - Part 1</title><content type='html'>When I was building a back-end email reader that reads emails from a mailbox and save them for future processing. I encountered a problem that the back-end task would stop unexpectedly due to various reasons, such as when a malformed email was received, or the mailbox was temporary disconnected, or the pop3 or imap server was temporarily out of service for maintainance, etc.&lt;br /&gt;&lt;br /&gt;A reliable email reader program was needed so it can be started to run continually until some catastrophic events occurred. The email reader presented here will read email messages into our &lt;a href="http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean_02.html"&gt;portable message beans&lt;/a&gt;, and display them to the console. The pop3 server  located on the "localhost" is used, and the mailbox name is "support" with password "support". Please change them to point to your pop3 account accordingly. &lt;br /&gt;&lt;br /&gt;We will need two more supporting classes which I will present them in Part 2.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/MailReader.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.Date;&lt;br /&gt;import java.util.Properties;&lt;br /&gt;&lt;br /&gt;import javax.mail.Folder;&lt;br /&gt;import javax.mail.Message;&lt;br /&gt;import javax.mail.MessagingException;&lt;br /&gt;import javax.mail.NoSuchProviderException;&lt;br /&gt;import javax.mail.Session;&lt;br /&gt;import javax.mail.Store;&lt;br /&gt;import javax.mail.event.ConnectionEvent;&lt;br /&gt;import javax.mail.event.ConnectionListener;&lt;br /&gt;import javax.mail.event.MessageCountAdapter;&lt;br /&gt;import javax.mail.event.MessageCountEvent;&lt;br /&gt;import javax.mail.event.StoreEvent;&lt;br /&gt;import javax.mail.event.StoreListener;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * This class provides methods to read e-mails from a mailbox.&lt;br /&gt; */&lt;br /&gt;public class MailReader implements ConnectionListener, StoreListener {&lt;br /&gt; private static final long serialVersionUID = -9061869821061961065L;&lt;br /&gt; private static final Logger logger = Logger.getLogger(MailReader.class);&lt;br /&gt; protected static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; protected final String LF = System.getProperty("line.separator", "\n");&lt;br /&gt; private final boolean debugSession = false;&lt;br /&gt;&lt;br /&gt; private final Session session;&lt;br /&gt; private final Mailbox mailbox;&lt;br /&gt; private final MailProcessor processor;&lt;br /&gt; &lt;br /&gt; private Store store = null;&lt;br /&gt; private Folder folder = null;&lt;br /&gt; &lt;br /&gt; private static final int MAX_MSGS_PER_READ = 100;&lt;br /&gt; private static final int MAX_WAIT = 120 * 1000; // up to two minutes&lt;br /&gt;&lt;br /&gt; private final int msgsPerRead;&lt;br /&gt; private final int pollingFreq;&lt;br /&gt; private int messagesProcessed = 0;&lt;br /&gt;&lt;br /&gt; private static final int[] RetryFreqs = &lt;br /&gt;  { 5, 10, 10, 20, 20, 20, 30, 30, 30, 30, 60, 60, 60, 60, 60 }; // in seconds&lt;br /&gt; private static final int RETRY_FREQ = 120; // in seconds&lt;br /&gt;&lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  Mailbox vo = new Mailbox("localhost", "support", "support");&lt;br /&gt;  MailReader reader = new MailReader(vo);&lt;br /&gt;  try {&lt;br /&gt;   reader.readMail();&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * create a MailReader instance&lt;br /&gt;  * &lt;br /&gt;  * @param mbox -&lt;br /&gt;  *            mailbox properties&lt;br /&gt;  */&lt;br /&gt; public MailReader(Mailbox mbox) {&lt;br /&gt;  this.mailbox = mbox;&lt;br /&gt;&lt;br /&gt;  // number of e-mails (="msgsPerPass") to read per cycle&lt;br /&gt;  int msgs_per_read = mbox.getMessagesPerRead();&lt;br /&gt;  msgs_per_read = msgs_per_read &amp;lt;= 0 ? 5 : msgs_per_read; // default is 5&lt;br /&gt;  msgsPerRead = msgs_per_read;&lt;br /&gt;&lt;br /&gt;  // number of seconds (="pollingFreq") to wait between reads&lt;br /&gt;  int _freq = mbox.getMinimumWait() * 1000 + msgsPerRead * 100;&lt;br /&gt;  pollingFreq = _freq &amp;gt; MAX_WAIT ? MAX_WAIT : _freq; // upper limit is MAX_WAIT&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Wait between reads in milliseconds: " + pollingFreq);&lt;br /&gt;  &lt;br /&gt;  // enable RFC2231 support in parameter lists, since javamail 1.4&lt;br /&gt;  // Since very few existing programs support RFC2231, disable it for now&lt;br /&gt;  /*&lt;br /&gt;  System.setProperty("mail.mime.encodeparameters", "true");&lt;br /&gt;  System.setProperty("mail.mime.decodeparameters", "true");&lt;br /&gt;  System.setProperty("mail.mime.encodefilename", "true");&lt;br /&gt;  System.setProperty("mail.mime.decodefilename", "true");&lt;br /&gt;  */&lt;br /&gt;  &lt;br /&gt;  // to make the reader more tolerable&lt;br /&gt;  System.setProperty("mail.mime.multipart.ignoremissingendboundary", "true");&lt;br /&gt;  System.setProperty("mail.mime.multipart.ignoremissingboundaryparameter", "true");&lt;br /&gt;  &lt;br /&gt;  Properties m_props = (Properties) System.getProperties().clone();&lt;br /&gt;  m_props.setProperty("mail.debug", "true");&lt;br /&gt;  m_props.setProperty("mail.debug.quote", "true");&lt;br /&gt;&lt;br /&gt;  /*&lt;br /&gt;   * POP3 - properties of com.sun.mail.pop3 &lt;br /&gt;   * mailbox can be accessed via URL: pop3://user:password@host:port/INBOX&lt;br /&gt;   */&lt;br /&gt;  // set timeouts in milliseconds. default for both is infinite&lt;br /&gt;  // Socket connection timeout&lt;br /&gt;  m_props.setProperty("mail.pop3.connectiontimeout", "900000");&lt;br /&gt;  // Socket I/O timeout&lt;br /&gt;  m_props.setProperty("mail.pop3.timeout", "750000");&lt;br /&gt;  // m_props.setProperty("mail.pop3.rsetbeforequit","true");&lt;br /&gt;  /* issue RSET before QUIT, default: false */&lt;br /&gt;&lt;br /&gt;  /* IMAP - properties of com.sun.mail.imap */&lt;br /&gt;  // set timeouts in milliseconds. default for both is infinite&lt;br /&gt;  // Socket connection timeout&lt;br /&gt;  m_props.setProperty("mail.imap.connectiontimeout", "900000");&lt;br /&gt;  // Socket I/O timeout&lt;br /&gt;  m_props.setProperty("mail.imap.timeout", "750000");&lt;br /&gt;  &lt;br /&gt;  // Certain IMAP servers do not implement the IMAP Partial FETCH&lt;br /&gt;  // functionality properly&lt;br /&gt;  // set Partial fetch to false to workaround exchange server 5.5 bug&lt;br /&gt;  m_props.setProperty("mail.imap.partialfetch","false");&lt;br /&gt;  &lt;br /&gt;  // If your version of Exchange doesn't implement POP3 properly, you need&lt;br /&gt;  // to tell JavaMail to forget about TOP headers by setting the &lt;br /&gt;  // mail.pop3.forgettopheaders property to true.&lt;br /&gt;  if (mbox.isExchange()) {&lt;br /&gt;   m_props.setProperty("mail.pop3.forgettopheaders","true");&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // Get a Session object&lt;br /&gt;  if (mbox.isUseSsl()) {&lt;br /&gt;   m_props.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");&lt;br /&gt;   m_props.setProperty("mail.pop3.socketFactory.fallback", "false");&lt;br /&gt;   m_props.setProperty("mail.pop3.port", mbox.getPort()+"");&lt;br /&gt;   m_props.setProperty("mail.pop3.socketFactory.port", mbox.getPort()+"");&lt;br /&gt;   session = Session.getInstance(m_props);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   session = Session.getInstance(m_props, null);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  processor = new MailProcessor(mbox);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * invoke application plug-in to process e-mails.&lt;br /&gt;  * &lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  * @throws IOException&lt;br /&gt;  */&lt;br /&gt; private void readMail() throws MessagingException, IOException {&lt;br /&gt;  session.setDebug(true); // DON'T CHANGE THIS&lt;br /&gt;  String protocol = mailbox.getProtocol();&lt;br /&gt;  if (!"imap".equalsIgnoreCase(protocol) &amp;amp;&amp;amp; !"pop3".equalsIgnoreCase(protocol)) {&lt;br /&gt;   throw new IllegalArgumentException("Invalid protocol " + protocol);&lt;br /&gt;  }&lt;br /&gt;  if (store == null) {&lt;br /&gt;   try {&lt;br /&gt;    // Get a Store object&lt;br /&gt;    store = session.getStore(protocol);&lt;br /&gt;    store.addConnectionListener(this);&lt;br /&gt;    store.addStoreListener(this);&lt;br /&gt;   }&lt;br /&gt;   catch (NoSuchProviderException pe) {&lt;br /&gt;    logger.fatal("NoSuchProviderException caught during session.getStore()", pe);&lt;br /&gt;    throw pe;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  try {&lt;br /&gt;   connect(store, 0, mailbox.getMaxRetries()); // could fail due to authentication error&lt;br /&gt;   folder = getFolder(store, 0, 1); // retry once on folder&lt;br /&gt;   // reset debug mode&lt;br /&gt;   session.setDebug(debugSession);&lt;br /&gt;   if ("imap".equalsIgnoreCase(protocol)) {&lt;br /&gt;    // only IMAP support MessageCountListener&lt;br /&gt;    final String _folder = mailbox.getFolderName();&lt;br /&gt;    // Add messageCountListener to listen to new messages from IMAP server&lt;br /&gt;    addMsgCountListener(folder, _folder);&lt;br /&gt;   }&lt;br /&gt;   if ("pop3".equalsIgnoreCase(protocol)) {&lt;br /&gt;    readFromPop3();&lt;br /&gt;   }&lt;br /&gt;   else if ("imap".equalsIgnoreCase(protocol)) {&lt;br /&gt;    readFromImap();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (InterruptedException e) {&lt;br /&gt;   logger.warn("InterruptedException caught, exiting...", e);&lt;br /&gt;  }&lt;br /&gt;  finally {&lt;br /&gt;   try {&lt;br /&gt;    if (folder != null &amp;amp;&amp;amp; folder.isOpen()) {&lt;br /&gt;     folder.close(false);&lt;br /&gt;    }&lt;br /&gt;    store.close();&lt;br /&gt;   }&lt;br /&gt;   catch (Exception e) {&lt;br /&gt;    logger.error("Exception caught", e);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("MailReader ended");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void readFromPop3() throws InterruptedException, MessagingException, IOException {&lt;br /&gt;  final String _user = mailbox.getUserId();&lt;br /&gt;  final String _host = mailbox.getHost();&lt;br /&gt;  final String _folder = mailbox.getFolderName();&lt;br /&gt;  boolean keepRunning = true;&lt;br /&gt;  int retries = 0;&lt;br /&gt;  do {&lt;br /&gt;   try {&lt;br /&gt;    if (folder.isOpen()) {&lt;br /&gt;     folder.close(false);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   catch (MessagingException em) {&lt;br /&gt;    logger.error("MessagingException caught during folder.close()", em);&lt;br /&gt;   }&lt;br /&gt;   try {&lt;br /&gt;    Thread.sleep(pollingFreq); // exit if interrupted&lt;br /&gt;    // reopen the folder in order to pick up the new messages&lt;br /&gt;    folder.open(Folder.READ_WRITE);&lt;br /&gt;   }&lt;br /&gt;   catch (MessagingException e) {&lt;br /&gt;    logger.error("Failed to open folder " + _user + "@" + _host + ":" + _folder);&lt;br /&gt;    logger.error("MessagingException caught", e);&lt;br /&gt;    if (retries++ &amp;lt; mailbox.getMaxRetries() || mailbox.getMaxRetries() &amp;lt; 0) {&lt;br /&gt;     int sleepFor;&lt;br /&gt;     // wait for a while and try to reopen the folder&lt;br /&gt;     if (retries &amp;lt; RetryFreqs.length) {&lt;br /&gt;      sleepFor = RetryFreqs[retries];&lt;br /&gt;     }&lt;br /&gt;     else {&lt;br /&gt;      sleepFor = RETRY_FREQ;&lt;br /&gt;     }&lt;br /&gt;     logger.error("Exception caught during folder.open(), retry(=" + retries&lt;br /&gt;       + ") in " + sleepFor + " seconds");&lt;br /&gt;     Thread.sleep(sleepFor * 1000);&lt;br /&gt;      // terminate if interrupted&lt;br /&gt;     continue;&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;     logger.fatal("All retries failed for " + _user + "@" + _host + ":" + _folder);&lt;br /&gt;     throw e;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   if (retries &amp;gt; 0) {&lt;br /&gt;    logger.warn("Opened " + _user + "@" + _host + ":" + _folder + " after " + retries + " retries");&lt;br /&gt;    retries = 0; // reset retry counter&lt;br /&gt;   }&lt;br /&gt;   Date start_tms = new Date();&lt;br /&gt;   int msgCount;&lt;br /&gt;   if ((msgCount = folder.getMessageCount()) &amp;gt; 0) {&lt;br /&gt;    logger.info(mailbox.getUserId() + "'s " + _folder + " has " + msgCount + " messages.");&lt;br /&gt;    // "msgsPerRead" is used so the flagged messages will be purged more often&lt;br /&gt;    int msgsToRead = Math.min(msgCount, msgsPerRead);&lt;br /&gt;    // if we can't keep up, process more messages in each cycle&lt;br /&gt;    if (msgCount &amp;gt; msgsToRead * 50) {&lt;br /&gt;     msgsToRead *= 50;&lt;br /&gt;    }&lt;br /&gt;    else if (msgCount &amp;gt; msgsToRead * 10) {&lt;br /&gt;     msgsToRead *= 10;&lt;br /&gt;    }&lt;br /&gt;    else if (msgCount &amp;gt; msgsToRead * 5) {&lt;br /&gt;     msgsToRead *= 5;&lt;br /&gt;    }&lt;br /&gt;    msgsToRead = msgsToRead &amp;gt; MAX_MSGS_PER_READ ? MAX_MSGS_PER_READ : msgsToRead;&lt;br /&gt;    logger.info("number of messages to be read in this cycle: " + msgsToRead);&lt;br /&gt;    Message[] msgs = null;&lt;br /&gt;    try {&lt;br /&gt;     msgs = folder.getMessages(1, msgsToRead);&lt;br /&gt;    }&lt;br /&gt;    catch (IndexOutOfBoundsException ie) {&lt;br /&gt;     logger.error("IndexOutOfBoundsException caught, retry with getMessages()", ie);&lt;br /&gt;     msgs = folder.getMessages();&lt;br /&gt;     logger.info("Retry with folder.getMessages() is successful.");&lt;br /&gt;    }&lt;br /&gt;    execute(msgs); // process the messages read&lt;br /&gt;    folder.close(true); // "true" to delete the flagged messages&lt;br /&gt;    logger.info(msgs.length + " messages have been purged from pop3 mailbox.");&lt;br /&gt;    messagesProcessed += msgs.length;&lt;br /&gt;    long proc_time = new Date().getTime() - start_tms.getTime();&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug(msgs.length+ " messages read, time taken: " + proc_time);&lt;br /&gt;   }&lt;br /&gt;  } while (keepRunning); // end of do-while&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private void readFromImap() throws MessagingException, InterruptedException, IOException {&lt;br /&gt;  boolean keepRunning = true;&lt;br /&gt;  folder.open(Folder.READ_WRITE);&lt;br /&gt;  /*&lt;br /&gt;   * fix for some IMAP servers: some IMAP servers wouldn't pick up the&lt;br /&gt;   * existing messages, the MessageCountListener may not be implemented&lt;br /&gt;   * correctly for those servers.&lt;br /&gt;   */&lt;br /&gt;  if (folder.getMessageCount() &amp;gt; 0) {&lt;br /&gt;   logger.info(mailbox.getUserId() + "'s " + mailbox.getFolderName() + " has "&lt;br /&gt;     + folder.getMessageCount() + " messages.");&lt;br /&gt;   Date start_tms = new Date();&lt;br /&gt;   Message msgs[] = folder.getMessages();&lt;br /&gt;   execute(msgs);&lt;br /&gt;   folder.expunge(); // remove messages marked as DELETED&lt;br /&gt;   logger.info(msgs.length + " messages have been expunged from imap mailbox.");&lt;br /&gt;   long proc_time = new Date().getTime() - start_tms.getTime();&lt;br /&gt;   if (isDebugEnabled)&lt;br /&gt;    logger.debug(msgs.length+ " messages read, time taken: " + proc_time);&lt;br /&gt;  }&lt;br /&gt;  /* end of the fix */&lt;br /&gt;  while (keepRunning) {&lt;br /&gt;   Thread.sleep(pollingFreq); // sleep for "pollingFreq"&lt;br /&gt;   // This is to force the IMAP server to send us&lt;br /&gt;   // EXISTS notifications.&lt;br /&gt;   folder.getMessageCount();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * Add messageCountListener to listen to new messages for IMAP.&lt;br /&gt;  * &lt;br /&gt;  * @param folder -&lt;br /&gt;  *            a Folder object&lt;br /&gt;  * @param _folder -&lt;br /&gt;  *            folder name&lt;br /&gt;  */&lt;br /&gt; private void addMsgCountListener(final Folder folder, final String _folder) {&lt;br /&gt;  folder.addMessageCountListener(new MessageCountAdapter() {&lt;br /&gt;   private final Logger logger = Logger.getLogger(MessageCountAdapter.class);&lt;br /&gt;   public void messagesAdded(MessageCountEvent ev) {&lt;br /&gt;    Message[] msgs = ev.getMessages();&lt;br /&gt;    logger.info("Got " + msgs.length + " new messages from " + _folder);&lt;br /&gt;    Date start_tms = new Date();&lt;br /&gt;    try {&lt;br /&gt;     execute(msgs);&lt;br /&gt;     folder.expunge(); // remove messages marked as DELETED&lt;br /&gt;     logger.info(msgs.length + " messages have been expunged from imap mailbox.");&lt;br /&gt;     messagesProcessed += msgs.length;&lt;br /&gt;    }&lt;br /&gt;    catch (MessagingException ex) {&lt;br /&gt;     logger.fatal("MessagingException caught", ex);&lt;br /&gt;     throw new RuntimeException(ex.getMessage());&lt;br /&gt;    }&lt;br /&gt;    catch (IOException ex) {&lt;br /&gt;     logger.fatal("IOException caught", ex);&lt;br /&gt;     throw new RuntimeException(ex.getMessage());&lt;br /&gt;    }&lt;br /&gt;    finally {&lt;br /&gt;     long proc_time = new Date().getTime() - start_tms.getTime();&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug(msgs.length+ " messages processed, time taken: " + proc_time);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }); // end of IMAP folder.addMessageCountListener&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /*&lt;br /&gt;  * process e-mails.&lt;br /&gt;  * &lt;br /&gt;  * @param msgs -&lt;br /&gt;  *            messages to be processed.&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  * @throws IOException&lt;br /&gt;  */&lt;br /&gt; private void execute(Message[] msgs) throws IOException, MessagingException {&lt;br /&gt;  if (msgs == null || msgs.length == 0) return;&lt;br /&gt;  processor.process(msgs);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * implement ConnectionListener interface&lt;br /&gt;  * &lt;br /&gt;  * @param e -&lt;br /&gt;  *            Connection event&lt;br /&gt;  */&lt;br /&gt; public void opened(ConnectionEvent e) {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("&amp;gt;&amp;gt;&amp;gt; ConnectionListener: connection opened()");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * implement ConnectionListener interface&lt;br /&gt;  * &lt;br /&gt;  * @param e -&lt;br /&gt;  *            Connection event&lt;br /&gt;  */&lt;br /&gt; public void disconnected(ConnectionEvent e) {&lt;br /&gt;  logger.info("&amp;gt;&amp;gt;&amp;gt; ConnectionListener: connection disconnected()");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * implement ConnectionListener interface&lt;br /&gt;  * &lt;br /&gt;  * @param e -&lt;br /&gt;  *            Connection event&lt;br /&gt;  */&lt;br /&gt; public void closed(ConnectionEvent e) {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("&amp;gt;&amp;gt;&amp;gt; ConnectionListener: connection closed()");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void notification(StoreEvent e) {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("&amp;gt;&amp;gt;&amp;gt; StoreListener: notification event: " + e.getMessage());&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* end of the implementation */&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * connect to Store with retry logic.&lt;br /&gt;  * &lt;br /&gt;  * @param store&lt;br /&gt;  *            Store object&lt;br /&gt;  * @param retries&lt;br /&gt;  *            number of retries performed&lt;br /&gt;  * @param maxRetries&lt;br /&gt;  *            number of retries to be performed before giving up&lt;br /&gt;  * @throws MessagingException &lt;br /&gt;  *             when retries reached the maxRetries&lt;br /&gt;  * @throws InterruptedException &lt;br /&gt;  */&lt;br /&gt; void connect(Store store, int retries, int maxRetries) throws MessagingException,&lt;br /&gt;   InterruptedException {&lt;br /&gt;  int portnbr = mailbox.getPort();&lt;br /&gt;  // -1 to use the default port&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Port used: " + portnbr);&lt;br /&gt;  if (retries &amp;gt; 0) { // retrying, close store first&lt;br /&gt;   try {&lt;br /&gt;    store.close();&lt;br /&gt;   }&lt;br /&gt;   catch (MessagingException e) {&lt;br /&gt;    logger.error("MessagingException caught during retry on store.close()", e);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  try {&lt;br /&gt;   // connect&lt;br /&gt;   store.connect(mailbox.getHost(), portnbr, mailbox.getUserId(), mailbox.getUserPswd());&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException me) {&lt;br /&gt;   if (retries &amp;lt; maxRetries || maxRetries &amp;lt; 0) {&lt;br /&gt;    int sleepFor;&lt;br /&gt;    if (retries &amp;lt; RetryFreqs.length) {&lt;br /&gt;     sleepFor = RetryFreqs[retries];&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;     sleepFor = RETRY_FREQ;&lt;br /&gt;    }&lt;br /&gt;    logger.error("MessagingException caught during store.connect, retry(=" + retries&lt;br /&gt;      + ") in " + sleepFor + " seconds");&lt;br /&gt;    try {&lt;br /&gt;     Thread.sleep(sleepFor * 1000);&lt;br /&gt;    }&lt;br /&gt;    catch (InterruptedException e) {&lt;br /&gt;     logger.warn("InterruptedException caught", e);&lt;br /&gt;     throw e;&lt;br /&gt;    }&lt;br /&gt;    connect(store, ++retries, maxRetries);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.fatal("Exception caught during store.connect, all retries failed...");&lt;br /&gt;    throw me;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve Folder with retry logic.&lt;br /&gt;  * &lt;br /&gt;  * @param store&lt;br /&gt;  *            Store object&lt;br /&gt;  * @param retries&lt;br /&gt;  *            number of retries performed&lt;br /&gt;  * @param maxRetries&lt;br /&gt;  *            number of retries to be performed before giving up&lt;br /&gt;  * @return Folder instance&lt;br /&gt;  * @throws MessagingException &lt;br /&gt;  * @throws InterruptedException &lt;br /&gt;  */&lt;br /&gt; Folder getFolder(Store store, int retries, int maxRetries) throws MessagingException,&lt;br /&gt;   InterruptedException {&lt;br /&gt;  try {&lt;br /&gt;   // Open a Folder&lt;br /&gt;   //folder = store.getDefaultFolder();&lt;br /&gt;   folder = store.getFolder(mailbox.getFolderName());&lt;br /&gt;&lt;br /&gt;   if (folder == null || !folder.exists()) {&lt;br /&gt;    throw new MessagingException("Invalid folder " + mailbox.getFolderName());&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException me) {&lt;br /&gt;   if (retries &amp;lt; maxRetries || maxRetries &amp;lt; 0) {&lt;br /&gt;    int sleepFor;&lt;br /&gt;    if (retries &amp;lt; RetryFreqs.length) {&lt;br /&gt;     sleepFor = RetryFreqs[retries];&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;     sleepFor = RETRY_FREQ;&lt;br /&gt;    }&lt;br /&gt;    logger.error("MessagingException caught during store.getFolder, retry(=" + retries&lt;br /&gt;      + ") in " + sleepFor + " seconds");&lt;br /&gt;    try {&lt;br /&gt;     Thread.sleep(sleepFor * 1000);&lt;br /&gt;    }&lt;br /&gt;    catch (InterruptedException e) {&lt;br /&gt;     logger.warn("InterruptedException caught", e);&lt;br /&gt;     throw e;&lt;br /&gt;    }&lt;br /&gt;    return getFolder(store, ++retries, maxRetries);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.fatal("Exception caught during store.getFolder, all retries failed");&lt;br /&gt;    throw me;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return folder;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-5622174328077347377?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/5622174328077347377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/build-reliable-email-reader-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/5622174328077347377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/5622174328077347377'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/build-reliable-email-reader-part-1.html' title='Build a reliable email reader - Part 1'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-1178740433065335670</id><published>2009-09-10T11:18:00.001-04:00</published><updated>2009-09-10T11:35:15.765-04:00</updated><title type='text'>Build a reliable email reader - Part 2</title><content type='html'>Here are the two supporting classes needed by the MailReader class. The first one is the Mailbox class that is used to hold all properties of a mailbox. You will need at least three properties to initialize an instance: pop3 server address, mailbox user name, and mailbox password. Use setters to override default values of other properties. Please notice that with Sun provider only the default folder name ("INBOX") is supported.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/Mailbox.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &lt;http: licenses="" www.gnu.org=""&gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;&lt;br /&gt;public class Mailbox implements Serializable {&lt;br /&gt; private static final long serialVersionUID = 826439429623556631L;&lt;br /&gt;&lt;br /&gt; private final String userId; &lt;br /&gt; private final String userPswd;&lt;br /&gt; private final String host;&lt;br /&gt;&lt;br /&gt; private int port;&lt;br /&gt; private String protocol;&lt;br /&gt; private String folderName;&lt;br /&gt; private int messagesPerRead;&lt;br /&gt; private boolean useSsl;&lt;br /&gt; private int maxRetries;&lt;br /&gt; private int minimumWait; // in seconds&lt;br /&gt; private boolean isExchange;&lt;br /&gt; &lt;br /&gt; public Mailbox(String host, String userId, String userPswd) {&lt;br /&gt;  this.host = host;&lt;br /&gt;  this.userId = userId;&lt;br /&gt;  this.userPswd = userPswd;&lt;br /&gt;  initDefault();&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; void initDefault() {&lt;br /&gt;  port = -1; // use protocol default port&lt;br /&gt;  protocol = "pop3";&lt;br /&gt;  folderName = "INBOX";&lt;br /&gt;  messagesPerRead = 10;&lt;br /&gt;  useSsl = false;&lt;br /&gt;  maxRetries = -1;&lt;br /&gt;  minimumWait = 5;&lt;br /&gt;  isExchange = false;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getFolderName() {&lt;br /&gt;  return folderName;&lt;br /&gt; }&lt;br /&gt; public void setFolderName(String folderName) {&lt;br /&gt;  this.folderName = folderName;&lt;br /&gt; }&lt;br /&gt; public String getHost() {&lt;br /&gt;  return host;&lt;br /&gt; }&lt;br /&gt; public int getMinimumWait() {&lt;br /&gt;  return minimumWait;&lt;br /&gt; }&lt;br /&gt; public void setMinimumWait(int minimumWait) {&lt;br /&gt;  this.minimumWait = minimumWait;&lt;br /&gt; }&lt;br /&gt; public int getPort() {&lt;br /&gt;  return port;&lt;br /&gt; }&lt;br /&gt; public void setPort(int port) {&lt;br /&gt;  this.port = port;&lt;br /&gt; }&lt;br /&gt; public String getProtocol() {&lt;br /&gt;  return protocol;&lt;br /&gt; }&lt;br /&gt; public void setProtocol(String protocol) {&lt;br /&gt;  this.protocol = protocol;&lt;br /&gt; }&lt;br /&gt; public int getMessagesPerRead() {&lt;br /&gt;  return messagesPerRead;&lt;br /&gt; }&lt;br /&gt; public void setMessagesPerRead(int readPerPass) {&lt;br /&gt;  this.messagesPerRead = readPerPass;&lt;br /&gt; }&lt;br /&gt; public int getMaxRetries() {&lt;br /&gt;  return maxRetries;&lt;br /&gt; }&lt;br /&gt; public void setMaxRetries(int retryMax) {&lt;br /&gt;  this.maxRetries = retryMax;&lt;br /&gt; }&lt;br /&gt; public String getUserId() {&lt;br /&gt;  return userId;&lt;br /&gt; }&lt;br /&gt; public String getUserPswd() {&lt;br /&gt;  return userPswd;&lt;br /&gt; }&lt;br /&gt; public boolean isUseSsl() {&lt;br /&gt;  return useSsl;&lt;br /&gt; }&lt;br /&gt; public void setUseSsl(boolean useSsl) {&lt;br /&gt;  this.useSsl = useSsl;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public boolean isExchange() {&lt;br /&gt;  return isExchange;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setExchange(boolean isExchange) {&lt;br /&gt;  this.isExchange = isExchange;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/http:&gt;&lt;/pre&gt;The next class is the MailProcessor class which is used to process the email messages read by the MailReader. This is most likely the class you would customize when building your own back-end mail reader.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/MailProcessor.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.util.Date;&lt;br /&gt;&lt;br /&gt;import javax.mail.Flags;&lt;br /&gt;import javax.mail.Message;&lt;br /&gt;import javax.mail.MessagingException;&lt;br /&gt;import javax.mail.Part;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * process email's handed over by MailReader class.&lt;br /&gt; * &lt;br /&gt; * @author JackW&lt;br /&gt; */&lt;br /&gt;public class MailProcessor {&lt;br /&gt; static final Logger logger = Logger.getLogger(MailProcessor.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; protected final String LF = System.getProperty("line.separator", "\n");&lt;br /&gt; private final Mailbox mailbox;&lt;br /&gt; &lt;br /&gt; private static final int MAX_BODY_SIZE = 150 * 1024; // 150KB&lt;br /&gt; private static final int MAX_CMPT_SIZE = 1024 * 1024; // 1MB&lt;br /&gt; private static final int MAX_TOTAL_SIZE = 10 * 1024 * 1024; // 10MB&lt;br /&gt;&lt;br /&gt; public MailProcessor(Mailbox mailbox) {&lt;br /&gt;  this.mailbox = mailbox;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * process messages.&lt;br /&gt;  * &lt;br /&gt;  * @param msgs -&lt;br /&gt;  *            array of Messages.&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  * @throws IOException &lt;br /&gt;  */&lt;br /&gt; public void process(Message[] msgs) throws MessagingException, IOException {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Entering process() method...");&lt;br /&gt;  for (int i = 0; i &amp;lt; msgs.length; i++) {&lt;br /&gt;   if (msgs[i] != null &amp;amp;&amp;amp; !msgs[i].isSet(Flags.Flag.SEEN)&lt;br /&gt;     &amp;amp;&amp;amp; !msgs[i].isSet(Flags.Flag.DELETED)) {&lt;br /&gt;    processPart(msgs[i]);&lt;br /&gt;    // message has been processed, delete it from mail box&lt;br /&gt;    msgs[i].setFlag(Flags.Flag.DELETED, true);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * process message part&lt;br /&gt;  * &lt;br /&gt;  * @param p -&lt;br /&gt;  *            part&lt;br /&gt;  * @throws MessagingException &lt;br /&gt;  * @throws IOException &lt;br /&gt;  */&lt;br /&gt; MessageBean processPart(Part p) throws IOException, MessagingException {&lt;br /&gt;  Date start_tms = new Date();&lt;br /&gt;  &lt;br /&gt;  // parse the MimeMessage to MessageBean&lt;br /&gt;  MessageBean msgBean = MessageBeanUtil.mimeToBean(p);&lt;br /&gt;  &lt;br /&gt;  // MailBox Host Address&lt;br /&gt;  msgBean.setMailboxHost(mailbox.getHost());&lt;br /&gt;  // MailBox User Id&lt;br /&gt;  msgBean.setMailboxUser(mailbox.getUserId());&lt;br /&gt;  &lt;br /&gt;  // get message body&lt;br /&gt;  String body = msgBean.getBody();&lt;br /&gt;&lt;br /&gt;  // check message body and component size&lt;br /&gt;  boolean msgSizeTooLarge = false;&lt;br /&gt;  if (body.length() &amp;gt; MAX_BODY_SIZE) {&lt;br /&gt;   msgSizeTooLarge = true;&lt;br /&gt;   logger.warn("Message body size exceeded limit: " + body.length());&lt;br /&gt;  }&lt;br /&gt;  int totalSize = body.length();&lt;br /&gt;  if (!msgSizeTooLarge &amp;amp;&amp;amp; msgBean.getComponentsSize().size() &amp;gt; 0) {&lt;br /&gt;   for (int i = 0; i &amp;lt; msgBean.getComponentsSize().size(); i++) {&lt;br /&gt;    Integer objSize = (Integer) msgBean.getComponentsSize().get(i);&lt;br /&gt;    if (objSize.intValue() &amp;gt; MAX_CMPT_SIZE) {&lt;br /&gt;     msgSizeTooLarge = true;&lt;br /&gt;     logger.warn("Message component(" + i + ") exceeded limit: " + objSize.intValue());&lt;br /&gt;     break;&lt;br /&gt;    }&lt;br /&gt;    totalSize += objSize;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (!msgSizeTooLarge &amp;amp;&amp;amp; totalSize &amp;gt; MAX_TOTAL_SIZE) {&lt;br /&gt;   logger.warn("Message total size exceeded limit: " + totalSize);&lt;br /&gt;   msgSizeTooLarge = true;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  if (msgSizeTooLarge) {&lt;br /&gt;   logger.error("The email message has been rejected due to its size");&lt;br /&gt;   // XXX - add your code here to deal with it&lt;br /&gt;  }&lt;br /&gt;  else { // email size within the limit&lt;br /&gt;   if (msgBean.getSmtpMessageId() == null) {&lt;br /&gt;    logger.warn("SMTP Message-Id is null, FROM Address = " + msgBean.getFromAsString());&lt;br /&gt;   }&lt;br /&gt;   if (isDebugEnabled)&lt;br /&gt;    logger.debug("Message read..." + LF + msgBean);&lt;br /&gt;   // XXX: Add you code here to process the message ...&lt;br /&gt;  }&lt;br /&gt;  if (isDebugEnabled &amp;amp;&amp;amp; msgBean.getAttachCount() &amp;gt; 0)&lt;br /&gt;   logger.debug("Number of attachments receibved: " + msgBean.getAttachCount());&lt;br /&gt;&lt;br /&gt;  long time_spent = new Date().getTime() - start_tms.getTime();&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Msg from " + msgBean.getFromAsString() + " processed, " + time_spent);&lt;br /&gt;  &lt;br /&gt;  return msgBean;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-1178740433065335670?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/1178740433065335670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/build-reliable-email-reader-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/1178740433065335670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/1178740433065335670'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/build-reliable-email-reader-part-2.html' title='Build a reliable email reader - Part 2'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-2210989880426852544</id><published>2009-09-04T14:23:00.000-04:00</published><updated>2009-09-04T14:24:28.787-04:00</updated><title type='text'>Simple IBM MQ Queue Depth Monitor</title><content type='html'>For those that are still running IBM MQ (or Websphere MQ) on mainframes, presented here is a simple java queue depth monitor program that polls a queue manager periodically for the number of messages in a queue. The program utilizes the classes provided in the PCF package  to get the queue depth information. This sample code will output a message to the console when the queue contains more than 10  messages at the moment of polling. You can change it to send out an email or snmp alert instead.&lt;br /&gt;In order to compile and run this program, PCF package (MS0B) is needed and can be &lt;a href="http://www-01.ibm.com/support/docview.wss?rs=171&amp;amp;uid=swg24000668&amp;amp;loc=en_US&amp;amp;cs=utf-8&amp;amp;lang=en"&gt;downloaded from IBM&lt;/a&gt;. You will also need "com.ibm.mq.jar" and IBM's "j2ee.jar". They can be found from IBM's WSAD or RAD IDE development tools.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/ibmmq/QueueDepthMonitor.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.ibmmq;&lt;br /&gt;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;import com.ibm.mq.MQException;&lt;br /&gt;import com.ibm.mq.pcf.CMQC;&lt;br /&gt;import com.ibm.mq.pcf.CMQCFC;&lt;br /&gt;import com.ibm.mq.pcf.PCFException;&lt;br /&gt;import com.ibm.mq.pcf.PCFMessage;&lt;br /&gt;import com.ibm.mq.pcf.PCFMessageAgent;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Simple queue depth monitor program that uses PCFAgent to generate and parse&lt;br /&gt; * a PCF query.&lt;br /&gt; */&lt;br /&gt;public class QueueDepthMonitor implements Runnable {&lt;br /&gt; protected static Logger logger = Logger.getLogger(QueueDepthMonitor.class);&lt;br /&gt; protected static boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; final String qmgrName;&lt;br /&gt; final String host;&lt;br /&gt; final int port;&lt;br /&gt; final String channel;&lt;br /&gt; final String queueName;&lt;br /&gt; final int alertDepth;&lt;br /&gt;&lt;br /&gt; final static int Polling_Freq = 30 * 1000; // 30 seconds&lt;br /&gt;&lt;br /&gt; QueueDepthMonitor(String name, String host, String port, String channel, String queueName,&lt;br /&gt;   int alertDepth) {&lt;br /&gt;  this.qmgrName = name;&lt;br /&gt;  this.host = host;&lt;br /&gt;  this.channel = channel;&lt;br /&gt;  this.port = Integer.parseInt(port);&lt;br /&gt;  this.queueName = queueName;&lt;br /&gt;  this.alertDepth = alertDepth;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void run() {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Starting Queue Depth monitor for " + queueName + "...");&lt;br /&gt;  while (true) {&lt;br /&gt;   checkDepth();&lt;br /&gt;   try {&lt;br /&gt;    Thread.sleep(Polling_Freq); // sleep for 30 seconds&lt;br /&gt;   }&lt;br /&gt;   catch (InterruptedException e) {&lt;br /&gt;    logger.info("The monitor has been interrupted, exit...");&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private void checkDepth() {&lt;br /&gt;  PCFMessageAgent agent = null;&lt;br /&gt;  int[] attrs = { CMQC.MQCA_Q_NAME, CMQC.MQIA_CURRENT_Q_DEPTH };&lt;br /&gt;  PCFMessage request = new PCFMessage(CMQCFC.MQCMD_INQUIRE_Q);&lt;br /&gt;  request.addParameter(CMQC.MQCA_Q_NAME, queueName);&lt;br /&gt;  request.addParameter(CMQC.MQIA_Q_TYPE, CMQC.MQQT_LOCAL);&lt;br /&gt;  request.addParameter(CMQCFC.MQIACF_Q_ATTRS, attrs);&lt;br /&gt;  PCFMessage[] responses;&lt;br /&gt;&lt;br /&gt;  if (isDebugEnabled) {&lt;br /&gt;   logger.debug("Connecting to " + qmgrName + " at " + host + ":" + port + " over " + channel);&lt;br /&gt;  }&lt;br /&gt;  try {&lt;br /&gt;   // Connect a PCFAgent to the queue manager&lt;br /&gt;   agent = new PCFMessageAgent(host, port, channel);&lt;br /&gt;   // Use the agent to send the request&lt;br /&gt;   responses = agent.send(request);&lt;br /&gt;   // retrieving queue depth&lt;br /&gt;   for (int i = 0; i &amp;lt; responses.length; i++) {&lt;br /&gt;    String name = responses[i].getStringParameterValue(CMQC.MQCA_Q_NAME);&lt;br /&gt;    int depth = responses[i].getIntParameterValue(CMQC.MQIA_CURRENT_Q_DEPTH);&lt;br /&gt;    if (isDebugEnabled &amp;amp;&amp;amp; name != null)&lt;br /&gt;     logger.debug("Queue " + name + " Depth " + depth);&lt;br /&gt;    if (name != null &amp;amp;&amp;amp; queueName.equals(name.trim())) { // just for safety&lt;br /&gt;     if (depth &amp;gt; alertDepth) {&lt;br /&gt;      logger.info(qmgrName + "/" + queueName + " depth = " + depth&lt;br /&gt;        + ", exceeded alert threshold: " + alertDepth);&lt;br /&gt;      // XXX: add your code here to send out alert&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (PCFException pcfe) {&lt;br /&gt;   logger.error("PCFException caught", pcfe);&lt;br /&gt;   PCFMessage[] msgs = (PCFMessage[]) pcfe.exceptionSource;&lt;br /&gt;   for (int i = 0; i &amp;lt; msgs.length; i++) {&lt;br /&gt;    logger.error(msgs[i]);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MQException mqe) {&lt;br /&gt;   logger.error("MQException caught", mqe);&lt;br /&gt;  }&lt;br /&gt;  catch (IOException ioe) {&lt;br /&gt;   logger.error("IOException caught", ioe);&lt;br /&gt;  }&lt;br /&gt;  finally {&lt;br /&gt;   // Disconnect&lt;br /&gt;   if (agent != null) {&lt;br /&gt;    try {&lt;br /&gt;     agent.disconnect();&lt;br /&gt;    }&lt;br /&gt;    catch (Exception e) {&lt;br /&gt;     logger.error("Exception caught during disconnect", e);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.warn("unable to disconnect, agent is null.");&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  String qmgrName = "QMGR";&lt;br /&gt;  String host = "localhost";&lt;br /&gt;  String port = "1450";&lt;br /&gt;  String channel = "SYSTEM.DEF.SVRCONN";&lt;br /&gt;  String queueName = "TEST_QUEUE";&lt;br /&gt;&lt;br /&gt;  QueueDepthMonitor monitor = new QueueDepthMonitor(qmgrName, host, port, channel, queueName, 10);&lt;br /&gt;  new Thread(monitor).start();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-2210989880426852544?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/2210989880426852544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/simple-ibm-mq-queue-depth-monitor.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2210989880426852544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2210989880426852544'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/simple-ibm-mq-queue-depth-monitor.html' title='Simple IBM MQ Queue Depth Monitor'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-7924490885333471204</id><published>2009-09-04T09:30:00.000-04:00</published><updated>2009-09-04T09:30:26.431-04:00</updated><title type='text'>Detect bounced emails - Part 3</title><content type='html'>Now it's the time for the bounce finder class. A simple method called "parse" is provided in the class that takes a message bean as input and returns a bounce type. This method will always return a value even when the email is not a rejected message, the word "GENERIC" is returned in this case.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/BounceFinder.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.ByteArrayInputStream;&lt;br /&gt;import java.io.ByteArrayOutputStream;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;import java.util.Date;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.StringTokenizer;&lt;br /&gt;&lt;br /&gt;import javax.mail.Address;&lt;br /&gt;import javax.mail.internet.AddressException;&lt;br /&gt;import javax.mail.internet.InternetAddress;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;import blog.javaclue.javamail.SmtpScanner.BOUNCE_TYPES;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Scan email header and body, and match rules to determine the bounce type.&lt;br /&gt; * &lt;br /&gt; * @author jackw&lt;br /&gt; */&lt;br /&gt;public final class BounceFinder {&lt;br /&gt; static final Logger logger = Logger.getLogger(BounceFinder.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; private final SmtpScanner rfcScan;&lt;br /&gt;&lt;br /&gt; static final String TEN_DASHES = "----------";&lt;br /&gt; static final String ORIGMSG_SEPARATOR = "-----Original Message-----";&lt;br /&gt; static final String REPLY_SEPARATOR = "---------Reply Separator---------";&lt;br /&gt; static final String LF = System.getProperty("line.separator", "\n");&lt;br /&gt;&lt;br /&gt; public final static String VERP_BOUNCE_ADDR_XHEADER = "X-VERP_Bounce_Addr";&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * default constructor&lt;br /&gt;  */&lt;br /&gt; public BounceFinder() throws IOException {&lt;br /&gt;  rfcScan = SmtpScanner.getInstance();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Scans email properties to find out the bounce type. It also checks VERP&lt;br /&gt;  * headers to get original recipient.&lt;br /&gt;  * &lt;br /&gt;  * @param msgBean&lt;br /&gt;  *            a MessageBean instance&lt;br /&gt;  */&lt;br /&gt; public String parse(MessageBean msgBean) {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("Entering parse() method...");&lt;br /&gt;  String bounceType = null;&lt;br /&gt;  &lt;br /&gt;  // retrieve attachments into an array, it also gathers rfc822/Delivery Status.&lt;br /&gt;  BodypartUtil.retrieveAttachments(msgBean);&lt;br /&gt;&lt;br /&gt;  // scan message for Enhanced Mail System Status Code (rfc1893/rfc3464)&lt;br /&gt;  BodypartBean aNode = null;&lt;br /&gt;  if (msgBean.getReport() != null) {&lt;br /&gt;   /*&lt;br /&gt;    * multipart/report mime type is present, retrieve DSN/MDN report.&lt;br /&gt;    */&lt;br /&gt;   MessageNode mNode = msgBean.getReport();&lt;br /&gt;   // locate message/delivery-status section&lt;br /&gt;   aNode = BodypartUtil.retrieveDlvrStatus(mNode.getBodypartNode(), mNode.getLevel());&lt;br /&gt;   if (aNode != null) {&lt;br /&gt;    // first scan message/delivery-status&lt;br /&gt;    byte[] attchValue = (byte[]) aNode.getValue();&lt;br /&gt;    if (attchValue != null) {&lt;br /&gt;     if (isDebugEnabled) {&lt;br /&gt;      logger.debug("parse() - scan message/report status -----&amp;lt;" + LF + new String(attchValue) + "&amp;gt;-----");&lt;br /&gt;     }&lt;br /&gt;     if (bounceType == null) {&lt;br /&gt;      bounceType = rfcScan.scanBody(new String(attchValue));&lt;br /&gt;     }&lt;br /&gt;     parseDsn(attchValue, msgBean);&lt;br /&gt;     msgBean.setDsnDlvrStat(new String(attchValue));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else if ((aNode = BodypartUtil.retrieveMDNReceipt(mNode.getBodypartNode(), mNode.getLevel())) != null) {&lt;br /&gt;    // got message/disposition-notification&lt;br /&gt;    byte[] attchValue = (byte[]) aNode.getValue();&lt;br /&gt;    if (attchValue != null) {&lt;br /&gt;     if (isDebugEnabled) {&lt;br /&gt;      logger.debug("parse() - display message/report status -----&amp;lt;" + LF + new String(attchValue) + "&amp;gt;-----");&lt;br /&gt;     }&lt;br /&gt;     if (bounceType == null) {&lt;br /&gt;      bounceType = BOUNCE_TYPES.MDN_RECEIPT.toString();&lt;br /&gt;     }&lt;br /&gt;     // MDN comes with original and final recipients&lt;br /&gt;     parseDsn(attchValue, msgBean);&lt;br /&gt;     msgBean.setDsnDlvrStat(new String(attchValue));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    // missing message/* section, try text/plain&lt;br /&gt;    List&amp;lt;BodypartBean&amp;gt; nodes = BodypartUtil.retrieveReportText(mNode.getBodypartNode(), mNode.getLevel());&lt;br /&gt;    if (!nodes.isEmpty()) {&lt;br /&gt;     ByteArrayOutputStream baos = new ByteArrayOutputStream(); &lt;br /&gt;     for (BodypartBean bodyPart : nodes) {&lt;br /&gt;      byte[] attchValue = (byte[]) bodyPart.getValue();&lt;br /&gt;      try {&lt;br /&gt;       baos.write(attchValue);&lt;br /&gt;      }&lt;br /&gt;      catch (IOException e) {&lt;br /&gt;       logger.error("IOException caught", e);&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;     try {&lt;br /&gt;      baos.close();&lt;br /&gt;     }&lt;br /&gt;     catch (IOException e) {}&lt;br /&gt;     byte[] attchValue = baos.toByteArray();&lt;br /&gt;     if (attchValue != null) {&lt;br /&gt;      if (isDebugEnabled) {&lt;br /&gt;       logger.debug("parse() - scan message/report text -----&amp;lt;" + LF + new String(attchValue) + "&amp;gt;-----");&lt;br /&gt;      }&lt;br /&gt;      if (bounceType == null) {&lt;br /&gt;       bounceType = rfcScan.scanBody(new String(attchValue));&lt;br /&gt;      }&lt;br /&gt;      parseDsn(attchValue, msgBean);&lt;br /&gt;      msgBean.setDsnText(new String(attchValue));&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   // locate possible message/rfc822 section under multipart/report&lt;br /&gt;   aNode = BodypartUtil.retrieveMessageRfc822(mNode.getBodypartNode(), mNode.getLevel());&lt;br /&gt;   if (aNode != null &amp;amp;&amp;amp; msgBean.getRfc822() == null) {&lt;br /&gt;    msgBean.setRfc822(new MessageNode(aNode, mNode.getLevel()));&lt;br /&gt;   }&lt;br /&gt;   // locate possible text/rfc822-headers section under multipart/report&lt;br /&gt;   aNode = BodypartUtil.retrieveRfc822Headers(mNode.getBodypartNode(), mNode.getLevel());&lt;br /&gt;   if (aNode != null &amp;amp;&amp;amp; msgBean.getRfc822() == null) {&lt;br /&gt;    msgBean.setRfc822(new MessageNode(aNode, mNode.getLevel()));&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (msgBean.getRfc822() != null) {&lt;br /&gt;   /*&lt;br /&gt;    * message/rfc822 is present, retrieve RFC report.&lt;br /&gt;    */&lt;br /&gt;   MessageNode mNode = msgBean.getRfc822();&lt;br /&gt;   aNode = BodypartUtil.retrieveRfc822Text(mNode.getBodypartNode(), mNode.getLevel());&lt;br /&gt;   if (aNode != null) {&lt;br /&gt;    StringBuffer sb = new StringBuffer();&lt;br /&gt;    // get original message headers&lt;br /&gt;    List&amp;lt;MsgHeader&amp;gt; vheader = aNode.getHeaders();&lt;br /&gt;    for (int i = 0; vheader != null &amp;amp;&amp;amp; i &amp;lt; vheader.size(); i++) {&lt;br /&gt;     MsgHeader header = vheader.get(i);&lt;br /&gt;     sb.append(header.getName() + ": " + header.getValue() + LF);&lt;br /&gt;    }&lt;br /&gt;    boolean foundAll = false;&lt;br /&gt;    String rfcHeaders = sb.toString();&lt;br /&gt;    if (!StringUtil.isEmpty(rfcHeaders)) {&lt;br /&gt;     // rfc822 headers&lt;br /&gt;     if (isDebugEnabled) {&lt;br /&gt;      logger.debug("parse() - scan rfc822 headers -----&amp;lt;" + LF + rfcHeaders + "&amp;gt;-----");&lt;br /&gt;     }&lt;br /&gt;     foundAll = parseRfc(rfcHeaders, msgBean);&lt;br /&gt;     msgBean.setDsnRfc822(rfcHeaders);&lt;br /&gt;    }&lt;br /&gt;    byte[] attchValue = (byte[]) aNode.getValue();&lt;br /&gt;    if (attchValue != null) {&lt;br /&gt;     // rfc822 text&lt;br /&gt;     String rfcText = new String(attchValue);&lt;br /&gt;     sb.append(rfcText);&lt;br /&gt;     String mtype = aNode.getMimeType();&lt;br /&gt;     if (mtype.startsWith("text/") || mtype.startsWith("message/")) {&lt;br /&gt;      if (foundAll == false) {&lt;br /&gt;       if (isDebugEnabled) {&lt;br /&gt;        logger.debug("parse() - scan rfc822 text -----&amp;lt;" + LF + rfcText + "&amp;gt;-----");&lt;br /&gt;       }&lt;br /&gt;       parseRfc(rfcText, msgBean);&lt;br /&gt;       msgBean.setDsnRfc822(sb.toString());&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;     if (msgBean.getDsnText() == null) {&lt;br /&gt;      msgBean.setDsnText(rfcText);&lt;br /&gt;     }&lt;br /&gt;     else {&lt;br /&gt;      msgBean.setDsnText(msgBean.getDsnText() + LF + LF + "RFC822 Text:" + LF + rfcText);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    if (bounceType == null) {&lt;br /&gt;     bounceType = rfcScan.scanBody(sb.toString());&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  } // end of RFC Scan&lt;br /&gt;&lt;br /&gt;  String body = msgBean.getBody();&lt;br /&gt;  if (msgBean.getRfc822() != null &amp;amp;&amp;amp; bounceType == null) {&lt;br /&gt;   // message/rfc822 is present, scan message body for rfc1893 status code&lt;br /&gt;   // TODO: may cause false positives. need to revisit this.&lt;br /&gt;   if (isDebugEnabled)&lt;br /&gt;    logger.debug("parse() - scan body text -----&amp;lt;" + LF + body + "&amp;gt;-----");&lt;br /&gt;   bounceType = rfcScan.scanBody(body);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // check CC/BCC&lt;br /&gt;  if (bounceType == null) {&lt;br /&gt;   // if the "real_to" address is not found in envelope, but is&lt;br /&gt;   // included in CC or BCC: set bounceType to CC_USER&lt;br /&gt;   for (int i = 0; msgBean.getTo() != null &amp;amp;&amp;amp; i &amp;lt; msgBean.getTo().length; i++) {&lt;br /&gt;    Address to = msgBean.getTo()[i];&lt;br /&gt;    if (containsNoAddress(msgBean.getToEnvelope(), to)) {&lt;br /&gt;     if (containsAddress(msgBean.getCc(), to)&lt;br /&gt;       || containsAddress(msgBean.getBcc(), to)) {&lt;br /&gt;      bounceType = BOUNCE_TYPES.CC_USER.toString();&lt;br /&gt;      break;&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // check VERP bounce address, set bounce type to SOFT_BOUNCE if VERP recipient found&lt;br /&gt;  List&amp;lt;MsgHeader&amp;gt; headers = msgBean.getHeaders();&lt;br /&gt;  for (MsgHeader header : headers) {&lt;br /&gt;   if (VERP_BOUNCE_ADDR_XHEADER.equals(header.getName())) {&lt;br /&gt;    logger.info("parse() - VERP Recipient found: ==&amp;gt;" + header.getValue() + "&amp;lt;==");&lt;br /&gt;    if (msgBean.getOrigRcpt() != null &amp;amp;&amp;amp; !StringUtil.isEmpty(header.getValue())&lt;br /&gt;      &amp;amp;&amp;amp; !msgBean.getOrigRcpt().equalsIgnoreCase(header.getValue())) {&lt;br /&gt;     logger.warn("parse() - replace original recipient: " + msgBean.getOrigRcpt()&lt;br /&gt;       + " with VERP recipient: " + header.getValue());&lt;br /&gt;    }&lt;br /&gt;    if (!StringUtil.isEmpty(header.getValue())) {&lt;br /&gt;     // VERP Bounce - always override&lt;br /&gt;     msgBean.setOrigRcpt(header.getValue());&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;     logger.warn("parse() - " + VERP_BOUNCE_ADDR_XHEADER + " Header found, but it has no value.");&lt;br /&gt;    }&lt;br /&gt;    if (bounceType == null) {&lt;br /&gt;     // a bounced mail shouldn't have Return-Path&lt;br /&gt;     String rPath = msgBean.getReturnPath() == null ? "" : msgBean.getReturnPath();&lt;br /&gt;     if (StringUtil.isEmpty(rPath) || "&amp;lt;&amp;gt;".equals(rPath.trim())) {&lt;br /&gt;      bounceType = BOUNCE_TYPES.SOFT_BOUNCE.toString();&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // if it's hard or soft bounce and no final recipient was found, scan&lt;br /&gt;  // message body for final recipient using known patterns.&lt;br /&gt;  if (BOUNCE_TYPES.HARD_BOUNCE.toString().equals(bounceType)&lt;br /&gt;    || BOUNCE_TYPES.SOFT_BOUNCE.toString().equals(bounceType)) {&lt;br /&gt;   if (StringUtil.isEmpty(msgBean.getFinalRcpt())&lt;br /&gt;     &amp;amp;&amp;amp; StringUtil.isEmpty(msgBean.getOrigRcpt())) {&lt;br /&gt;    String finalRcpt = BounceAddressFinder.getInstance().find(body);&lt;br /&gt;    if (!StringUtil.isEmpty(finalRcpt)) {&lt;br /&gt;     logger.info("parse() - Final Recipient found from message body: " + finalRcpt);&lt;br /&gt;     msgBean.setFinalRcpt(finalRcpt);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  if (bounceType == null) { // use default&lt;br /&gt;   bounceType = SmtpScanner.BOUNCETYPE.GENERIC.toString();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  logger.info("parse() - bounceType: " + bounceType);&lt;br /&gt;&lt;br /&gt;  return bounceType;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private boolean containsAddress(Address[] addrs, Address to) {&lt;br /&gt;  if (to != null &amp;amp;&amp;amp; addrs != null &amp;amp;&amp;amp; addrs.length &amp;gt; 0) {&lt;br /&gt;   for (int i = 0; i &amp;lt; addrs.length; i++) {&lt;br /&gt;    if (to.equals(addrs[i])) {&lt;br /&gt;     return true;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return false;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private boolean containsNoAddress(Address[] addrs, Address to) {&lt;br /&gt;  if (to != null &amp;amp;&amp;amp; addrs != null &amp;amp;&amp;amp; addrs.length &amp;gt; 0) {&lt;br /&gt;   for (int i = 0; i &amp;lt; addrs.length; i++) {&lt;br /&gt;    if (to.equals(addrs[i])) {&lt;br /&gt;     return false;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Parse the message/delivery-status to retrieve DSN fields. Also used by&lt;br /&gt;  * message/disposition-notification to retrieve final recipient.&lt;br /&gt;  * &lt;br /&gt;  * @param attchValue -&lt;br /&gt;  *            delivery status text&lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *            MessageBean object&lt;br /&gt;  */&lt;br /&gt; private void parseDsn(byte[] attchValue, MessageBean msgBean) {&lt;br /&gt;  // retrieve Final-Recipient, Action, and Status&lt;br /&gt;  ByteArrayInputStream bais = new ByteArrayInputStream(attchValue);&lt;br /&gt;  BufferedReader br = new BufferedReader(new InputStreamReader(bais));&lt;br /&gt;  String line = null;&lt;br /&gt;  try {&lt;br /&gt;   while ((line = br.readLine()) != null) {&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug("parseDsn() - Line: " + line);&lt;br /&gt;    line = line.trim();&lt;br /&gt;    if (line.toLowerCase().startsWith("final-recipient:")) {&lt;br /&gt;     // "Final-Recipient" ":" address-type ";" generic-address&lt;br /&gt;     // address-type = rfc822 / unknown&lt;br /&gt;     StringTokenizer st = new StringTokenizer(line, " ;");&lt;br /&gt;     while (st.hasMoreTokens()) {&lt;br /&gt;      String token = st.nextToken().trim();&lt;br /&gt;      if (token.indexOf("@") &amp;gt; 0) {&lt;br /&gt;       msgBean.setFinalRcpt(token);&lt;br /&gt;       logger.info("parseDsn() - Final_Recipient found: ==&amp;gt;" + token + "&amp;lt;==");&lt;br /&gt;       break;&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("original-recipient:")) {&lt;br /&gt;     // "Original-Recipient" ":" address-type ";" generic-address&lt;br /&gt;     StringTokenizer st = new StringTokenizer(line, " ;");&lt;br /&gt;     while (st.hasMoreTokens()) {&lt;br /&gt;      String token = st.nextToken().trim();&lt;br /&gt;      if (token.indexOf("@") &amp;gt; 0) {&lt;br /&gt;       msgBean.setOrigRcpt(token);&lt;br /&gt;       logger.info("parseDsn() - Original_Recipient found: ==&amp;gt;" + token + "&amp;lt;==");&lt;br /&gt;       break;&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("action:")) {&lt;br /&gt;     /**&lt;br /&gt;      * "Action" ":" action-value = &lt;br /&gt;      * 1) failed - could not be delivered to the recipient.&lt;br /&gt;      * 2) delayed - the reporting MTA has so far been unable to deliver&lt;br /&gt;      *  or relay the message.&lt;br /&gt;      * 3) delivered - the message was successfully delivered.&lt;br /&gt;      * 4) relayed - the message has been relayed or gatewayed.&lt;br /&gt;      * 5) expanded - delivered and forwarded by reporting MTA to multiple&lt;br /&gt;      *  additional recipient addresses.&lt;br /&gt;      */ &lt;br /&gt;     String action = line.substring(7).trim();&lt;br /&gt;     msgBean.setDsnAction(action);&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug("parseDsn() - Action found: ==&amp;gt;" + action + "&amp;lt;==");&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("status:")) {&lt;br /&gt;     // "Status" ":" status-code (digit "." 1*3digit "." 1*3 digit)&lt;br /&gt;     String status = line.substring(7).trim();&lt;br /&gt;     if (status.indexOf(" ") &amp;gt; 0) {&lt;br /&gt;      status = status.substring(0, status.indexOf(" "));&lt;br /&gt;     }&lt;br /&gt;     msgBean.setDsnStatus(status);&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug("parseDsn() - Status found: ==&amp;gt;" + status + "&amp;lt;==");&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("diagnostic-code:")) {&lt;br /&gt;     // "Diagnostic-Code" ":" diagnostic-code&lt;br /&gt;     String diagcode = line.substring(16).trim();&lt;br /&gt;     msgBean.setDiagnosticCode(diagcode);&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug("parseDsn() - Diagnostic-Code: found: ==&amp;gt;" + diagcode + "&amp;lt;==");&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;   logger.error("IOException caught during parseDsn()", e);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * parse message/rfc822 to retrieve original email properties: final&lt;br /&gt;  * recipient, original subject and original SMTP message-id.&lt;br /&gt;  * &lt;br /&gt;  * @param rfc_text -&lt;br /&gt;  *            rfc822 text&lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *            MessageBean object&lt;br /&gt;  * @return true if all three properties were found&lt;br /&gt;  */&lt;br /&gt; private boolean parseRfc(String rfc_text, MessageBean msgBean) {&lt;br /&gt;  // retrieve original To address&lt;br /&gt;  ByteArrayInputStream bais = new ByteArrayInputStream(rfc_text.getBytes());&lt;br /&gt;  BufferedReader br = new BufferedReader(new InputStreamReader(bais));&lt;br /&gt;  int lineCount = 0;&lt;br /&gt;  boolean gotToAddr = false, gotSubj = false, gotSmtpId = false;&lt;br /&gt;   // allows to quit scan once all three headers are found&lt;br /&gt;  String line = null;&lt;br /&gt;  try {&lt;br /&gt;   while ((line = br.readLine()) != null) {&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug("parseRfc() - Line: " + line);&lt;br /&gt;    line = line.trim();&lt;br /&gt;    if (line.toLowerCase().startsWith("to:")) {&lt;br /&gt;     // "To" ":" generic-address&lt;br /&gt;     String token = line.substring(3).trim();&lt;br /&gt;     if (StringUtil.isEmpty(msgBean.getFinalRcpt())) {&lt;br /&gt;      msgBean.setFinalRcpt(token);&lt;br /&gt;     }&lt;br /&gt;     else if (StringUtil.compareEmailAddrs(msgBean.getFinalRcpt(), token) != 0) {&lt;br /&gt;      logger.error("parseRfc() - Final_Rcpt from RFC822: " + token + " is different from DSN's: " + msgBean.getFinalRcpt());&lt;br /&gt;     }&lt;br /&gt;     logger.info("parseRfc() - Final_Recipient(RFC822 To) found: ==&amp;gt;" + token + "&amp;lt;==");&lt;br /&gt;     gotToAddr = true;&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("subject:")) {&lt;br /&gt;     // "Subject" ":" subject text&lt;br /&gt;     String token = line.substring(8).trim();&lt;br /&gt;     if (StringUtil.isEmpty(msgBean.getOrigSubject())) {&lt;br /&gt;      msgBean.setOrigSubject(token);&lt;br /&gt;     }&lt;br /&gt;     logger.info("parseRfc() - Original_Subject(RFC822 To) found: ==&amp;gt;" + token + "&amp;lt;==");&lt;br /&gt;     gotSubj = true;&lt;br /&gt;    }&lt;br /&gt;    else if (line.toLowerCase().startsWith("message-id:")) {&lt;br /&gt;     // "Message-Id" ":" SMTP message id&lt;br /&gt;     String token = line.substring(11).trim();&lt;br /&gt;     if (StringUtil.isEmpty(msgBean.getSmtpMessageId())) {&lt;br /&gt;      msgBean.setRfcMessageId(token);&lt;br /&gt;     }&lt;br /&gt;     logger.info("parseRfc() - Smtp Message-Id(RFC822 To) found: ==&amp;gt;" + token + "&amp;lt;==");&lt;br /&gt;     gotSmtpId = true;&lt;br /&gt;    }&lt;br /&gt;    if (gotToAddr &amp;amp;&amp;amp; gotSubj &amp;amp;&amp;amp; gotSmtpId) {&lt;br /&gt;     return true;&lt;br /&gt;    }&lt;br /&gt;    if (++lineCount &amp;gt; 100 &amp;amp;&amp;amp; line.indexOf(":") &amp;lt; 0) {&lt;br /&gt;     break; // check if it's a header after 100 lines&lt;br /&gt;    }&lt;br /&gt;   } // end of while&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;   logger.error("IOException caught during parseRfc()", e);&lt;br /&gt;  }&lt;br /&gt;  return false;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  try {&lt;br /&gt;   BounceFinder parser = new BounceFinder();&lt;br /&gt;   MessageBean mBean = new MessageBean();&lt;br /&gt;   try {&lt;br /&gt;    mBean.setFrom(InternetAddress.parse("event.alert@localhost", false));&lt;br /&gt;    mBean.setTo(InternetAddress.parse("abc@domain.com", false));&lt;br /&gt;   }&lt;br /&gt;   catch (AddressException e) {&lt;br /&gt;    logger.error("AddressException caught", e);&lt;br /&gt;   }&lt;br /&gt;   mBean.setSubject("A Exception occured");&lt;br /&gt;   mBean.setValue(new Date()+ " 5.2.2 Invalid user account.");&lt;br /&gt;   mBean.setMailboxUser("testUser");&lt;br /&gt;   String bType = parser.parse(mBean);&lt;br /&gt;   System.out.println("### Bounce Type: " + bType);&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;After an email is found to be a rejected message, the bounce finder class will also try to find the email address of the intended recipient, as this could be very important in case you want to remove the address from your mailing list. You can get the address by calling getOrigRcpt() or getFinalRcpt() of MessageBean class.&lt;br /&gt;Since there are still many popular mail servers that are not strictly follow the RFC standards, a bounce address finder class is provided to locate the original recipient from message body, in case the bounce finder class failed to find it from RFC components. The patterns are not exact science, rather they are derived from limited samples of rejected emails. Take that into account when you adapt this bounce address finder.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/BounceAddressFinder.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.regex.Matcher;&lt;br /&gt;import java.util.regex.Pattern;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;public final class BounceAddressFinder {&lt;br /&gt; static final Logger logger = Logger.getLogger(BounceAddressFinder.class);&lt;br /&gt; static final boolean isDebugEnabled = false; //logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; private final List&amp;lt;MyPattern&amp;gt; patternList = new ArrayList&amp;lt;MyPattern&amp;gt;();&lt;br /&gt; private static BounceAddressFinder addressFinder = null;&lt;br /&gt; &lt;br /&gt; private BounceAddressFinder() {&lt;br /&gt;  if (patternList.isEmpty()) {&lt;br /&gt;   loadPatterns();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static synchronized BounceAddressFinder getInstance() {&lt;br /&gt;  if (addressFinder == null) {&lt;br /&gt;   addressFinder = new BounceAddressFinder();&lt;br /&gt;  }&lt;br /&gt;  return addressFinder;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public String find(String body) {&lt;br /&gt;  if (body != null &amp;amp;&amp;amp; body.trim().length() &amp;gt; 0) {&lt;br /&gt;   for (MyPattern myPattern : patternList) {&lt;br /&gt;    Matcher m = myPattern.getPattern().matcher(body);&lt;br /&gt;    if (m.find()) {&lt;br /&gt;     if (isDebugEnabled) {&lt;br /&gt;      for (int i = 1; i &amp;lt;= m.groupCount(); i++) {&lt;br /&gt;       logger.debug(myPattern.getPatternName() + ", group(" + i + ") - " + m.group(i));&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;     return m.group(m.groupCount());&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static final class MyPattern {&lt;br /&gt;  private final String patternName;&lt;br /&gt;  private final String patternRegex;&lt;br /&gt;  private final Pattern pattern;&lt;br /&gt;  MyPattern(String name, String value) {&lt;br /&gt;   this.patternName = name;&lt;br /&gt;   this.patternRegex = value;&lt;br /&gt;   pattern = Pattern.compile(patternRegex, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public Pattern getPattern() {&lt;br /&gt;   return pattern;&lt;br /&gt;  }&lt;br /&gt;  public String getPatternName() {&lt;br /&gt;   return patternName;&lt;br /&gt;  }&lt;br /&gt;  public String getPatternRegex() {&lt;br /&gt;   return patternRegex;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private final void loadPatterns() {&lt;br /&gt;  String bodyGmail = &lt;br /&gt;   "Delivery .{4,10} following recipient(?:s)? failed[\\.|\\s](?:permanently:)?\\s+" +&lt;br /&gt;   "&amp;lt;?(" + StringUtil.getEmailRegex() + ")&amp;gt;?\\s+";&lt;br /&gt;  patternList.add(new MyPattern("Gmail",bodyGmail));&lt;br /&gt;  &lt;br /&gt;  String bodyAol = &lt;br /&gt;   "\\-{3,6} The following address(?:es|\\(es\\))? had (?:permanent fatal errors|delivery problems) \\-{3,6}\\s+" +&lt;br /&gt;   "&amp;lt;?(" + StringUtil.getEmailRegex() + ")&amp;gt;?(?:\\s|;)";&lt;br /&gt;  patternList.add(new MyPattern("AOL",bodyAol));&lt;br /&gt;  &lt;br /&gt;  String bodyYahoo = &lt;br /&gt;   "This .{1,10} permanent error.\\s+I(?:'ve| have) given up\\. Sorry it did(?:n't| not) work out\\.\\s+" +&lt;br /&gt;   "&amp;lt;?(" + StringUtil.getEmailRegex() + ")&amp;gt;?";&lt;br /&gt;  patternList.add(new MyPattern("Yahoo",bodyYahoo));&lt;br /&gt;  &lt;br /&gt;  String bodyPostfix = &lt;br /&gt;   "message\\s.*could\\s+not\\s+be\\s+.{0,10}delivered\\s+to\\s.*(?:recipient(?:s)?|destination(?:s)?)" +&lt;br /&gt;   ".{80,180}\\sinclude\\s+this\\s+problem\\s+report.{60,120}" +&lt;br /&gt;   "\\s+&amp;lt;(" + StringUtil.getEmailRegex() + ")&amp;gt;";&lt;br /&gt;  patternList.add(new MyPattern("Postfix",bodyPostfix));&lt;br /&gt;  &lt;br /&gt;  String bodyFailed = &lt;br /&gt;   "Failed\\s+to\\s+deliver\\s+to\\s+\\'(" + StringUtil.getEmailRegex() + ")\\'" +&lt;br /&gt;   ".{1,20}\\smodule.{5,100}\\sreports";&lt;br /&gt;  patternList.add(new MyPattern("Failed",bodyFailed));&lt;br /&gt;  &lt;br /&gt;  String bodyFirewall = &lt;br /&gt;   "Your\\s+message\\s+to:\\s+(" + StringUtil.getEmailRegex() + ")\\s+" +&lt;br /&gt;   ".{1,10}\\sblocked\\s+by\\s.{1,20}\\sSpam\\s+Firewall";&lt;br /&gt;  patternList.add(new MyPattern("SpamFirewall",bodyFirewall));&lt;br /&gt;  &lt;br /&gt;  String bodyFailure = &lt;br /&gt;   "message\\s.{8,20}\\scould\\s+not\\s+be\\s+delivered\\s.{10,40}\\srecipients" +&lt;br /&gt;   ".{6,20}\\spermanent\\s+error.{10,20}\\saddress(?:\\(es\\))?\\s+failed:" +&lt;br /&gt;   "\\s+(" + StringUtil.getEmailRegex() + ")\\s";&lt;br /&gt;  patternList.add(new MyPattern("Failure",bodyFailure));&lt;br /&gt;  &lt;br /&gt;  String bodyUnable = &lt;br /&gt;   "Unable to deliver message to the following address(?:\\(es\\))?.{0,5}" +&lt;br /&gt;   "\\s+&amp;lt;(" + StringUtil.getEmailRegex() + ")&amp;gt;";&lt;br /&gt;  patternList.add(new MyPattern("Unable",bodyUnable));&lt;br /&gt;  &lt;br /&gt;  String bodyEtrust = &lt;br /&gt;   "\\scould not deliver the e(?:\\-)?mail below because\\s.{10,20}\\srecipient(?:s)?\\s.{1,10}\\srejected"+&lt;br /&gt;   ".{60,200}\\s(" + StringUtil.getEmailRegex() + ")";&lt;br /&gt;  patternList.add(new MyPattern("eTrust",bodyEtrust));&lt;br /&gt;  &lt;br /&gt;  String bodyReport = &lt;br /&gt;   "\\scollection of report(?:s)? about email delivery\\s.+\\sFAILED:\\s.{1,1000}" +&lt;br /&gt;   "Final Recipient:.{0,20};\\s*(" + StringUtil.getEmailRegex() + ")";&lt;br /&gt;  patternList.add(new MyPattern("Report",bodyReport));&lt;br /&gt;  &lt;br /&gt;  String bodyNotReach = &lt;br /&gt;   "Your message.{1,400}did not reach the following recipient(?:\\(s\\))?:" +&lt;br /&gt;   "\\s+(" + StringUtil.getEmailRegex() + ")";&lt;br /&gt;  patternList.add(new MyPattern("NotReach",bodyNotReach));&lt;br /&gt;  &lt;br /&gt;  String bodyFailed2 = &lt;br /&gt;   "Could not deliver message to the following recipient(?:\\(s\\))?:" +&lt;br /&gt;   "\\s+Failed Recipient:\\s+(" + StringUtil.getEmailRegex() + ")\\s";&lt;br /&gt;  patternList.add(new MyPattern("Failed2",bodyFailed2));&lt;br /&gt;  &lt;br /&gt;  String bodyExceeds = &lt;br /&gt;   "User(?:'s)?\\s+mailbox\\s+exceeds\\s+allowed\\s+size:\\s+" +&lt;br /&gt;   "(" + StringUtil.getEmailRegex() + ")\\s+";&lt;br /&gt;  patternList.add(new MyPattern("Exceeds",bodyExceeds));&lt;br /&gt;  &lt;br /&gt;  String bodyDelayed = &lt;br /&gt;   "Message\\s+delivery\\s+to\\s+\\'(" + StringUtil.getEmailRegex() + ")\\'" +&lt;br /&gt;   "\\s+delayed.{1,20}\\smodule.{5,100}\\sreports";&lt;br /&gt;  patternList.add(new MyPattern("Delayed",bodyDelayed));&lt;br /&gt;  &lt;br /&gt;  String bodyInvalid = &lt;br /&gt;   "Invalid\\s+Address(?:es)?.{1,20}\\b(?:TO|addr)\\b.{1,20}\\s+&amp;lt;?(" + StringUtil.getEmailRegex() + ")&amp;gt;?\\s+";&lt;br /&gt;  patternList.add(new MyPattern("Invalid",bodyInvalid));&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-7924490885333471204?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/7924490885333471204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-3.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/7924490885333471204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/7924490885333471204'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-3.html' title='Detect bounced emails - Part 3'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-3316264410304045861</id><published>2009-09-04T09:23:00.000-04:00</published><updated>2009-09-04T09:26:39.297-04:00</updated><title type='text'>Detect bounced emails - Part 2</title><content type='html'>In Part 1 we provided a class to retrieve RFC related components from a mime message. Now we are providing a class that scans the components to find out the reason why the email was rejected.&lt;br /&gt;The reasons are grouped into bounce types. Following are the bounce types recognized by this scanner:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; HARD_BOUNCE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; SOFT_BOUNCE&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; MAILBOX_FULL&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; CC_USER&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; MDN_RECEIPT // read receipt&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/SmtpScanner.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.FileNotFoundException;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;import java.util.HashMap;&lt;br /&gt;import java.util.Stack;&lt;br /&gt;import java.util.StringTokenizer;&lt;br /&gt;import java.util.regex.Matcher;&lt;br /&gt;import java.util.regex.Pattern;&lt;br /&gt;import java.util.regex.PatternSyntaxException;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Scan input string for RFC1893/RFC2821 mail status code&lt;br /&gt; * &lt;br /&gt; * @author jackw&lt;br /&gt; */&lt;br /&gt;final class SmtpScanner {&lt;br /&gt; static final Logger logger = Logger.getLogger(SmtpScanner.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt; final int maxLenToScan = 8192*4; // scan up to 32k&lt;br /&gt; &lt;br /&gt; private static final HashMap&amp;lt;String, String&amp;gt; RFC1893_STATUS_CODE = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt; private static final HashMap&amp;lt;String, String&amp;gt; RFC1893_STATUS_DESC = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt; private static final HashMap&amp;lt;String, String&amp;gt; RFC2821_STATUS_CODE = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt; private static final HashMap&amp;lt;String, String&amp;gt; RFC2821_STATUS_DESC = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt; private static final HashMap&amp;lt;String, String&amp;gt; RFC2821_STATUS_MATCHINGTEXT = new HashMap&amp;lt;String, String&amp;gt;();&lt;br /&gt; &lt;br /&gt; public static enum BOUNCETYPE { GENERIC }; // default bounce type - not a bounce.&lt;br /&gt; public static enum BOUNCE_TYPES {&lt;br /&gt;  HARD_BOUNCE, // Hard bounce - suspend,notify,close&lt;br /&gt;  SOFT_BOUNCE, // Soft bounce - bounce++,close&lt;br /&gt;  MAILBOX_FULL, // mailbox full, can be treated as Soft Bounce&lt;br /&gt;  CC_USER, // Mail received as a Carbon Copy&lt;br /&gt;  MDN_RECEIPT, // MDN - read receipt&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static SmtpScanner smtpCodeScan = null;&lt;br /&gt;&lt;br /&gt; private static final String&lt;br /&gt;  LETTER_S = &amp;quot;s&amp;quot;,&lt;br /&gt;  LETTER_H = &amp;quot;h&amp;quot;,&lt;br /&gt;  LETTER_F = &amp;quot;f&amp;quot;,&lt;br /&gt;  LETTER_K = &amp;quot;k&amp;quot;,&lt;br /&gt;  LETTER_U = &amp;quot;u&amp;quot;;&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * default constructor&lt;br /&gt;  */&lt;br /&gt; private SmtpScanner() throws IOException {&lt;br /&gt;  loadRfc1893StatusCode();&lt;br /&gt;  loadRfc2821StatusCode();&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static SmtpScanner getInstance() throws IOException {&lt;br /&gt;  if (smtpCodeScan==null) {&lt;br /&gt;   smtpCodeScan = new SmtpScanner();&lt;br /&gt;  }&lt;br /&gt;  return smtpCodeScan;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * returns a message id, null if not found&lt;br /&gt;  * &lt;br /&gt;  * @param str -&lt;br /&gt;  *            message body&lt;br /&gt;  * @return message id, null if not found&lt;br /&gt;  */&lt;br /&gt; String scanBody(String body) {&lt;br /&gt;  String bounceType = scanBody(body, 1);&lt;br /&gt;  if (bounceType == null) {&lt;br /&gt;   bounceType = scanBody(body, 2);&lt;br /&gt;  }&lt;br /&gt;  return bounceType;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static Pattern pattern1 = Pattern.compile(&amp;quot;\\s([245]\\.\\d{1,3}\\.\\d{1,3})\\s&amp;quot;, Pattern.DOTALL);&lt;br /&gt; private static Pattern pattern2 = Pattern.compile(&amp;quot;\\s([245]\\d\\d)\\s&amp;quot;, Pattern.DOTALL);&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * &amp;lt;ul&amp;gt;&lt;br /&gt;  * &amp;lt;li&amp;gt; first pass: check if it contains a RFC1893 code. RFC1893 codes are&lt;br /&gt;  * from 5 to 9 bytes long (x.x.x -&amp;gt; x.xxx.xxx) and start with 2.x.x or 4.x.x&lt;br /&gt;  * or 5.x.x&lt;br /&gt;  * &amp;lt;li&amp;gt; second pass: check if it contains a 3 digit numeric number: 2xx, 4xx&lt;br /&gt;  * or 5xx.&lt;br /&gt;  * &amp;lt;/ul&amp;gt;&lt;br /&gt;  * &lt;br /&gt;  * @param str -&lt;br /&gt;  *            message body&lt;br /&gt;  * @param pass -&lt;br /&gt;  *            1) first pass: look for RFC1893 token (x.x.x).&lt;br /&gt;  *            2) second pass: look for RFC2821 token (xxx), must also match reply text.&lt;br /&gt;  * @return bounce type or null if no RFC code is found.&lt;br /&gt;  */&lt;br /&gt; private String scanBody(String body, int pass) {&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug(&amp;quot;Entering the examineBody method, pass &amp;quot; + pass);&lt;br /&gt;  if (StringUtil.isEmpty(body)) { // sanity check&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  BOUNCE_TYPES bounceType = null;&lt;br /&gt;  if (pass == 1) {&lt;br /&gt;   Matcher m = pattern1.matcher(StringUtil.cut(body, maxLenToScan));&lt;br /&gt;   if (m.find()) { // only one time&lt;br /&gt;    String token = m.group(m.groupCount());&lt;br /&gt;    logger.info(&amp;quot;examineBody(): RFC1893 token found: &amp;quot; + token);&lt;br /&gt;    if ((bounceType = searchRfc1893CodeTable(token)) != null) {&lt;br /&gt;     return bounceType.toString();&lt;br /&gt;    }&lt;br /&gt;    else if (token.startsWith(&amp;quot;5.&amp;quot;)) { // 5.x.x&lt;br /&gt;     return BOUNCE_TYPES.HARD_BOUNCE.toString();&lt;br /&gt;    }&lt;br /&gt;    else if (token.startsWith(&amp;quot;4.&amp;quot;)) { // 4.x.x&lt;br /&gt;     return BOUNCE_TYPES.SOFT_BOUNCE.toString();&lt;br /&gt;    }&lt;br /&gt;    else if (token.startsWith(&amp;quot;2.&amp;quot;)) { // 2.x.x&lt;br /&gt;     // 2.x.x = OK message returned, MDN receipt.&lt;br /&gt;     return BOUNCE_TYPES.MDN_RECEIPT.toString();&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (pass == 2) {&lt;br /&gt;   Matcher m = pattern2.matcher(StringUtil.cut(body, maxLenToScan));&lt;br /&gt;   int end = 0;&lt;br /&gt;   int count = 0;&lt;br /&gt;   while (m.find(end) &amp;&amp; count++ &amp;lt; 2) { // repeat two times&lt;br /&gt;    String token = m.group(m.groupCount());&lt;br /&gt;    end = m.end(m.groupCount());&lt;br /&gt;    logger.info(&amp;quot;examineBody(): Numeric token found: &amp;quot; + token);&lt;br /&gt;    if ((bounceType = searchRfc2821CodeTable(token)) != null) {&lt;br /&gt;     //return bounceType;&lt;br /&gt;     return matchRfcText(bounceType, token, body, end);&lt;br /&gt;    }&lt;br /&gt;    if (token.startsWith(&amp;quot;5&amp;quot;)) {&lt;br /&gt;     // 5xx = permanent failure, re-send will fail&lt;br /&gt;     String r = matchRfcText(BOUNCE_TYPES.HARD_BOUNCE, token, body, end);&lt;br /&gt;     if (r != null) return r;&lt;br /&gt;     // else look for the second token&lt;br /&gt;    }&lt;br /&gt;    else if(token.equals(&amp;quot;422&amp;quot;)) {&lt;br /&gt;     // 422 = mailbox full, re-send may be successful&lt;br /&gt;     return matchRfcText(BOUNCE_TYPES.MAILBOX_FULL, token, body, end);&lt;br /&gt;    }&lt;br /&gt;    else if (token.startsWith(&amp;quot;4&amp;quot;)) {&lt;br /&gt;     // 4xx = persistent transient failure, re-send may be successful&lt;br /&gt;     String r = matchRfcText(BOUNCE_TYPES.SOFT_BOUNCE, token, body, end);&lt;br /&gt;     if (r != null) return r;&lt;br /&gt;     // else look for the second token&lt;br /&gt;    }&lt;br /&gt;    else if(token.startsWith(&amp;quot;2&amp;quot;)) {&lt;br /&gt;     // 2xx = OK message returned.&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * For RFC 2821, to further match reply text to prevent false positives.&lt;br /&gt;  * &lt;br /&gt;  * @param bounceType -&lt;br /&gt;  *            Bounce Type&lt;br /&gt;  * @param code -&lt;br /&gt;  *            RFC2821 code&lt;br /&gt;  * @param tokens -&lt;br /&gt;  *            message text stored in an array, each element holds a word.&lt;br /&gt;  * @param idx -&lt;br /&gt;  *            where the RFC2821 code located in the array&lt;br /&gt;  * @return bounce type, or null if failed to match reply text.&lt;br /&gt;  */&lt;br /&gt; private String matchRfcText(BOUNCE_TYPES bounceType, String code, String body, int idx) {&lt;br /&gt;  String matchingText = RFC2821_STATUS_MATCHINGTEXT.get(code);&lt;br /&gt;  if (matchingText == null) {&lt;br /&gt;   if (code.startsWith(&amp;quot;4&amp;quot;)) {&lt;br /&gt;    matchingText = RFC2821_STATUS_MATCHINGTEXT.get(&amp;quot;4xx&amp;quot;);&lt;br /&gt;   }&lt;br /&gt;   else if (code.startsWith(&amp;quot;5&amp;quot;)) {&lt;br /&gt;    matchingText = RFC2821_STATUS_MATCHINGTEXT.get(&amp;quot;5xx&amp;quot;);&lt;br /&gt;   }&lt;br /&gt;   if (matchingText == null) { // just for safety&lt;br /&gt;    return null;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  // RFC reply text - the first 120 characters after the RFC code &lt;br /&gt;  String rfcText = StringUtil.cut(body.substring(idx), 120);&lt;br /&gt;  try {&lt;br /&gt;   Pattern p = Pattern.compile(matchingText, Pattern.DOTALL | Pattern.CASE_INSENSITIVE);&lt;br /&gt;   Matcher m = p.matcher(rfcText);&lt;br /&gt;   if (m.find()) {&lt;br /&gt;    logger.info(&amp;quot;Match Succeeded: [&amp;quot; + rfcText + &amp;quot;] matched [&amp;quot; + matchingText + &amp;quot;]&amp;quot;);&lt;br /&gt;    return bounceType.toString();&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.info(&amp;quot;Match Failed: [&amp;quot; + rfcText + &amp;quot;] did not match [&amp;quot; + matchingText + &amp;quot;]&amp;quot;);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (PatternSyntaxException e) {&lt;br /&gt;   logger.error(&amp;quot;PatternSyntaxException caught&amp;quot;, e);&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * search smtp code table by RFC1893 token.&lt;br /&gt;  * &lt;br /&gt;  * @param token&lt;br /&gt;  *            DSN status token, for example: 5.0.0&lt;br /&gt;  * @return message id related to the token&lt;br /&gt;  */&lt;br /&gt; private BOUNCE_TYPES searchRfc1893CodeTable(String token) {&lt;br /&gt;  // search rfc1893 hash table - x.x.x&lt;br /&gt;  BOUNCE_TYPES bounceType = searchRfcCodeTable(token, RFC1893_STATUS_CODE);&lt;br /&gt;  // search rfc1893 hash table - .x.x&lt;br /&gt;  if (bounceType == null) {&lt;br /&gt;   bounceType = searchRfcCodeTable(token.substring(1), RFC1893_STATUS_CODE);&lt;br /&gt;  }&lt;br /&gt;  return bounceType;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * search smtp code table by RFC token.&lt;br /&gt;  * &lt;br /&gt;  * @param token -&lt;br /&gt;  *            DSN status token, for example: 5.0.0, or 500 depending on the&lt;br /&gt;  *            map used&lt;br /&gt;  * @param map -&lt;br /&gt;  *            either RFC1893_STATUS_CODE or RFC2821_STATUS_CODE&lt;br /&gt;  * @return message id of the token&lt;br /&gt;  */&lt;br /&gt; private BOUNCE_TYPES searchRfcCodeTable(String token, HashMap&amp;lt;String, String&amp;gt; map) {&lt;br /&gt;  String type = map.get(token);&lt;br /&gt;&lt;br /&gt;  if (type != null) { // found RFC status code&lt;br /&gt;   logger.info(&amp;quot;searchRfcCodeTable(): A match is found for type: &amp;quot; + type);&lt;br /&gt;   if (type.equals(LETTER_H)) {&lt;br /&gt;    return BOUNCE_TYPES.HARD_BOUNCE;&lt;br /&gt;   }&lt;br /&gt;   else if (type.equals(LETTER_S)) {&lt;br /&gt;    return BOUNCE_TYPES.SOFT_BOUNCE;&lt;br /&gt;   }&lt;br /&gt;   else if (type.equals(LETTER_F)) {&lt;br /&gt;    return BOUNCE_TYPES.MAILBOX_FULL;&lt;br /&gt;   }&lt;br /&gt;   else if (type.equals(LETTER_K)) {&lt;br /&gt;    return BOUNCE_TYPES.MDN_RECEIPT;&lt;br /&gt;   }&lt;br /&gt;   else if (type.equals(LETTER_U)) {&lt;br /&gt;    if (token.startsWith(&amp;quot;4&amp;quot;)) {&lt;br /&gt;     return BOUNCE_TYPES.SOFT_BOUNCE;&lt;br /&gt;    }&lt;br /&gt;    else if (token.startsWith(&amp;quot;5&amp;quot;)) {&lt;br /&gt;     return BOUNCE_TYPES.HARD_BOUNCE;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * search smtp code table by RFC token.&lt;br /&gt;  * &lt;br /&gt;  * @param token -&lt;br /&gt;  *            RFC2821 token, for example: 500&lt;br /&gt;  * @return message id of the token&lt;br /&gt;  */&lt;br /&gt; private BOUNCE_TYPES searchRfc2821CodeTable(String token) {&lt;br /&gt;  return searchRfcCodeTable(token, RFC2821_STATUS_CODE);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * load the rfc1893 code table, from Rfc1893.properties file, into memory.&lt;br /&gt;  * &lt;br /&gt;  * @throws IOException&lt;br /&gt;  */&lt;br /&gt; private void loadRfc1893StatusCode() throws IOException {&lt;br /&gt;  ClassLoader loader = this.getClass().getClassLoader();&lt;br /&gt;  try {&lt;br /&gt;   // read in RFC1893 status code file and store it in two property objects&lt;br /&gt;   InputStream is = loader.getResourceAsStream(&amp;quot;Rfc1893.properties&amp;quot;);&lt;br /&gt;   BufferedReader fr = new BufferedReader(new InputStreamReader(is));&lt;br /&gt;   String inStr=null, code=null;&lt;br /&gt;   while ((inStr = fr.readLine()) != null) {&lt;br /&gt;    if (!inStr.startsWith(&amp;quot;#&amp;quot;)) {&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug(&amp;quot;loadRfc1893StatusCode(): &amp;quot; + inStr);&lt;br /&gt;     StringTokenizer st = new StringTokenizer(inStr, &amp;quot;^\r\n&amp;quot;);&lt;br /&gt;     if (st.countTokens() == 3) {&lt;br /&gt;      code = st.nextToken();&lt;br /&gt;      RFC1893_STATUS_CODE.put(code, st.nextToken());&lt;br /&gt;      RFC1893_STATUS_DESC.put(code, st.nextToken());&lt;br /&gt;     }&lt;br /&gt;     else if (st.countTokens() == 0) {&lt;br /&gt;      // ignore&lt;br /&gt;     }&lt;br /&gt;     else {&lt;br /&gt;      logger.fatal(&amp;quot;loadRfc1893StatusCode(): Wrong record format: &amp;quot; + inStr);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   fr.close();&lt;br /&gt;  }&lt;br /&gt;  catch (FileNotFoundException ex) {&lt;br /&gt;   logger.fatal(&amp;quot;file Rfc1893.properties does not exist&amp;quot;, ex);&lt;br /&gt;   throw ex;&lt;br /&gt;  }&lt;br /&gt;  catch (IOException ex) {&lt;br /&gt;   logger.fatal(&amp;quot;IOException caught during loading statcode.conf&amp;quot;, ex);&lt;br /&gt;   throw ex;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * load the rfc2821 code table, from Rfc2821.properties file, into memory.&lt;br /&gt;  * &lt;br /&gt;  * @throws IOException&lt;br /&gt;  */&lt;br /&gt; private void loadRfc2821StatusCode() throws IOException {&lt;br /&gt;  ClassLoader loader = this.getClass().getClassLoader();&lt;br /&gt;  try {&lt;br /&gt;   // read in RFC2821 status code file and store it in two property objects&lt;br /&gt;   InputStream is = loader.getResourceAsStream(&amp;quot;Rfc2821.properties&amp;quot;);&lt;br /&gt;   BufferedReader fr = new BufferedReader(new InputStreamReader(is));&lt;br /&gt;   String inStr=null, code=null;&lt;br /&gt;   while ((inStr = fr.readLine()) != null) {&lt;br /&gt;    if (!inStr.startsWith(&amp;quot;#&amp;quot;)) {&lt;br /&gt;     if (isDebugEnabled)&lt;br /&gt;      logger.debug(&amp;quot;loadRfc2821StatusCode(): &amp;quot; + inStr);&lt;br /&gt;     StringTokenizer st = new StringTokenizer(inStr, &amp;quot;^\r\n&amp;quot;);&lt;br /&gt;     if (st.countTokens() == 3) {&lt;br /&gt;      code = st.nextToken(); // 1st token = RFC code&lt;br /&gt;      RFC2821_STATUS_CODE.put(code, st.nextToken()); // 2nd token = type&lt;br /&gt;      String desc = st.nextToken(); // 3rd token = description&lt;br /&gt;      RFC2821_STATUS_DESC.put(code, desc);&lt;br /&gt;      // extract regular expression to be further matched&lt;br /&gt;      String matchingRegex = getMatchingRegex(desc);&lt;br /&gt;      if (matchingRegex != null) {&lt;br /&gt;       RFC2821_STATUS_MATCHINGTEXT.put(code, matchingRegex);&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;     else if (st.countTokens() == 0) {&lt;br /&gt;      // ignore&lt;br /&gt;     }&lt;br /&gt;     else {&lt;br /&gt;      logger.fatal(&amp;quot;loadRfc2821StatusCode(): Wrong record format: &amp;quot; + inStr);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   fr.close();&lt;br /&gt;  }&lt;br /&gt;  catch (FileNotFoundException ex) {&lt;br /&gt;   logger.fatal(&amp;quot;file Rfc2821.properties does not exist&amp;quot;, ex);&lt;br /&gt;   throw ex;&lt;br /&gt;  }&lt;br /&gt;  catch (IOException ex) {&lt;br /&gt;   logger.fatal(&amp;quot;IOException caught during loading statcode.conf&amp;quot;, ex);&lt;br /&gt;   throw ex;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private String getMatchingRegex(String desc) throws IOException {&lt;br /&gt;  int left = desc.indexOf(&amp;quot;{&amp;quot;);&lt;br /&gt;  if (left &amp;lt; 0) {&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  Stack&amp;lt;Integer&amp;gt; stack = new Stack&amp;lt;Integer&amp;gt;();&lt;br /&gt;  stack.push(Integer.valueOf(left));&lt;br /&gt;  int nextPos = left;&lt;br /&gt;  while (stack.size() &amp;gt; 0) {&lt;br /&gt;   int leftPos = desc.indexOf(&amp;quot;{&amp;quot;, nextPos + 1);&lt;br /&gt;   int rightPos = desc.indexOf(&amp;quot;}&amp;quot;, nextPos + 1);&lt;br /&gt;   if (leftPos &amp;gt; rightPos) {&lt;br /&gt;    if (rightPos &amp;gt; 0) {&lt;br /&gt;     stack.pop();&lt;br /&gt;     nextPos = rightPos;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else if (leftPos &amp;lt; rightPos) {&lt;br /&gt;    if (leftPos &amp;gt; 0) {&lt;br /&gt;     nextPos = leftPos;&lt;br /&gt;     stack.push(Integer.valueOf(leftPos));&lt;br /&gt;    }&lt;br /&gt;    else if (rightPos &amp;gt; 0) {&lt;br /&gt;     stack.pop();&lt;br /&gt;     nextPos = rightPos;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (nextPos &amp;gt; left) {&lt;br /&gt;   if (stack.size() == 0) {&lt;br /&gt;    return desc.substring(left + 1, nextPos);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.error(&amp;quot;getMatchingRegex() - missing close curly brace: &amp;quot; + desc);&lt;br /&gt;    throw new IOException(&amp;quot;Missing close curly brace: &amp;quot; + desc);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  try {&lt;br /&gt;   SmtpScanner scan = SmtpScanner.getInstance();&lt;br /&gt;   String bounceType = scan.scanBody(&amp;quot;aaaaab\n5.0.0\nefg &amp;quot;);&lt;br /&gt;   System.out.println(&amp;quot;BounceType: &amp;quot; + bounceType);&lt;br /&gt;   bounceType = scan.scanBody(&amp;quot;aaa 201 aab\n422\naccount is full &amp;quot;);&lt;br /&gt;   System.out.println(&amp;quot;BounceType: &amp;quot; + bounceType);&lt;br /&gt;   bounceType = scan.scanBody(&amp;quot;aaaaab\n400\ntemporary failure &amp;quot;);&lt;br /&gt;   System.out.println(&amp;quot;BounceType: &amp;quot; + bounceType);&lt;br /&gt;   System.out.println(scan.getMatchingRegex(&amp;quot;{(?:mailbox|account).{0,180}(?:storage|full|limit|quota)}&amp;quot;));&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This class needs two additional property files to function, save them under the root folder of your classpath:&lt;br /&gt;&lt;br /&gt;1) Rfc1893.properties:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# RFC1893/RFC3463 status code and description&lt;br /&gt;# status code = class "." subject "." detail&lt;br /&gt;# 2.x.x Success&lt;br /&gt;# 4.x.x Persistent Transient Failure&lt;br /&gt;# 5.x.x Permanent Failure&lt;br /&gt;#&lt;br /&gt;# format: StatusCode^Type^Description&lt;br /&gt;# type = &lt;s=soft,h=hard,k=okay,f=mailbox block,u="unknown" full,l="size" large,b="mail" too=""&gt;&lt;br /&gt;#&lt;br /&gt;# permanent failure&lt;br /&gt;5.0.0^h^Other undefined status&lt;br /&gt;5.1.0^h^Other address status&lt;br /&gt;5.1.1^h^Bad destination mailbox address&lt;br /&gt;5.1.2^h^Bad destination system address&lt;br /&gt;5.1.3^h^Bad destination mailbox address syntax&lt;br /&gt;5.1.4^h^Destination mailbox address ambiguous&lt;br /&gt;5.1.5^h^Destination mailbox address invalid (source: Microsoft)&lt;br /&gt;5.1.6^h^Mailbox has moved&lt;br /&gt;5.1.7^h^Bad sender's mailbox address syntax&lt;br /&gt;5.1.8^h^Bad sender's system address&lt;br /&gt;5.2.0^h^Other or undefined mailbox status&lt;br /&gt;5.2.1^h^Mailbox disabled, not accepting messages&lt;br /&gt;5.2.2^f^Mailbox full&lt;br /&gt;5.2.3^l^Message length exceeds administrative limit.&lt;br /&gt;5.2.4^h^Mailing list expansion problem&lt;br /&gt;5.3.0^h^Other or undefined mail system status&lt;br /&gt;5.3.1^s^Mail system full&lt;br /&gt;5.3.2^h^System not accepting network messages&lt;br /&gt;5.3.3^h^System not capable of selected features&lt;br /&gt;5.3.4^l^Message too big for system&lt;br /&gt;5.3.5^h^System incorrectly configured&lt;br /&gt;5.4.0^h^Other or undefined network or routing status&lt;br /&gt;5.4.1^h^No answer from host&lt;br /&gt;5.4.2^h^Bad connection&lt;br /&gt;5.4.3^h^Routing server failure&lt;br /&gt;5.4.4^h^Unable to route&lt;br /&gt;5.4.5^h^Network congestion&lt;br /&gt;5.4.6^h^Routing loop detected&lt;br /&gt;5.4.7^h^Delivery time expired&lt;br /&gt;5.4.8^h^Loop detected, check recipient policy. (Source: Microsoft)&lt;br /&gt;5.5.0^h^Other or undefined protocol status&lt;br /&gt;5.5.1^h^Invalid command&lt;br /&gt;5.5.2^h^Syntax error&lt;br /&gt;5.5.3^h^Too many recipients&lt;br /&gt;5.5.4^h^Invalid command arguments&lt;br /&gt;5.5.5^h^Wrong protocol version&lt;br /&gt;5.6.0^b^Other or undefined media error&lt;br /&gt;5.6.1^b^Media not supported&lt;br /&gt;5.6.2^h^Conversion required and prohibited&lt;br /&gt;5.6.3^h^Conversion required but not supported&lt;br /&gt;5.6.4^h^Conversion with loss performed&lt;br /&gt;5.6.5^h^Conversion failed&lt;br /&gt;5.7.0^h^Other or undefined security status&lt;br /&gt;5.7.1^b^Delivery not authorized, message refused&lt;br /&gt;5.7.2^h^Mailing list expansion prohibited&lt;br /&gt;5.7.3^h^Security conversion required but not possible&lt;br /&gt;5.7.4^h^Security features not supported&lt;br /&gt;5.7.5^b^Cryptographic failure&lt;br /&gt;5.7.6^b^Cryptographic algorithm not supported&lt;br /&gt;5.7.7^b^Message integrity failure&lt;br /&gt;# persistent transient failure&lt;br /&gt;4.0.0^s^Other undefined status&lt;br /&gt;4.1.0^s^Other address status&lt;br /&gt;4.1.4^s^Destination mailbox address ambiguous&lt;br /&gt;4.1.5^s^Destination mailbox address valid&lt;br /&gt;4.1.7^s^Bad sender's mailbox address syntax&lt;br /&gt;4.1.8^s^Bad sender's system address&lt;br /&gt;4.2.0^s^Other or undefined mailbox status&lt;br /&gt;4.2.1^s^Mailbox disabled, not accepting messages&lt;br /&gt;4.2.2^f^Mailbox full&lt;br /&gt;4.2.4^s^Mailing list expansion problem&lt;br /&gt;4.3.0^s^Other or undefined mail system status&lt;br /&gt;4.3.1^s^Mail system full&lt;br /&gt;4.3.2^s^System not accepting network messages&lt;br /&gt;4.3.3^s^System not capable of selected features&lt;br /&gt;4.3.5^s^System incorrectly configured&lt;br /&gt;4.4.0^s^Other or undefined network or routing status&lt;br /&gt;4.4.1^s^No answer from host&lt;br /&gt;4.4.2^s^Bad connection&lt;br /&gt;4.4.3^s^Routing server failure&lt;br /&gt;4.4.4^s^Unable to route&lt;br /&gt;4.4.5^s^Network congestion&lt;br /&gt;4.4.6^s^Routing loop detected&lt;br /&gt;4.4.7^s^Delivery time expired&lt;br /&gt;4.5.0^s^Other or undefined protocol status&lt;br /&gt;4.5.3^s^Too many recipients&lt;br /&gt;4.5.5^s^Wrong protocol version&lt;br /&gt;4.6.0^s^Other or undefined media error&lt;br /&gt;4.6.2^s^Conversion required and prohibited&lt;br /&gt;4.6.3^s^Conversion required but not supported&lt;br /&gt;4.6.4^s^Conversion with loss performed&lt;br /&gt;4.6.5^s^Conversion failed&lt;br /&gt;4.7.0^s^Other or undefined security status&lt;br /&gt;4.7.5^s^Cryptographic failure&lt;br /&gt;4.7.6^s^Cryptographic algorithm not supported&lt;br /&gt;4.7.7^s^Message integrity failure&lt;br /&gt;# generic entries&lt;br /&gt;.0.0^s^Other undefined status&lt;br /&gt;.1.0^s^Other address status&lt;br /&gt;.1.1^h^Bad destination mailbox address&lt;br /&gt;.1.2^h^Bad destination system address&lt;br /&gt;.1.3^h^Bad destination mailbox address syntax&lt;br /&gt;.1.4^h^Destination mailbox address ambiguous&lt;br /&gt;.1.5^k^Destination mailbox address valid&lt;br /&gt;.1.6^h^Mailbox has moved&lt;br /&gt;.1.7^s^Bad sender's mailbox address syntax&lt;br /&gt;.1.8^s^Bad sender's system address&lt;br /&gt;.2.0^s^Other or undefined mailbox status&lt;br /&gt;.2.1^h^Mailbox disabled, not accepting messages&lt;br /&gt;.2.2^f^Mailbox full&lt;br /&gt;.2.3^l^Message length exceeds administrative limit.&lt;br /&gt;.2.4^s^Mailing list expansion problem&lt;br /&gt;.3.0^s^Other or undefined mail system status&lt;br /&gt;.3.1^s^Mail system full&lt;br /&gt;.3.2^s^System not accepting network messages&lt;br /&gt;.3.3^s^System not capable of selected features&lt;br /&gt;.3.4^l^Message too big for system&lt;br /&gt;.3.5^s^System incorrectly configured&lt;br /&gt;.4.0^s^Other or undefined network or routing status&lt;br /&gt;.4.1^s^No answer from host&lt;br /&gt;.4.2^s^Bad connection&lt;br /&gt;.4.3^s^Routing server failure&lt;br /&gt;.4.4^s^Unable to route&lt;br /&gt;.4.5^s^Network congestion&lt;br /&gt;.4.6^s^Routing loop detected&lt;br /&gt;.4.7^s^Delivery time expired&lt;br /&gt;.5.0^s^Other or undefined protocol status&lt;br /&gt;.5.1^h^Invalid command&lt;br /&gt;.5.2^h^Syntax error&lt;br /&gt;.5.3^s^Too many recipients&lt;br /&gt;.5.4^h^Invalid command arguments&lt;br /&gt;.5.5^s^Wrong protocol version&lt;br /&gt;.6.0^s^Other or undefined media error&lt;br /&gt;.6.1^s^Media not supported&lt;br /&gt;.6.2^s^Conversion required and prohibited&lt;br /&gt;.6.3^s^Conversion required but not supported&lt;br /&gt;.6.4^s^Conversion with loss performed&lt;br /&gt;.6.5^s^Conversion failed&lt;br /&gt;.7.0^s^Other or undefined security status&lt;br /&gt;.7.1^b^Delivery not authorized, message refused&lt;br /&gt;.7.2^h^Mailing list expansion prohibited&lt;br /&gt;.7.3^h^Security conversion required but not possible&lt;br /&gt;.7.4^h^Security features not supported&lt;br /&gt;.7.5^s^Cryptographic failure&lt;br /&gt;.7.6^s^Cryptographic algorithm not supported&lt;br /&gt;.7.7^s^Message integrity failure&lt;br /&gt;&lt;/s=soft,h=hard,k=okay,f=mailbox&gt;&lt;/pre&gt;&lt;br /&gt;2) Rfc2821.properties:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;# RFC2821 reply code and description&lt;br /&gt;# reply code = xyz&lt;br /&gt;# 1yz Positive Preliminary reply&lt;br /&gt;# 2yz   Positive Completion reply&lt;br /&gt;# 3yz   Positive Intermediate reply&lt;br /&gt;# 4yz   Transient Negative Completion reply&lt;br /&gt;# 5yz   Permanent Negative Completion reply&lt;br /&gt;#&lt;br /&gt;# format: ReplyCode^Type^Description&lt;br /&gt;# type = &lt;s=soft,h=hard,k=okay,f=mailbox full,u="unknown"&gt;&lt;br /&gt;# Description: text enclosed in curly brackets should be further matched to prevent false positives.&lt;br /&gt;#&lt;br /&gt;211^k^System status, or system help reply&lt;br /&gt;214^k^Help message&lt;br /&gt;220^k^&lt;domain&gt; Service ready&lt;br /&gt;221^k^&lt;domain&gt; Service closing transmission channel&lt;br /&gt;250^k^Requested mail action okay, completed&lt;br /&gt;251^k^User not local; will forward to &lt;forward-path&gt;&lt;br /&gt;252^k^Cannot VRFY user, but will accept message and attempt delivery&lt;br /&gt;354^k^Start mail input; end with &lt;crlf&gt;.&lt;crlf&gt;&lt;br /&gt;421^s^&lt;domain&gt; Service not available, closing transmission channel {\bnot\s+available}&lt;br /&gt;450^s^Requested mail action not taken: mailbox unavailable {\baction\s+not\s+taken}&lt;br /&gt;451^s^Requested action aborted: local error in processing {\baction\s+aborted}&lt;br /&gt;452^s^Requested action not taken: insufficient system storage {\baction\s+not\s+taken}&lt;br /&gt;500^h^Syntax error, command unrecognized {\berror}&lt;br /&gt;501^h^Syntax error in parameters or arguments {\berror}&lt;br /&gt;502^h^Command not implemented {\bnot\s+implemented}&lt;br /&gt;503^h^Bad sequence of commands {\bBad\s+sequence}&lt;br /&gt;504^h^Command parameter not implemented {\bnot\s+implemented}&lt;br /&gt;550^h^Requested action not taken: mailbox unavailable {\baction\s+not\s+taken}&lt;br /&gt;551^h^User not local; please try &lt;forward-path&gt; {\bnot\s+local}&lt;br /&gt;552^f^Requested mail action aborted: exceeded storage allocation {\baction\s+aborted}&lt;br /&gt;553^h^Requested action not taken: mailbox name not allowed {\baction\s+not\s+taken}&lt;br /&gt;554^h^Transaction failed {\b(?:failed|delivery error)}&lt;br /&gt;#&lt;br /&gt;# *** Custom entries, not defined by RFC 2821 ***&lt;br /&gt;#&lt;br /&gt;422^f^{\b(?:mailbox|account)\b.{0,100}(?:storage|full|limit|quota)} mailbox full.&lt;br /&gt;4xx^s^{\btemporary\s.{0,100}(?:failure|error)}, used to match undefined codes starting with 4&lt;br /&gt;5xx^h^{\bpermanent\s.{0,100}(?:failure|error)}, used to match undefined codes starting with 5&lt;br /&gt;&lt;/forward-path&gt;&lt;/domain&gt;&lt;/crlf&gt;&lt;/crlf&gt;&lt;/forward-path&gt;&lt;/domain&gt;&lt;/domain&gt;&lt;/s=soft,h=hard,k=okay,f=mailbox&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-3316264410304045861?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/3316264410304045861/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3316264410304045861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3316264410304045861'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-2.html' title='Detect bounced emails - Part 2'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-6903639820053365956</id><published>2009-09-03T14:01:00.001-04:00</published><updated>2009-09-03T16:55:16.589-04:00</updated><title type='text'>Detect bounced emails - Part 1</title><content type='html'>When an email is rejected by a mail server due to a violation such as user or mailbox not found, certain information are returned and included in either message headers or message body, or both.&lt;br /&gt;There are many RFC's that address the rules and codes that should be included in the rejected messages.&lt;br /&gt;For simplicity, we will only deal with RFC822, RFC1893, and RFC3464 here, which I believe are implemented in most if not all commercial and open source SMTP servers.&lt;br /&gt;Please refer &lt;a href="http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-3.html"&gt;Portable java mail message bean&lt;/a&gt; as it is used by this bounce detecting program.&lt;br /&gt;First we need a class to retrieve RFC822 Headers  and Delivery Status Report from message bean, this class provides methods that travel through body part tree and collect RFC components and attachments. The RFC components are then used by a scanner (provided in Part 2) to find rejection reasons.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/BodypartUtil.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;br /&gt; * &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;import javax.mail.Part;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * provide utility methods to retrieve attachments from BodypartBean&lt;br /&gt; */&lt;br /&gt;public final class BodypartUtil implements Serializable {&lt;br /&gt; private static final long serialVersionUID = -8920127339846912514L;&lt;br /&gt; static final Logger logger = Logger.getLogger(BodypartUtil.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; final static String LF = System.getProperty("line.separator", "\n");&lt;br /&gt; &lt;br /&gt; private BodypartUtil() {&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * Retrieve all attachments into a list. It also looks for delivery status&lt;br /&gt;  * report and rfc822 report. If it found a report, it saves the link to the&lt;br /&gt;  * report to a MessageBean field so it can be easily retrieved later. Please&lt;br /&gt;  * call this method before you call any other methods that will retrieve any&lt;br /&gt;  * delivery reports.&lt;br /&gt;  * &lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *            retrieving from&lt;br /&gt;  * @return a list of MessageNode's&lt;br /&gt;  */&lt;br /&gt; public static List&amp;lt;MessageNode&amp;gt; retrieveAttachments(MessageBean msgBean) {&lt;br /&gt;  msgBean.setAttachments(retrieveAttachments((BodypartBean) msgBean, msgBean, 1));&lt;br /&gt;  return msgBean.getAttachments();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static List&amp;lt;MessageNode&amp;gt; retrieveAttachments(BodypartBean aNode, MessageBean msgBean,&lt;br /&gt;   int level) {&lt;br /&gt;  if (level &amp;lt;= 1) {&lt;br /&gt;   msgBean.setRfc822(null);&lt;br /&gt;   msgBean.setReport(null);&lt;br /&gt;  }&lt;br /&gt;  List&amp;lt;MessageNode&amp;gt; aNodes = new ArrayList&amp;lt;MessageNode&amp;gt;();&lt;br /&gt;  Iterator&amp;lt;BodypartBean&amp;gt; it = aNode.getIterator();&lt;br /&gt;  while (it.hasNext()) {&lt;br /&gt;   BodypartBean subNode = it.next();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   String desc = subNode.getDescription();&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String nullInd = subNode.getValue() == null ? "null" : "not null";&lt;br /&gt;   logger.info("retrieveAttachments(): level=" + level + ", mtype=" + mtype + ", "&lt;br /&gt;     + ", disp=" + disp + ", desc=" + desc + ", " + nullInd);&lt;br /&gt;   if (Part.ATTACHMENT.equalsIgnoreCase(disp)&lt;br /&gt;     || (Part.INLINE.equalsIgnoreCase(disp) &amp;amp;&amp;amp; desc != null)&lt;br /&gt;     || MessageBeanUtil.getFileName(subNode.getContentType()) != null) {&lt;br /&gt;    &lt;br /&gt;    // this Node is an attachment&lt;br /&gt;    aNodes.add(new MessageNode(subNode, level));&lt;br /&gt;   }&lt;br /&gt;   // find other attachments down from the Node&lt;br /&gt;   List&amp;lt;MessageNode&amp;gt; subAttch = retrieveAttachments(subNode, msgBean, level + 1);&lt;br /&gt;   if (subAttch!=null) {&lt;br /&gt;    aNodes.addAll(subAttch);&lt;br /&gt;   }&lt;br /&gt;   // save the node that contains status report&lt;br /&gt;   if (mtype.startsWith("message/rfc822") &amp;amp;&amp;amp; msgBean.getRfc822() == null)&lt;br /&gt;    msgBean.setRfc822(new MessageNode(subNode, level));&lt;br /&gt;   if (mtype.startsWith("multipart/report") &amp;amp;&amp;amp; msgBean.getReport() == null)&lt;br /&gt;    msgBean.setReport(new MessageNode(subNode, level));&lt;br /&gt;  }&lt;br /&gt;  // root node could also be multipart/report content type&lt;br /&gt;  String mtype = aNode.getMimeType();&lt;br /&gt;  if (mtype.startsWith("multipart/report") &amp;amp;&amp;amp; msgBean.getReport() == null) {&lt;br /&gt;   String disp = aNode.getDisposition();&lt;br /&gt;   String desc = aNode.getDescription();&lt;br /&gt;   logger.info("retrieveAttachments(): level=" + (level - 1) + ", mtype=" + mtype&lt;br /&gt;     + ", disp=" + disp + ", desc=" + desc);&lt;br /&gt;   msgBean.setReport(new MessageNode(aNode, level));&lt;br /&gt;  }&lt;br /&gt;  msgBean.setAttachments(aNodes);&lt;br /&gt;  return msgBean.getAttachments();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public static List&amp;lt;BodypartBean&amp;gt; retrieveAlternatives(MessageBean msgBean) {&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; aNodes = null;&lt;br /&gt;  if (msgBean.getContentType().startsWith("multipart/alternative")) {&lt;br /&gt;   aNodes = msgBean.getNodes();&lt;br /&gt;  }&lt;br /&gt;  else if (msgBean.getContentType().startsWith("multipart/mixed")) {&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = msgBean.getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean subNode = it.next();&lt;br /&gt;    if (subNode.getContentType().startsWith("multipart/alternative")) {&lt;br /&gt;     aNodes = subNode.getNodes();&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (aNodes == null) { // just for safety&lt;br /&gt;   aNodes = new ArrayList&amp;lt;BodypartBean&amp;gt;();&lt;br /&gt;  }&lt;br /&gt;  if (aNodes.size() == 0) { // no alternatives, use body&lt;br /&gt;   aNodes.add(msgBean);&lt;br /&gt;  }&lt;br /&gt;  return aNodes;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * retrieve RFC822 component into a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public static BodypartBean retrieveRfc822Text(BodypartBean aNode, int level) {&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  if (sNode != null &amp;amp;&amp;amp; sNode.size() &amp;gt; 0) {&lt;br /&gt;   // message/rfc822 attaches a text node as its child body part.&lt;br /&gt;   BodypartBean subNode = sNode.get(0); // only the first node&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveRFC822Text() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("text")) {&lt;br /&gt;    logger.info("retrieveRFC822Text() - found the child bodypart from level "&lt;br /&gt;      + level);&lt;br /&gt;    return subNode;&lt;br /&gt;   }&lt;br /&gt;   else { // go deeper to get the text node&lt;br /&gt;    return retrieveRfc822Text(subNode, level + 1);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else { // message/rfc822 contains no sub nodes&lt;br /&gt;   logger.info("retrieveRFC822Text() - missing the lower level node, check if it's a text/rfc822-headers.");&lt;br /&gt;   String mtype = aNode.getMimeType();&lt;br /&gt;   String disp = aNode.getDisposition();&lt;br /&gt;   logger.info("retrieveRFC822Text() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("text/rfc822-headers")) {&lt;br /&gt;    logger.info("retrieveRFC822Text() - found the text/rfc822-headers from level "&lt;br /&gt;        + level);&lt;br /&gt;    return aNode;&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    logger.info("retrieveRFC822Text() - missing the lower level node and rfc822-headers, use it anyway.");&lt;br /&gt;    return aNode;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve rfc1894/rfc1891 component in a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public static BodypartBean retrieveDlvrStatus(BodypartBean aNode, int level) {&lt;br /&gt;  // multipart/report could attach a status node as its child body part.&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  for (int i = 0; sNode != null &amp;amp;&amp;amp; i &amp;lt; sNode.size(); i++) {&lt;br /&gt;   BodypartBean subNode = sNode.get(i);&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveDlvrStatus() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("message/delivery-status")) {&lt;br /&gt;    logger.info("retrieveDlvrStatus() - found message/delivery-status bodypart from level "&lt;br /&gt;        + level);&lt;br /&gt;    return subNode; // return delivery-status if one is found&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  logger.info("retrieveDlvrStatus() - missing the lower level node or the lower level node has no text.");&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve rfc3798 component in a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public static BodypartBean retrieveMDNReceipt(BodypartBean aNode, int level) {&lt;br /&gt;  // multipart/report could attach a MDN node as its child body part.&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  for (int i = 0; sNode != null &amp;amp;&amp;amp; i &amp;lt; sNode.size(); i++) {&lt;br /&gt;   BodypartBean subNode = sNode.get(i);&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveMDNReceipt() - proceeded to level " + level + ", mtype=" + mtype&lt;br /&gt;     + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("message/disposition-notification")) {&lt;br /&gt;    logger.info("retrieveMDNReceipt() - found message/disposition-notification "&lt;br /&gt;      + "bodypart from level " + level);&lt;br /&gt;    return subNode; // return disposition-notification if one is&lt;br /&gt;        // found&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve rfc1894/rfc1891 components in a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean list&lt;br /&gt;  */&lt;br /&gt; public static List&amp;lt;BodypartBean&amp;gt; retrieveReportText(BodypartBean aNode, int level) {&lt;br /&gt;  // multipart/report could attach text nodes as its sub body parts.&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; list = new ArrayList&amp;lt;BodypartBean&amp;gt;();&lt;br /&gt;  for (int i = 0; sNode != null &amp;amp;&amp;amp; i &amp;lt; sNode.size(); i++) {&lt;br /&gt;   BodypartBean subNode = sNode.get(i);&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveReportText() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("text") &amp;amp;&amp;amp; !mtype.startsWith("text/rfc822-headers")) {&lt;br /&gt;    logger.info("retrieveReportText() - found " + mtype + " bodypart from level "&lt;br /&gt;      + level);&lt;br /&gt;    list.add(subNode);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return list;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve rfc822 message in a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public static BodypartBean retrieveMessageRfc822(BodypartBean aNode, int level) {&lt;br /&gt;  // locate message/rfc822 section under multipart/report&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  for (int i = 0; sNode != null &amp;amp;&amp;amp; i &amp;lt; sNode.size(); i++) {&lt;br /&gt;   BodypartBean subNode = sNode.get(i);&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveMessageRFC822() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("message/rfc822")) {&lt;br /&gt;    logger.info("retrieveMessageRFC822() - found message/rfc822 section from level "&lt;br /&gt;      + level);&lt;br /&gt;    return subNode;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * retrieve RFC822 headers component in a BodypartBean format&lt;br /&gt;  * @param aNode - root Node&lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public static BodypartBean retrieveRfc822Headers(BodypartBean aNode, int level) {&lt;br /&gt;  // locate text/rfc822-headers section under multipart/report&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; sNode = aNode.getNodes();&lt;br /&gt;  for (int i = 0; sNode != null &amp;amp;&amp;amp; i &amp;lt; sNode.size(); i++) {&lt;br /&gt;   BodypartBean subNode = sNode.get(i);&lt;br /&gt;   String mtype = subNode.getMimeType();&lt;br /&gt;   String disp = subNode.getDisposition();&lt;br /&gt;   logger.info("retrieveRFC822Headers() - proceeded to level " + level + ", mtype="&lt;br /&gt;     + mtype + ", disp=" + disp);&lt;br /&gt;   if (mtype.startsWith("text/rfc822-headers")) {&lt;br /&gt;    logger.info("retrieveRFC822Headers() - found message/rfc822 section from level "&lt;br /&gt;      + level);&lt;br /&gt;    return subNode;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-6903639820053365956?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/6903639820053365956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/6903639820053365956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/6903639820053365956'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/detect-bounced-emails-part-1.html' title='Detect bounced emails - Part 1'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-8776628233440790181</id><published>2009-09-03T13:17:00.000-04:00</published><updated>2009-09-03T14:27:50.799-04:00</updated><title type='text'>Portable java mail message bean - Part 5</title><content type='html'>This is the last part of the series, in which it provides a string manipulation utility class that is needed by the conversion utility class presented in Part 4.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/StringUtil.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;/pre&gt;&lt;pre&gt;* &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.lang.reflect.Method;&lt;br /&gt;import java.lang.reflect.Modifier;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.StringTokenizer;&lt;br /&gt;import java.util.regex.Matcher;&lt;br /&gt;import java.util.regex.Pattern;&lt;br /&gt;&lt;br /&gt;import javax.mail.Address;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;public final class StringUtil {&lt;br /&gt; static final Logger logger = Logger.getLogger(StringUtil.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; public final static String TOKEN_XHDR_BEGIN = "10.";&lt;br /&gt; public final static String TOKEN_XHDR_END = ".0";&lt;br /&gt;&lt;br /&gt;    private StringUtil() {&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt; public static boolean isEmpty(String str) {&lt;br /&gt;  return (str == null || str.trim().length() == 0);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * trim the input string from the right to the specified length.&lt;br /&gt;  * &lt;br /&gt;  * @param str -&lt;br /&gt;  *            original string&lt;br /&gt;  * @param len -&lt;br /&gt;  *            string size&lt;br /&gt;  * @return string trimmed to the size of "len"&lt;br /&gt;  */&lt;br /&gt; public static String cut(String str, int len) {&lt;br /&gt;  if (str == null || str.length() &amp;lt;= len || len &amp;lt; 0)&lt;br /&gt;   return str;&lt;br /&gt;  else&lt;br /&gt;   return str.substring(0, len);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * trim the input string from the right to the specified length.&lt;br /&gt;  * &lt;br /&gt;  * @param str -&lt;br /&gt;  *            original string&lt;br /&gt;  * @param len -&lt;br /&gt;  *            string size&lt;br /&gt;  * @return trimmed string plus three dots if its size is over specified length.&lt;br /&gt;  */&lt;br /&gt; public static String cutWithDots(String str, int len) {&lt;br /&gt;  if (str == null || str.length() &amp;lt;= len || len &amp;lt; 0)&lt;br /&gt;   return str;&lt;br /&gt;  else if (str.length() &amp;gt; len)&lt;br /&gt;   return str.substring(0, len) + "...";&lt;br /&gt;  else&lt;br /&gt;   return str;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * remove double and single quotes from input string.&lt;br /&gt;  * &lt;br /&gt;  * @param data -&lt;br /&gt;  *            input string&lt;br /&gt;  * @return string with quotes removed, or null if input is null&lt;br /&gt;  */&lt;br /&gt; public static String removeQuotes(String data) {&lt;br /&gt;  if (data == null) return data;&lt;br /&gt;  StringTokenizer st = new StringTokenizer(data, "\"\'");&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  while (st.hasMoreTokens())&lt;br /&gt;   sb.append(st.nextToken());&lt;br /&gt;&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * convert Address array to a string, addresses are comma delimited. Display&lt;br /&gt;  * names are removed from returned addresses.&lt;br /&gt;  * &lt;br /&gt;  * @param addrs -&lt;br /&gt;  *            Address array&lt;br /&gt;  * @return a string of addresses, comma delimited. or null if input is&lt;br /&gt;  *         null&lt;br /&gt;  */&lt;br /&gt; public static String addrToString(Address[] addrs) {&lt;br /&gt;  return addrToString(addrs, true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * convert Address array to string, addresses are comma delimited.&lt;br /&gt;  * &lt;br /&gt;  * @param addrs -&lt;br /&gt;  *            Address array&lt;br /&gt;  * @param removeDisplayName -&lt;br /&gt;  *            remove display name from addresses if true&lt;br /&gt;  * @return a string of addresses, comma delimited. or null if input is&lt;br /&gt;  *         null&lt;br /&gt;  */&lt;br /&gt; public static String addrToString(Address[] addrs, boolean removeDisplayName) {&lt;br /&gt;  if (addrs == null || addrs.length == 0) {&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  String str = addrs[0].toString();&lt;br /&gt;  if (removeDisplayName) {&lt;br /&gt;   str = removeDisplayName(str);&lt;br /&gt;  }&lt;br /&gt;  for (int i = 1; i &amp;lt; addrs.length; i++) {&lt;br /&gt;   if (removeDisplayName) {&lt;br /&gt;    str = str + "," + removeDisplayName(addrs[i].toString());&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    str = str + "," + addrs[i].toString();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return str;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * remove display name from an email address, and convert all characters &lt;br /&gt;  * to lower case.&lt;br /&gt;  * &lt;br /&gt;  * @param addr -&lt;br /&gt;  *            email address&lt;br /&gt;  * @return email address without display name, or null if input is null.&lt;br /&gt;  */&lt;br /&gt; public static String removeDisplayName(String addr) {&lt;br /&gt;  return removeDisplayName(addr, true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * remove display name from an email address.&lt;br /&gt;  * &lt;br /&gt;  * @param addr -&lt;br /&gt;  *            email address&lt;br /&gt;  * @param toLowerCase -&lt;br /&gt;  *            true to convert characters to lower case&lt;br /&gt;  * @return email address without display name, or null if input is null.&lt;br /&gt;  */&lt;br /&gt; public static String removeDisplayName(String addr, boolean toLowerCase) {&lt;br /&gt;  if (isEmpty(addr)) {&lt;br /&gt;   return addr;&lt;br /&gt;  }&lt;br /&gt;  int at_pos = addr.lastIndexOf("@");&lt;br /&gt;  if (at_pos &amp;gt; 0) {&lt;br /&gt;   int pos1 = addr.lastIndexOf("&amp;lt;", at_pos);&lt;br /&gt;   int pos2 = addr.indexOf("&amp;gt;", at_pos + 1);&lt;br /&gt;   if (pos1 &amp;gt;= 0 &amp;amp;&amp;amp; pos2 &amp;gt; pos1) {&lt;br /&gt;    addr = addr.substring(pos1 + 1, pos2);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (toLowerCase)&lt;br /&gt;   return addr.toLowerCase();&lt;br /&gt;  else&lt;br /&gt;   return addr;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * check if an email address has a display name.&lt;br /&gt;  * &lt;br /&gt;  * @param addr -&lt;br /&gt;  *            email address&lt;br /&gt;  * @return true if it has a display name&lt;br /&gt;  */&lt;br /&gt; public static boolean hasDisplayName(String addr) {&lt;br /&gt;  if (isEmpty(addr)) return false;&lt;br /&gt;  return addr.matches("^\\s*\\S+.{0,250}\\&amp;lt;.+\\&amp;gt;\\s*$");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * return the display name of an email address.&lt;br /&gt;  * &lt;br /&gt;  * @param addr -&lt;br /&gt;  *            email address&lt;br /&gt;  * @return - display name of the address, or null if the email address does&lt;br /&gt;  *         not have a display name.&lt;br /&gt;  */&lt;br /&gt; public static String getDisplayName(String addr) {&lt;br /&gt;  if (isEmpty(addr)) {&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  int at_pos = addr.lastIndexOf("@");&lt;br /&gt;  if (at_pos &amp;gt; 0) {&lt;br /&gt;   int pos1 = addr.lastIndexOf("&amp;lt;", at_pos);&lt;br /&gt;   int pos2 = addr.indexOf("&amp;gt;", at_pos + 1);&lt;br /&gt;   if (pos1 &amp;gt;= 0 &amp;amp;&amp;amp; pos2 &amp;gt; pos1) {&lt;br /&gt;    String dispName = addr.substring(0, pos1);&lt;br /&gt;    return dispName.trim();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Compare two email addresses. Email address could be enclosed by angle&lt;br /&gt;  * brackets and it should still be equal to the one without angle brackets.&lt;br /&gt;  * &lt;br /&gt;  * @param addr1 -&lt;br /&gt;  *            email address 1&lt;br /&gt;  * @param addr2 -&lt;br /&gt;  *            email address 2&lt;br /&gt;  * @return 0 if addr1 == addr2, -1 if addr1 &amp;lt; addr2, or 1 if addr1 &amp;gt; addr2.&lt;br /&gt;  */&lt;br /&gt; public static int compareEmailAddrs(String addr1, String addr2) {&lt;br /&gt;  if (addr1 == null) {&lt;br /&gt;   if (addr2 != null) {&lt;br /&gt;    return -1;&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    return 0;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (addr2 == null) {&lt;br /&gt;   return 1;&lt;br /&gt;  }&lt;br /&gt;  addr1 = removeDisplayName(addr1, true);&lt;br /&gt;  addr2 = removeDisplayName(addr2, true);&lt;br /&gt;  return addr1.compareToIgnoreCase(addr2);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * returns the domain name of an email address.&lt;br /&gt;  * &lt;br /&gt;  * @param addr -&lt;br /&gt;  *            email address&lt;br /&gt;  * @return domain name of the address, or null if it's a local address&lt;br /&gt;  */&lt;br /&gt; public static String getEmailDomainName(String addr) {&lt;br /&gt;  if (isEmpty(addr)) {&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  int pos;&lt;br /&gt;  if ((pos = addr.lastIndexOf("@")) &amp;gt; 0) {&lt;br /&gt;   String domain = addr.substring(pos + 1).trim();&lt;br /&gt;   if (domain.endsWith("&amp;gt;")) {&lt;br /&gt;    domain = domain.substring(0, domain.length() - 1);&lt;br /&gt;   }&lt;br /&gt;   return (domain.length() == 0 ? null : domain);&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;  * Strip off leading and trailing spaces for all String objects on a list.&lt;br /&gt;  * &lt;br /&gt;  * @param list -&lt;br /&gt;  *            a list of objects&lt;br /&gt;  */&lt;br /&gt; public static void stripAll(ArrayList&amp;lt;Object&amp;gt; list) {&lt;br /&gt;  if (list==null) return;&lt;br /&gt;  for (int i=0; i&amp;lt;list.size(); i++) {&lt;br /&gt;   Object obj = list.get(i);&lt;br /&gt;   if (obj!=null &amp;amp;&amp;amp; obj instanceof String)&lt;br /&gt;    list.set(i,((String)obj).trim());&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Strip off leading and trailing spaces for all String objects in an array.&lt;br /&gt;  * &lt;br /&gt;  * @param array -&lt;br /&gt;  *            an array of objects&lt;br /&gt;  */&lt;br /&gt; public static void stripAll(Object[] array) {&lt;br /&gt;  if (array==null) return;&lt;br /&gt;  for (int i=0; i&amp;lt;array.length; i++) {&lt;br /&gt;   Object obj = array[i];&lt;br /&gt;   if (obj!=null &amp;amp;&amp;amp; obj instanceof String)&lt;br /&gt;    array[i]=((String)obj).trim();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * For String objects found in the bean class with a getter and a setter,&lt;br /&gt;  * this method will strip off the leading and trailing spaces of those&lt;br /&gt;  * string objects.&lt;br /&gt;  * &lt;br /&gt;  * @param obj -&lt;br /&gt;  *            a bean object&lt;br /&gt;  */&lt;br /&gt; public static void stripAll(Object obj) {&lt;br /&gt;  if (obj == null) {&lt;br /&gt;   return;&lt;br /&gt;  }&lt;br /&gt;  Method methods[] = obj.getClass().getDeclaredMethods();&lt;br /&gt;  try {&lt;br /&gt;   Class&amp;lt;?&amp;gt; setParms[] = { Class.forName("java.lang.String") };&lt;br /&gt;   for (int i = 0; i &amp;lt; methods.length; i++) {&lt;br /&gt;    Method method = (Method) methods[i];&lt;br /&gt;    Class&amp;lt;?&amp;gt; parmTypes[] = method.getParameterTypes();&lt;br /&gt;    int mod = method.getModifiers();&lt;br /&gt;    if (Modifier.isPublic(mod) &amp;amp;&amp;amp; !Modifier.isAbstract(mod) &amp;amp;&amp;amp; !Modifier.isStatic(mod)) {&lt;br /&gt;     if (method.getName().startsWith("get") &amp;amp;&amp;amp; parmTypes.length == 0&lt;br /&gt;       &amp;amp;&amp;amp; method.getReturnType().getName().equals("java.lang.String")) {&lt;br /&gt;      // invoke the get method&lt;br /&gt;      String str = (String) method.invoke(obj, (Object[])parmTypes);&lt;br /&gt;      if (str != null) { // trim the string&lt;br /&gt;       String setMethodName = "set" + method.getName().substring(3);&lt;br /&gt;       try {&lt;br /&gt;        Method setMethod = obj.getClass()&lt;br /&gt;          .getMethod(setMethodName, setParms);&lt;br /&gt;        if (setMethod != null) {&lt;br /&gt;         String strParms[] = { str.trim() };&lt;br /&gt;         setMethod.invoke(obj, (Object[])strParms);&lt;br /&gt;        }&lt;br /&gt;       }&lt;br /&gt;       catch (Exception e) {&lt;br /&gt;        // no corresponding set method, ignore.&lt;br /&gt;       }&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   System.err.println("ERROR: Exception caught during reflection - " + e);&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * replace all occurrences of replFrom with replWith in a string.&lt;br /&gt;  * &lt;br /&gt;  * @param body -&lt;br /&gt;  *            message text&lt;br /&gt;  * @param replFrom -&lt;br /&gt;  *            from string&lt;br /&gt;  * @param replWith -&lt;br /&gt;  *            with string&lt;br /&gt;  * @return new string&lt;br /&gt;  */&lt;br /&gt; public static String replaceAll(String body, String replFrom, String replWith) {&lt;br /&gt;  if (body == null || body.trim().length() == 0) {&lt;br /&gt;   return body;&lt;br /&gt;  }&lt;br /&gt;  if (replFrom == null || replWith == null) {&lt;br /&gt;   logger.warn("replaceAll() - either replFrom or replyWith is null.");&lt;br /&gt;   return body;&lt;br /&gt;  }&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  int newpos = 0, pos = 0;&lt;br /&gt;  while ((newpos = body.indexOf(replFrom, pos)) &amp;gt;= 0) {&lt;br /&gt;   sb.append(body.substring(pos, newpos));&lt;br /&gt;   sb.append(replWith);&lt;br /&gt;   pos = newpos + Math.max(1, replFrom.length());&lt;br /&gt;  }&lt;br /&gt;  sb.append(body.substring(pos, body.length()));&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * remove the first occurrence of the given string from a string.&lt;br /&gt;  * &lt;br /&gt;  * @param body -&lt;br /&gt;  *            original body&lt;br /&gt;  * @param removeStr -&lt;br /&gt;  *            string to be removed&lt;br /&gt;  * @return new body&lt;br /&gt;  */&lt;br /&gt; public static String removeFirstString(String body, String removeStr) {&lt;br /&gt;  return removeString(body, removeStr, false);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * remove the last occurrence of the given string from a string.&lt;br /&gt;  * &lt;br /&gt;  * @param body -&lt;br /&gt;  *            original body&lt;br /&gt;  * @param removeStr -&lt;br /&gt;  *            string to be removed&lt;br /&gt;  * @return new body&lt;br /&gt;  */&lt;br /&gt; public static String removeLastString(String body, String removeStr) {&lt;br /&gt;  return removeString(body, removeStr, true);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static String removeString(String body, String removeStr, boolean removeLast) {&lt;br /&gt;  if (body == null || body.trim().length() == 0) {&lt;br /&gt;   return body;&lt;br /&gt;  }&lt;br /&gt;  if (removeStr == null || removeStr.trim().length() == 0) {&lt;br /&gt;   return body;&lt;br /&gt;  }&lt;br /&gt;  int pos = -1;&lt;br /&gt;  if (removeLast) {&lt;br /&gt;   pos = body.lastIndexOf(removeStr);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   pos = body.indexOf(removeStr);&lt;br /&gt;  }&lt;br /&gt;  if (pos &amp;gt;= 0) {&lt;br /&gt;   body = body.substring(0, pos) + body.substring(pos + removeStr.length());&lt;br /&gt;  }&lt;br /&gt;  return body;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * returns a string of periods.&lt;br /&gt;  * &lt;br /&gt;  * @param level -&lt;br /&gt;  *            number periods to be returned&lt;br /&gt;  * @return string of periods&lt;br /&gt;  */&lt;br /&gt; public static String getPeriods(int level) {&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  for (int i = 0; i &amp;lt; level; i++) {&lt;br /&gt;   sb.append(".");&lt;br /&gt;  }&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public static String removeCRLFTabs(String str) {&lt;br /&gt;  // remove possible CR/LF and tabs, that are inserted by some Email&lt;br /&gt;  // servers, from the Email_ID found in bounced E-mails (MS exchange&lt;br /&gt;  // server for one). MS exchange server inserted "\r\n\t" into the&lt;br /&gt;  // Email_ID string, and it caused "check digit test" error.&lt;br /&gt;  StringTokenizer sTokens = new StringTokenizer(str, "\r\n\t");&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  while (sTokens.hasMoreTokens()) {&lt;br /&gt;   sb.append(sTokens.nextToken());&lt;br /&gt;  }&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;    private final static String localPart = "[a-z0-9!#$%&amp;amp;'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&amp;amp;'*+/=?^_`{|}~-]+)*";&lt;br /&gt; private final static String remotePart = "@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])+";&lt;br /&gt; private final static String intraPart = "@[a-z0-9](?:[a-z0-9-]*[a-z0-9])+";&lt;br /&gt; &lt;br /&gt;    private final static Pattern remotePattern = Pattern.compile("^" + localPart + remotePart + "$",&lt;br /&gt;   Pattern.CASE_INSENSITIVE);&lt;br /&gt;    private final static Pattern intraPattern = Pattern.compile("^" + localPart + intraPart + "$",&lt;br /&gt;   Pattern.CASE_INSENSITIVE);&lt;br /&gt;  private static final Pattern localPattern = Pattern.compile("^" + localPart + "$",&lt;br /&gt;   Pattern.CASE_INSENSITIVE);&lt;br /&gt;  &lt;br /&gt;  public static String getEmailRegex() {&lt;br /&gt;   return localPart + remotePart;&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;    /**&lt;br /&gt;  * Check if the provided string is a valid email address. This conforms to&lt;br /&gt;  * the RFC822 and RFC1035 specifications. Both local part and remote part&lt;br /&gt;  * are required.&lt;br /&gt;  * &lt;br /&gt;  * @param string&lt;br /&gt;  *            The string to be checked.&lt;br /&gt;  * @return True if string is an valid email address. False if not.&lt;br /&gt;  */&lt;br /&gt;    public static boolean isRemoteEmailAddress(String string) {&lt;br /&gt;     if (string == null) return false;&lt;br /&gt;     Matcher matcher = remotePattern.matcher(string);&lt;br /&gt;     return matcher.matches();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;  * Check if the provided string is a valid remote or Intranet email address.&lt;br /&gt;  * An Intranet email address could include only a sub-domain name such as&lt;br /&gt;  * "bounce" or "localhost" as its remote part.&lt;br /&gt;  * &lt;br /&gt;  * @param string&lt;br /&gt;  *            The string to be checked.&lt;br /&gt;  * @return True if string is an valid email address. False if not.&lt;br /&gt;  */&lt;br /&gt;    public static boolean isRemoteOrIntranetEmailAddress(String string) {&lt;br /&gt;     if (string == null) return false;&lt;br /&gt;     if (isRemoteEmailAddress(string)) return true;&lt;br /&gt;     Matcher matcher = intraPattern.matcher(string);&lt;br /&gt;     return matcher.matches();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * matches any remote or local email addresses like john (without a remote&lt;br /&gt;  * part) or john@localhost or john@smith.com.&lt;br /&gt;  * &lt;br /&gt;  * @param string&lt;br /&gt;  *            the email address to be checked&lt;br /&gt;  * @return true if it's a valid email address&lt;br /&gt;  */&lt;br /&gt; public static boolean isRemoteOrLocalEmailAddress(String string) {&lt;br /&gt;  if (string == null) return false;&lt;br /&gt;  if (isRemoteOrIntranetEmailAddress(string)) return true;&lt;br /&gt;  Matcher matcher = localPattern.matcher(string);&lt;br /&gt;  return matcher.matches();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;    public static boolean isValidEmailLocalPart(String string) {&lt;br /&gt;     Matcher matcher = localPattern.matcher(string);&lt;br /&gt;     return matcher.matches();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt; &lt;br /&gt; private static String bounceRegex = (new StringBuilder("\\s*\\W?((\\w+)\\-(")).append(&lt;br /&gt;   TOKEN_XHDR_BEGIN).append("\\d+").append(TOKEN_XHDR_END).append(&lt;br /&gt;   ")\\-(.+\\=.+)\\@(.+\\w))\\W?\\s*").toString();&lt;br /&gt;  // for ex.: bounce-10.07410251.0-jsmith=test.com@localhost&lt;br /&gt; private static Pattern bouncePattern = Pattern.compile(bounceRegex);&lt;br /&gt; private static String removeRegex = "\\s*\\W?((\\w+)\\-(\\w+)\\-(.+\\=.+)\\@(.+\\w))\\W?\\s*";&lt;br /&gt;  // for ex.: remove-testlist-jsmith=test.com@localhost&lt;br /&gt; private static Pattern removePattern = Pattern.compile(removeRegex);&lt;br /&gt; &lt;br /&gt; public static boolean isVERPAddress(String recipient) {&lt;br /&gt;  if (isEmpty(recipient)) {&lt;br /&gt;   return false;&lt;br /&gt;  }&lt;br /&gt;  Matcher bounceMatcher = bouncePattern.matcher(recipient);&lt;br /&gt;  Matcher removeMatcher = removePattern.matcher(recipient);&lt;br /&gt;  return bounceMatcher.matches() || removeMatcher.matches();&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static String getDestAddrFromVERP(String verpAddr) {&lt;br /&gt;  Matcher bounceMatcher = bouncePattern.matcher(verpAddr);&lt;br /&gt;  if (bounceMatcher.matches()) {&lt;br /&gt;   if (bounceMatcher.groupCount() &amp;gt;= 5) {&lt;br /&gt;    String destAddr = bounceMatcher.group(2) + "@" + bounceMatcher.group(5);&lt;br /&gt;    return destAddr;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  Matcher removeMatcher = removePattern.matcher(verpAddr);&lt;br /&gt;  if (removeMatcher.matches()) {&lt;br /&gt;   if (removeMatcher.groupCount() &amp;gt;= 5) {&lt;br /&gt;    String destAddr = removeMatcher.group(2) + "@" + removeMatcher.group(5);&lt;br /&gt;    return destAddr;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return verpAddr;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static String getOrigAddrFromVERP(String verpAddr) {&lt;br /&gt;  Matcher bounceMatcher = bouncePattern.matcher(verpAddr);&lt;br /&gt;  if (bounceMatcher.matches()) {&lt;br /&gt;   if (bounceMatcher.groupCount() &amp;gt;= 4) {&lt;br /&gt;    String origAddr = bounceMatcher.group(4).replace('=', '@');&lt;br /&gt;    return origAddr;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  Matcher removeMatcher = removePattern.matcher(verpAddr);&lt;br /&gt;  if (removeMatcher.matches()) {&lt;br /&gt;   if (removeMatcher.groupCount() &amp;gt;= 4) {&lt;br /&gt;    String origAddr = removeMatcher.group(4).replace('=', '@');&lt;br /&gt;    return origAddr;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return verpAddr;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  String addr = "\"ORCPT foobar@nc.rr.com\" &amp;lt;foobar@nc.rr.com&amp;gt;";&lt;br /&gt;  addr = "DirectStarTV &amp;lt;fqusoogd.undlwfeteot@chaffingphotosensitive.com&amp;gt;";&lt;br /&gt;  System.out.println(addr+" --&amp;gt; "+removeDisplayName(addr));&lt;br /&gt;  &lt;br /&gt;  System.out.println(removeFirstString("&amp;lt;pre&amp;gt;12345abcdefklqhdkh&amp;lt;/pre&amp;gt;", "&amp;lt;pre&amp;gt;"));&lt;br /&gt;  System.out.println("EmailAddr: " + isRemoteEmailAddress("A!#$%&amp;amp;'*+/=?.^_`{|}~-BC@localhost.us"));&lt;br /&gt;  System.out.println("EmailAddr: " + isRemoteOrLocalEmailAddress("A!#$%&amp;amp;'*+/=?.^_`{|}~-BC"));&lt;br /&gt;  System.out.println(getOrigAddrFromVERP("bounce-10.07410251.0-jsmith=test.com@localhost"));&lt;br /&gt;  System.out.println(getOrigAddrFromVERP("remove-testlist-jsmith=test.com@localhost"));&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-8776628233440790181?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/8776628233440790181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-5.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/8776628233440790181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/8776628233440790181'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-5.html' title='Portable java mail message bean - Part 5'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-2832114107692107198</id><published>2009-09-03T13:07:00.000-04:00</published><updated>2009-09-03T14:27:50.799-04:00</updated><title type='text'>Portable java mail message bean - Part 4</title><content type='html'>In the first three parts of this series, we have defined a portable message bean comprises of four serializable classes that are used to represent a simple or complex mime message.&lt;br /&gt;Provided in this part is a utility class (as promised) that converts a java mail mime message instance into a portable message bean, or vise verse. A simple static method (mimeToBean) is provided that takes a java mail mime message as input, and returns a portable message bean. Another static method (beanToMime) is provided to return a java mail mime message from a portable message bean.&lt;br /&gt;In order to use this class, a string manipulation utility class is needed, which will be provided in Part 5.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/MessageBeanUtil.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;/pre&gt;&lt;pre&gt;* &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.ByteArrayInputStream;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.io.UnsupportedEncodingException;&lt;br /&gt;import java.lang.reflect.Method;&lt;br /&gt;import java.lang.reflect.Modifier;&lt;br /&gt;import java.net.UnknownHostException;&lt;br /&gt;import java.text.ParseException;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Calendar;&lt;br /&gt;import java.util.Collections;&lt;br /&gt;import java.util.Date;&lt;br /&gt;import java.util.Enumeration;&lt;br /&gt;import java.util.HashSet;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.Set;&lt;br /&gt;&lt;br /&gt;import javax.activation.DataHandler;&lt;br /&gt;import javax.mail.Address;&lt;br /&gt;import javax.mail.BodyPart;&lt;br /&gt;import javax.mail.Header;&lt;br /&gt;import javax.mail.Message;&lt;br /&gt;import javax.mail.MessagingException;&lt;br /&gt;import javax.mail.Multipart;&lt;br /&gt;import javax.mail.Part;&lt;br /&gt;import javax.mail.Session;&lt;br /&gt;import javax.mail.Message.RecipientType;&lt;br /&gt;import javax.mail.internet.AddressException;&lt;br /&gt;import javax.mail.internet.InternetAddress;&lt;br /&gt;import javax.mail.internet.MailDateFormat;&lt;br /&gt;import javax.mail.internet.MimeBodyPart;&lt;br /&gt;import javax.mail.internet.MimeMessage;&lt;br /&gt;import javax.mail.internet.MimeMultipart;&lt;br /&gt;import javax.mail.util.ByteArrayDataSource;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;public final class MessageBeanUtil {&lt;br /&gt; static final Logger logger = Logger.getLogger(MessageBeanUtil.class);&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; final static String LF = System.getProperty("line.separator", "\n");&lt;br /&gt; &lt;br /&gt; static boolean debugSession = false;&lt;br /&gt; private static String hostName = null;&lt;br /&gt;&lt;br /&gt; public static final String RETURN_PATH = "Return-Path";&lt;br /&gt; public static final String XHEADER_PRIORITY = "X-Priority";&lt;br /&gt; public static final String XHEADER_MAILER = "X-Mailer";&lt;br /&gt; public static final String MESSAGE_TRUNCATED = "=== message truncated ===";&lt;br /&gt;&lt;br /&gt; private MessageBeanUtil() {&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * convert JavaMail MimeMessage to message bean&lt;br /&gt;  * &lt;br /&gt;  * @param p -&lt;br /&gt;  *            part&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  * @throws IOException&lt;br /&gt;  */&lt;br /&gt; public static MessageBean mimeToBean(Part p) throws IOException,&lt;br /&gt;   MessagingException {&lt;br /&gt;  // make sure it's a message&lt;br /&gt;  if (!(p instanceof Message) &amp;amp;&amp;amp; !(p instanceof MimeMessage)) {&lt;br /&gt;   // not a known message type&lt;br /&gt;   try {&lt;br /&gt;    logger.error("mimeToBean() - Unknown Part: " + p.getContentType());&lt;br /&gt;    logger.error("mimeToBean() - ---------------------------");&lt;br /&gt;   }&lt;br /&gt;   catch (Exception e) {&lt;br /&gt;    logger.error("Exception caught", e);&lt;br /&gt;   }&lt;br /&gt;   throw new MessagingException("Part was not a MimeMessage as expected");&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  if (hostName == null) {&lt;br /&gt;   try {&lt;br /&gt;    hostName = java.net.InetAddress.getLocalHost().getHostName();&lt;br /&gt;   }&lt;br /&gt;   catch (UnknownHostException e) {&lt;br /&gt;    logger.warn("mimeToBean() - UnknownHostException caught, default to localhost", e);&lt;br /&gt;    hostName = "localhost";&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  MessageBean msgBean = new MessageBean();&lt;br /&gt;  msgBean.clearParameters();&lt;br /&gt;  &lt;br /&gt;  processEnvelope((Message) p, msgBean);&lt;br /&gt;&lt;br /&gt;  processAttachment((BodypartBean) msgBean, p, msgBean, 0);&lt;br /&gt;  &lt;br /&gt;  return msgBean;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * process message envelope and headers&lt;br /&gt;  * &lt;br /&gt;  * @param msg -&lt;br /&gt;  *           a MimeMessage instance&lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *           a MessageBean instance&lt;br /&gt;  * @return - SMTP message id, or null if not found&lt;br /&gt;  * @throws AddressException&lt;br /&gt;  */&lt;br /&gt; private static String processEnvelope(Message msg, MessageBean msgBean)&lt;br /&gt;   throws AddressException {&lt;br /&gt;  Address[] from = null,&lt;br /&gt;   received_to = null,&lt;br /&gt;   envelope_to = null,&lt;br /&gt;   cc = null,&lt;br /&gt;   bcc = null,&lt;br /&gt;   replyto = null;&lt;br /&gt;  String[] xmailer = null;&lt;br /&gt;  String subject = null;&lt;br /&gt;  String messageId= null;&lt;br /&gt;  java.util.Date receivedTime = null;&lt;br /&gt;&lt;br /&gt;  // Received Date&lt;br /&gt;  try {&lt;br /&gt;   receivedTime = msg.getReceivedDate();&lt;br /&gt;   if (receivedTime != null) {&lt;br /&gt;    msgBean.setSentDate(receivedTime);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getReceivedDate()", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // retrieve Message-Id, Return-Path and Received Time from headers&lt;br /&gt;  try {&lt;br /&gt;   Enumeration&amp;lt;?&amp;gt; enu = ((MimeMessage) msg).getAllHeaders();&lt;br /&gt;   while (enu.hasMoreElements()) {&lt;br /&gt;    Header hdr = (Header) enu.nextElement();&lt;br /&gt;    String name = hdr.getName();&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug("processEnvelope() - header line: " + name + ": " + hdr.getValue());&lt;br /&gt;    if ("Message-ID".equalsIgnoreCase(name)) {&lt;br /&gt;     messageId= hdr.getValue();&lt;br /&gt;     logger.info("processEnvelope() - &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;Message-ID retrieved: " + messageId);&lt;br /&gt;     msgBean.setSmtpMessageId(messageId);&lt;br /&gt;    }&lt;br /&gt;    if (RETURN_PATH.equalsIgnoreCase(name)) {&lt;br /&gt;     msgBean.setReturnPath(hdr.getValue());&lt;br /&gt;    }&lt;br /&gt;    if ("Date".equals(name) &amp;amp;&amp;amp; receivedTime == null) {&lt;br /&gt;     receivedTime = getHeaderDate(hdr.getValue()); // SMTP Date&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("Exception caught from getAllHeaderLines()", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  Calendar rightNow = Calendar.getInstance();&lt;br /&gt;  // If Received DateTime not found from envelope, use current time&lt;br /&gt;  if (receivedTime == null) {&lt;br /&gt;   msgBean.setSentDate(rightNow.getTime());&lt;br /&gt;  }&lt;br /&gt;  // display Received Date Time&lt;br /&gt;  if (isDebugEnabled) {&lt;br /&gt;   logger.info("processEnvelope() - Email Received Time: "&lt;br /&gt;     + (receivedTime != null ? receivedTime.toString() : "UNKNOWN")&lt;br /&gt;     + ", SERVER-TIME: " + rightNow.getTime().toString());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  String[] received = null;&lt;br /&gt;  try {&lt;br /&gt;   received = ((MimeMessage) msg).getHeader("Received");&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught from getHeader(Received)", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // retrieve TO address from "Received" Headers.&lt;br /&gt;  String real_to = "";&lt;br /&gt;  if (received != null) { // sanity check, should never be null&lt;br /&gt;   // scan received headers for "for" address, starting from the bottom&lt;br /&gt;   // (the oldest) and going up until an email address is found.&lt;br /&gt;   int i;&lt;br /&gt;   String tmp_to = null;&lt;br /&gt;   for (i = received.length - 1; i &amp;gt;= 0; i--) {&lt;br /&gt;    if (isDebugEnabled) {&lt;br /&gt;     logger.debug("processEnvelope() - Received: " + received[i]);&lt;br /&gt;    }&lt;br /&gt;    if ((tmp_to = analyzeReceived(received[i])) != null) {&lt;br /&gt;     real_to = tmp_to;&lt;br /&gt;     logger.info("processEnvelope() - found \"for\" in Received Headers: " + real_to);&lt;br /&gt;     break; // exit loop&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  Address[] addr;&lt;br /&gt;  // get FROM from envelope or Return-Path&lt;br /&gt;  try {&lt;br /&gt;   String[] _froms = null;&lt;br /&gt;   if ((addr = msg.getFrom()) != null &amp;amp;&amp;amp; addr.length &amp;gt; 0) {&lt;br /&gt;    String addrStr = checkAddr(addr[0].toString());&lt;br /&gt;    for (int j = 1; j &amp;lt; addr.length; j++) {&lt;br /&gt;     addrStr += "," + checkAddr(addr[j].toString());&lt;br /&gt;    }&lt;br /&gt;    from = InternetAddress.parse(addrStr);&lt;br /&gt;   }&lt;br /&gt;   else if ((_froms = msg.getHeader(RETURN_PATH)) != null &amp;amp;&amp;amp; _froms.length &amp;gt; 0) {&lt;br /&gt;    logger.warn("processEnvelope() - FROM is missing from envelope, use Return-Path.");&lt;br /&gt;    String addrStr = checkAddr(_froms[0]);&lt;br /&gt;    for (int j = 1; j &amp;lt; _froms.length; j++) {&lt;br /&gt;     addrStr += "," + checkAddr(_froms[j]);&lt;br /&gt;    }&lt;br /&gt;    from = InternetAddress.parse(addrStr);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    // FROM is empty from envelope and Return-Path&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught from getFrom()", e);&lt;br /&gt;  }&lt;br /&gt;  msgBean.setFrom(from);&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("processEnvelope() - Email From Address: " + msgBean.getFromAsString());&lt;br /&gt;&lt;br /&gt;  // get TO from Received Headers&lt;br /&gt;  if (real_to != null &amp;amp;&amp;amp; real_to.trim().length() &amp;gt; 0) {&lt;br /&gt;   // found TO address from header&lt;br /&gt;   try {&lt;br /&gt;    received_to = InternetAddress.parse(real_to);&lt;br /&gt;   }&lt;br /&gt;   catch (javax.mail.internet.AddressException e) {&lt;br /&gt;    logger.error("Warning!!! AddressException caught from parsing " + real_to, e);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // get TO from envelope&lt;br /&gt;  try {&lt;br /&gt;   if ((addr = msg.getRecipients(RecipientType.TO)) != null &amp;amp;&amp;amp; addr.length &amp;gt; 0) {&lt;br /&gt;    String addrStr = checkAddr(addr[0].toString());&lt;br /&gt;    for (int j = 1; j &amp;lt; addr.length; j++) {&lt;br /&gt;     addrStr += "," + checkAddr(addr[j].toString());&lt;br /&gt;    }&lt;br /&gt;    envelope_to = InternetAddress.parse(addrStr);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    // TO is empty from envelope&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught from getRecipients(TO)", e);&lt;br /&gt;  }&lt;br /&gt;  msgBean.setToEnvelope(envelope_to);&lt;br /&gt;&lt;br /&gt;  // TO from "Delivered-To" header&lt;br /&gt;  Address[] delivered_to = null;&lt;br /&gt;  try {&lt;br /&gt;   String[] dlvrTo = msg.getHeader("Delivered-To");&lt;br /&gt;   if (dlvrTo != null &amp;amp;&amp;amp; dlvrTo.length &amp;gt; 0) {&lt;br /&gt;    String addrStr = checkAddr(dlvrTo[0]);&lt;br /&gt;    for (int j=1; j&amp;lt;dlvrTo.length; j++) {&lt;br /&gt;     addrStr += "," + checkAddr(dlvrTo[j]);&lt;br /&gt;    }&lt;br /&gt;    logger.info("processEnvelope() - \"Delivered-To\" found from header: " + addrStr);&lt;br /&gt;    delivered_to = InternetAddress.parse(addrStr);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught from msg.getHeader(\"Delivered-To\")", e);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // TO: Received (non-VERP) &amp;gt; Delivered-To &amp;gt; Received (VERP) &amp;gt; Envelope &lt;br /&gt;  if (received_to != null &amp;amp;&amp;amp; received_to.length &amp;gt; 0) {&lt;br /&gt;   String dest = received_to[0] == null ? null : received_to[0].toString();&lt;br /&gt;   if (!StringUtil.isEmpty(dest) &amp;amp;&amp;amp; !StringUtil.isVERPAddress(dest)) {&lt;br /&gt;    msgBean.setTo(received_to);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (msgBean.getTo() == null) {&lt;br /&gt;   if (delivered_to != null &amp;amp;&amp;amp; delivered_to.length &amp;gt; 0) {&lt;br /&gt;    // The real mailbox address this email is delivered to. If the email&lt;br /&gt;    // address is a VERP address, the original address is restored from&lt;br /&gt;    // the VERP address and is assigned to "Delivered-To" header.&lt;br /&gt;    msgBean.setTo(delivered_to);&lt;br /&gt;   }&lt;br /&gt;   else if (received_to != null &amp;amp;&amp;amp; received_to.length &amp;gt; 0) {&lt;br /&gt;    // Email address extracted from "Received" header is the real email&lt;br /&gt;    // address. But when VERP is enabled, since the Email Id is embedded&lt;br /&gt;    // in the VERP address, every email received will have its own VERP&lt;br /&gt;    // address. This will cause a disaster to EmailAddr table since all&lt;br /&gt;    // TO addresses are saved to that table.&lt;br /&gt;    String dest = received_to[0] == null ? null : received_to[0].toString();&lt;br /&gt;    if (!StringUtil.isEmpty(dest) &amp;amp;&amp;amp; StringUtil.isVERPAddress(dest)) {&lt;br /&gt;     String verpDest = StringUtil.getDestAddrFromVERP(dest);&lt;br /&gt;     try {&lt;br /&gt;      Address[] destAddr = InternetAddress.parse(verpDest);&lt;br /&gt;      msgBean.setTo(destAddr);&lt;br /&gt;     }&lt;br /&gt;     catch (AddressException e) {&lt;br /&gt;      logger.error("AddressException from Received_To: " + dest);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   if (msgBean.getTo() == null) {&lt;br /&gt;    msgBean.setTo(envelope_to);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  logger.info("processEnvelope() - Email To from Delivered-To: " + StringUtil.addrToString(delivered_to, false)&lt;br /&gt;    + ", from Received Header: " + StringUtil.addrToString(received_to, false)&lt;br /&gt;    + ", from Envelope: " + StringUtil.addrToString(envelope_to, false));&lt;br /&gt;  &lt;br /&gt;  // CC&lt;br /&gt;  try {&lt;br /&gt;   if ((addr = msg.getRecipients(RecipientType.CC)) != null &amp;amp;&amp;amp; addr.length &amp;gt; 0) {&lt;br /&gt;    cc = addr;&lt;br /&gt;    msgBean.setCc(cc);&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug("processEnvelope() - Email CC Address: " + msgBean.getCcAsString());&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getRecipients(CC)", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // BCC&lt;br /&gt;  try {&lt;br /&gt;   if ((addr = msg.getRecipients(RecipientType.BCC)) != null &amp;amp;&amp;amp; addr.length &amp;gt; 0) {&lt;br /&gt;    bcc = addr;&lt;br /&gt;    msgBean.setBcc(bcc);&lt;br /&gt;    if (isDebugEnabled)&lt;br /&gt;     logger.debug("processEnvelope() - Email BCC Address: " + msgBean.getBccAsString());&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getRecipients(BCC)", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // REPLYTO&lt;br /&gt;  try {&lt;br /&gt;   if ((addr = msg.getReplyTo()) != null &amp;amp;&amp;amp; addr.length &amp;gt; 0) {&lt;br /&gt;    String addrStr = checkAddr(addr[0].toString());&lt;br /&gt;    for (int j = 1; j &amp;lt; addr.length; j++) {&lt;br /&gt;     addrStr += "," + checkAddr(addr[j].toString());&lt;br /&gt;    }&lt;br /&gt;    replyto = InternetAddress.parse(addrStr);&lt;br /&gt;    msgBean.setReplyto(replyto);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getReplyTo()", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // SUBJECT&lt;br /&gt;  try {&lt;br /&gt;   subject = msg.getSubject();&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getSubject()", e);&lt;br /&gt;  }&lt;br /&gt;  msgBean.setSubject(subject);&lt;br /&gt;  if (isDebugEnabled)&lt;br /&gt;   logger.debug("processEnvelope() - Email Subject: [" + subject + "]");&lt;br /&gt;&lt;br /&gt;  // X-MAILER&lt;br /&gt;  try {&lt;br /&gt;   String[] hdrs = msg.getHeader(XHEADER_MAILER);&lt;br /&gt;   if (hdrs != null) {&lt;br /&gt;    xmailer = hdrs;&lt;br /&gt;    msgBean.setXmailer(xmailer);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getHeader(X-Mailer)", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // X-Priority: 1 (High), 2 (Normal), 3 (Low)&lt;br /&gt;  try {&lt;br /&gt;   String[] priority = ((MimeMessage) msg).getHeader(XHEADER_PRIORITY);&lt;br /&gt;   if (priority != null) {&lt;br /&gt;    msgBean.setPriority(priority);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   logger.error("MessagingException caught during getHeader(X-Priority)", e);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return messageId;&lt;br /&gt; } // end of processEnvelope&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * recursively build up an attachment tree structure from a MultiPart&lt;br /&gt;  * message.&lt;br /&gt;  * &lt;br /&gt;  * @param aNode -&lt;br /&gt;  *            current BodypartBean&lt;br /&gt;  * @param p -&lt;br /&gt;  *            JavaMail part&lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *            root message bean&lt;br /&gt;  * @param level -&lt;br /&gt;  *            attachment level&lt;br /&gt;  */&lt;br /&gt; private static void processAttachment(BodypartBean aNode, Part p, MessageBean msgBean,&lt;br /&gt;   int level) {&lt;br /&gt;  String disp = null, desc = null, contentType = "text/plain";&lt;br /&gt;  String dispOrig = null, descOrig = null;&lt;br /&gt;  String fileName = null;&lt;br /&gt;  // initialize part size&lt;br /&gt;  int partSize = 0;&lt;br /&gt;  // get content type&lt;br /&gt;  try {&lt;br /&gt;   contentType = p.getContentType();&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   contentType = "text/plain"; // failed to get content type, use default&lt;br /&gt;   logger.error("Exception caught during getContentType()", e);&lt;br /&gt;  }&lt;br /&gt;  // get disposition&lt;br /&gt;  try {&lt;br /&gt;   dispOrig = p.getDisposition();&lt;br /&gt;   /*&lt;br /&gt;    * disposition may look like:&lt;br /&gt;     * 1) inline &lt;br /&gt;     * 2) attachment &lt;br /&gt;     * 3) attachment|inline; filename=xxxxx&lt;br /&gt;    * I believe JavaMail is taking care of this. However the code&lt;br /&gt;    * stays here just for safety.&lt;br /&gt;    */ &lt;br /&gt;   if (dispOrig != null &amp;amp;&amp;amp; dispOrig.indexOf(";") &amp;gt; 0) {&lt;br /&gt;    disp = dispOrig.substring(0, dispOrig.indexOf(";"));&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    disp = dispOrig;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("Exception caught during getDisposition()", e);&lt;br /&gt;  }&lt;br /&gt;  // get description&lt;br /&gt;  try {&lt;br /&gt;   descOrig = p.getDescription();&lt;br /&gt;   // to make use of "desc" field by saving attachment file name on it.&lt;br /&gt;   if (descOrig == null) {&lt;br /&gt;    // if null, get attachment filename from content type field&lt;br /&gt;    desc = getFileName(contentType);&lt;br /&gt;    // JavaMail appends file name to content type field if one is&lt;br /&gt;    // found from disposition field&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    desc = descOrig;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("Exception caught during getDescription()", e);&lt;br /&gt;  }&lt;br /&gt;  // get file name&lt;br /&gt;  try {&lt;br /&gt;   fileName = p.getFileName();&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("Exception caught during getFileName()", e);&lt;br /&gt;  }&lt;br /&gt;  // get part size&lt;br /&gt;  try {&lt;br /&gt;   partSize = p.getSize();&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("Exception caught during getSize()", e);&lt;br /&gt;  }&lt;br /&gt;  // display some key information&lt;br /&gt;  if (isDebugEnabled) {&lt;br /&gt;   logger.info("processAttachment() - getDisposition(): " + dispOrig);&lt;br /&gt;   logger.info("processAttachment() - getDescription(): " + descOrig);&lt;br /&gt;   logger.info("processAttachment() - getContentType(): " + contentType + ", level:" + level + ", size:"&lt;br /&gt;     + partSize);&lt;br /&gt;  }&lt;br /&gt;  if (fileName != null &amp;amp;&amp;amp; isDebugEnabled) {&lt;br /&gt;   logger.debug("processAttachment() - getFileName() = " + fileName);&lt;br /&gt;  }&lt;br /&gt;  // build mime tree&lt;br /&gt;  try {&lt;br /&gt;   aNode.setDisposition(disp);&lt;br /&gt;   aNode.setDescription(desc);&lt;br /&gt;   aNode.setFileName(fileName);&lt;br /&gt;   // update attachment count&lt;br /&gt;   if (Part.ATTACHMENT.equalsIgnoreCase(disp) &lt;br /&gt;     || (Part.INLINE.equalsIgnoreCase(disp) &amp;amp;&amp;amp; desc != null)&lt;br /&gt;     || getFileName(contentType) != null) {&lt;br /&gt;    // update attachment count&lt;br /&gt;    msgBean.updateAttachCount(1);&lt;br /&gt;   }&lt;br /&gt;   // set content type and header fields&lt;br /&gt;   aNode.setContentType(contentType);&lt;br /&gt;   aNode.setHeaders(p);&lt;br /&gt;   aNode.setSize(partSize);&lt;br /&gt;   /*&lt;br /&gt;    * Using isMimeType to determine the content type.&lt;br /&gt;    */&lt;br /&gt;   if (p.isMimeType("text/plain") || p.isMimeType("text/html")) {&lt;br /&gt;    logger.info("processAttachment(): level " + level + ", text message: " + contentType);&lt;br /&gt;    aNode.setValue((String) p.getContent());&lt;br /&gt;    msgBean.getComponentsSize().add(Integer.valueOf(aNode.getSize()));&lt;br /&gt;   }&lt;br /&gt;   else if (p.isMimeType("multipart/*")) {&lt;br /&gt;    logger.info("processAttachment(): level " + level + ", Recursive Multipart: " + contentType);&lt;br /&gt;    Multipart mp = (Multipart) p.getContent();&lt;br /&gt;    int count = mp.getCount();&lt;br /&gt;    for (int i = 0; i &amp;lt; count; i++) {&lt;br /&gt;     Part p1 = mp.getBodyPart(i);&lt;br /&gt;     // call itself to build up a child attachment tree&lt;br /&gt;     if (p1 != null) {&lt;br /&gt;      BodypartBean subNode = new BodypartBean();&lt;br /&gt;      processAttachment(subNode, p1, msgBean, level+1);&lt;br /&gt;      aNode.put(subNode);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else if (p.isMimeType("message/rfc822")) {&lt;br /&gt;    // nested message type&lt;br /&gt;    logger.info("processAttachment(): level " + level + ", RFC822 Message: " + contentType);&lt;br /&gt;    Part p1 = (Part) p.getContent();&lt;br /&gt;    if (p1 != null) {&lt;br /&gt;     BodypartBean subNode = new BodypartBean();&lt;br /&gt;     processAttachment(subNode, p1, msgBean, level+1);&lt;br /&gt;     aNode.put(subNode);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    /*&lt;br /&gt;     * other mime type. could be application, image, audio, video, message, etc.&lt;br /&gt;     */&lt;br /&gt;    Object o = p.getContent();&lt;br /&gt;    /*&lt;br /&gt;     * unknown mine type section. check its java type.&lt;br /&gt;     */&lt;br /&gt;    if (o instanceof String) {&lt;br /&gt;     // text type of section&lt;br /&gt;     logger.info("processAttachment(): level " + level + ", String Content " + contentType);&lt;br /&gt;     aNode.setValue((String) o);&lt;br /&gt;     if (aNode.getValue() != null) {&lt;br /&gt;      msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length));&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    else if (o instanceof InputStream) {&lt;br /&gt;     // stream type of section&lt;br /&gt;     logger.info("processAttachment(): level " + level + ", InputStream Content " + contentType);&lt;br /&gt;     InputStream is = (InputStream) o;&lt;br /&gt;     aNode.setValue((InputStream) is);&lt;br /&gt;     if (aNode.getValue() != null) {&lt;br /&gt;      msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length));&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    else if (o != null) {&lt;br /&gt;     // unknown Java type, write it out as a string anyway.&lt;br /&gt;     logger.error("processAttachment(): level " + level + ", Unknown type: " + o.toString());&lt;br /&gt;     aNode.setValue((String) o.toString());&lt;br /&gt;     if (aNode.getValue() != null) {&lt;br /&gt;      msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length));&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;     // no content&lt;br /&gt;     logger.error("processAttachment(): level " + level + ", Content is null");&lt;br /&gt;     aNode.setValue((Object)null);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  } // end of the try block&lt;br /&gt;  catch (IndexOutOfBoundsException e) {&lt;br /&gt;   /* thrown from mp.getBodyPart(i), should never happen */&lt;br /&gt;   logger.error("processAttachment(): IndexOutOfBoundsException caught: " + contentType);&lt;br /&gt;   logger.error("IndexOutOfBoundsException caught", e);&lt;br /&gt;   aNode.setValue("001: IndexOutOfBoundsException caught during process.");&lt;br /&gt;   BodypartBean subNode = new BodypartBean("text/plain");&lt;br /&gt;   aNode.put(subNode);&lt;br /&gt;   setAnodeValue(subNode, p, "002: IndexOutOfBoundsException thrown from mp.getBodyPart(i).");&lt;br /&gt;   if (subNode.getValue() != null) {&lt;br /&gt;    msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length));&lt;br /&gt;   }&lt;br /&gt;   subNode.setDisposition(aNode.getDisposition());&lt;br /&gt;   subNode.setDescription(aNode.getDescription());&lt;br /&gt;  }&lt;br /&gt;  catch (MessagingException e) {&lt;br /&gt;   /*&lt;br /&gt;    * JavaMail failed to read the message body, use its raw data instead&lt;br /&gt;    */&lt;br /&gt;   logger.error("processAttachment(): MessagingException caught: " + contentType);&lt;br /&gt;   logger.error("MessagingException caught", e);&lt;br /&gt;   if (contentType.trim().toLowerCase().startsWith("multipart/")&lt;br /&gt;     || contentType.trim().toLowerCase().startsWith("message/rfc822")) {&lt;br /&gt;    aNode.setValue("003: MessagingException caught during process.");&lt;br /&gt;    BodypartBean subNode = new BodypartBean("text/plain");&lt;br /&gt;    aNode.put(subNode);&lt;br /&gt;    setAnodeValue(subNode, p);&lt;br /&gt;    if (subNode.getValue() != null) {&lt;br /&gt;     msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length));&lt;br /&gt;    }&lt;br /&gt;    subNode.setDisposition(aNode.getDisposition());&lt;br /&gt;    subNode.setDescription(aNode.getDescription());&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    setAnodeValue(aNode, p);&lt;br /&gt;    if (aNode.getValue() != null) {&lt;br /&gt;     msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (UnsupportedEncodingException e) {&lt;br /&gt;   /* unsupported encoding found, use its raw data instead */&lt;br /&gt;   logger.error("processAttachment(): UnsupportedEncodingException caught: " + contentType);&lt;br /&gt;   logger.error("UnsupportedEncodingException caught", e);&lt;br /&gt;   if (contentType.trim().toLowerCase().startsWith("multipart/")&lt;br /&gt;     || contentType.trim().toLowerCase().startsWith("message/rfc822")) {&lt;br /&gt;    aNode.setValue("004: UnsupportedEncodingException caught during process.");&lt;br /&gt;    BodypartBean subNode = new BodypartBean("text/plain");&lt;br /&gt;    aNode.put(subNode);&lt;br /&gt;    setAnodeValue(subNode, p);&lt;br /&gt;    if (subNode.getValue() != null) {&lt;br /&gt;     msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) subNode.getValue()).length));&lt;br /&gt;    }&lt;br /&gt;    subNode.setDisposition(aNode.getDisposition());&lt;br /&gt;    subNode.setDescription(aNode.getDescription());&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    setAnodeValue(aNode, p);&lt;br /&gt;    if (aNode.getValue() != null) {&lt;br /&gt;     msgBean.getComponentsSize().add(Integer.valueOf(((byte[]) aNode.getValue()).length));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;   /*&lt;br /&gt;    * IOException caught during decoding, couldn't read the message&lt;br /&gt;    * body. Use "-- Message body has been omitted --" as body text&lt;br /&gt;    */&lt;br /&gt;   logger.error("processAttachment(): IOException caught: " + contentType);&lt;br /&gt;   logger.error("IOException caught", e);&lt;br /&gt;   if (contentType.trim().toLowerCase().startsWith("multipart/")&lt;br /&gt;     || contentType.trim().toLowerCase().startsWith("message/rfc822")) {&lt;br /&gt;    aNode.setValue("005: IOException caught during process.");&lt;br /&gt;    BodypartBean subNode = new BodypartBean("text/plain");&lt;br /&gt;    aNode.put(subNode);&lt;br /&gt;    subNode.setValue("-- Message body has been omitted --");&lt;br /&gt;    subNode.setDisposition(aNode.getDisposition());&lt;br /&gt;    subNode.setDescription(aNode.getDescription());&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    aNode.setValue("-- Message body has been omitted --");&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   /* all other unchecked exceptions */&lt;br /&gt;   logger.error("processAttachment(): Exception caught: " + contentType);&lt;br /&gt;   logger.error("Exception caught", e);&lt;br /&gt;   if (contentType.trim().toLowerCase().startsWith("multipart/")&lt;br /&gt;     || contentType.trim().toLowerCase().startsWith("message/rfc822")) {&lt;br /&gt;    aNode.setValue("006: Exception caught during process.");&lt;br /&gt;    BodypartBean subNode = new BodypartBean("text/plain");&lt;br /&gt;    aNode.put(subNode);&lt;br /&gt;    setAnodeValue(subNode, p, "Unchecked Exception caught: " + e.toString());&lt;br /&gt;    subNode.setDisposition(aNode.getDisposition());&lt;br /&gt;    subNode.setDescription(aNode.getDescription());&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    setAnodeValue(aNode, p, "Unchecked Exception caught: " + e.toString());&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; } // end of processAttachment&lt;br /&gt;&lt;br /&gt; private static void setAnodeValue(BodypartBean anode, Part p) {&lt;br /&gt;  setAnodeValue(anode, p, "-- Message body has been omitted. Exception thrown from p.getInputStream() --");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static void setAnodeValue(BodypartBean anode, Part p, String errmsg) {&lt;br /&gt;  try {&lt;br /&gt;   anode.setValue((InputStream) p.getInputStream());&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   anode.setValue(errmsg);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; final static MailDateFormat mailDateFormat = new MailDateFormat();&lt;br /&gt; private static java.util.Date getHeaderDate(String text) {&lt;br /&gt;  if (StringUtil.isEmpty(text)) return null;&lt;br /&gt;  try {&lt;br /&gt;   java.util.Date date = mailDateFormat.parse(text);&lt;br /&gt;   return date;&lt;br /&gt;  }&lt;br /&gt;  catch (ParseException e) {&lt;br /&gt;   logger.warn("getHeaderDate() - ParseException caught parsing: " + text);&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * check and reformat email address&lt;br /&gt;  * &lt;br /&gt;  * @param s -&lt;br /&gt;  *            email address&lt;br /&gt;  * @return reformatted address&lt;br /&gt;  */&lt;br /&gt; private static String checkAddr(String s) {&lt;br /&gt;  // do not append domain name by default&lt;br /&gt;  return checkAddr(s, false);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * check and reformat email address&lt;br /&gt;  * &lt;br /&gt;  * @param s -&lt;br /&gt;  *            email address&lt;br /&gt;  * @param needDomain -&lt;br /&gt;  *            true requires domain&lt;br /&gt;  * @return reformatted address&lt;br /&gt;  */&lt;br /&gt; private static String checkAddr(String s, boolean needDomain) {&lt;br /&gt;  if (s == null || s.trim().length() == 0) {&lt;br /&gt;   return s;&lt;br /&gt;  }&lt;br /&gt;  try {&lt;br /&gt;   InternetAddress.parse(s);&lt;br /&gt;  }&lt;br /&gt;  catch (javax.mail.internet.AddressException e) {&lt;br /&gt;   logger.error("AddressException caught during parsing", e);&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  String addr = s;&lt;br /&gt;  // is this a name only address?&lt;br /&gt;  if (needDomain &amp;amp;&amp;amp; addr.indexOf("@") &amp;lt; 0) {&lt;br /&gt;   int pos;&lt;br /&gt;   if ((pos = addr.indexOf("&amp;gt;")) &amp;lt; 0) {&lt;br /&gt;    // does it look like &amp;lt;user&amp;gt;? no - append default domain name&lt;br /&gt;    addr += "@" + hostName;&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    addr = addr.substring(0, pos) + "@" + hostName + "&amp;gt;";&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return addr;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * analyze "Received" header and retrieve address from the header&lt;br /&gt;  * &lt;br /&gt;  * @param received -&lt;br /&gt;  *            header&lt;br /&gt;  * @return "for" address if found, null otherwise.&lt;br /&gt;  */&lt;br /&gt; private static String analyzeReceived(String received) {&lt;br /&gt;  int semicolon_pos = -1;&lt;br /&gt;  if (received != null &amp;amp;&amp;amp; (semicolon_pos = received.indexOf(";")) &amp;gt; 0) {&lt;br /&gt;   received = received.substring(0, semicolon_pos);&lt;br /&gt;   received = received.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ');&lt;br /&gt;&lt;br /&gt;   // required fields&lt;br /&gt;   int from_pos = received.indexOf("from ");&lt;br /&gt;   int by_pos = received.indexOf(" by ", from_pos + 1);&lt;br /&gt;   int low_pos = Math.min(from_pos, by_pos);&lt;br /&gt;   int high_pos = Math.max(from_pos, by_pos);&lt;br /&gt;   int max_pos = high_pos;&lt;br /&gt;&lt;br /&gt;   // check optional fields&lt;br /&gt;   int via_pos = received.indexOf(" via ", max_pos + 1);&lt;br /&gt;   max_pos = Math.max(max_pos, via_pos);&lt;br /&gt;   int with_pos = received.indexOf(" with ", max_pos + 1);&lt;br /&gt;   max_pos = Math.max(max_pos, with_pos);&lt;br /&gt;   int id_pos = received.indexOf(" id ", max_pos + 1);&lt;br /&gt;   max_pos = Math.max(max_pos, id_pos);&lt;br /&gt;&lt;br /&gt;   int for_pos = received.indexOf(" for ", max_pos + 1);&lt;br /&gt;   if (low_pos &amp;gt;= 0 &amp;amp;&amp;amp; for_pos &amp;gt; high_pos) {&lt;br /&gt;    return received.substring(for_pos + 4);&lt;br /&gt;   }&lt;br /&gt;   else if (by_pos &amp;gt;= 0 &amp;amp;&amp;amp; with_pos &amp;gt; by_pos &amp;amp;&amp;amp; for_pos &amp;gt; with_pos) {&lt;br /&gt;    // AOL or Google - "received" could only contain "by" and&lt;br /&gt;    // "with", but no "from"&lt;br /&gt;    return received.substring(for_pos + 4);&lt;br /&gt;   }&lt;br /&gt;   else if (low_pos &amp;gt;= 0 &amp;amp;&amp;amp; max_pos &amp;gt; high_pos) {&lt;br /&gt;    // found optional field&lt;br /&gt;    // address may have a display name and the display name may&lt;br /&gt;    // contain one of the search keys used by the optional fields&lt;br /&gt;    // (indexOf())&lt;br /&gt;    for_pos = received.lastIndexOf(" for ");&lt;br /&gt;    if (for_pos &amp;gt; high_pos) {&lt;br /&gt;     return received.substring(for_pos + 4);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /*&lt;br /&gt;  * locate the file name from content-type.&lt;br /&gt;  * &lt;br /&gt;  * @param ctype -&lt;br /&gt;  *            content type&lt;br /&gt;  * @return file name extracted from the content type&lt;br /&gt;  */&lt;br /&gt; private static String getFileName(String ctype) {&lt;br /&gt;  String desc = null;&lt;br /&gt;  if (ctype != null &amp;amp;&amp;amp; ctype.indexOf("name=") &amp;gt;= 0) {&lt;br /&gt;   desc = ctype.substring(ctype.indexOf("name=") + 5);&lt;br /&gt;   if (desc != null &amp;amp;&amp;amp; desc.indexOf(";") &amp;gt; 0)&lt;br /&gt;    desc = desc.substring(0, desc.indexOf(";"));&lt;br /&gt;  }&lt;br /&gt;  return desc;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * convert MessageBean to JavaMail MimeMessage&lt;br /&gt;  * &lt;br /&gt;  * @param msgBean -&lt;br /&gt;  *            a MessageBean object&lt;br /&gt;  * @return JavaMail Message&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  * @throws IOException &lt;br /&gt;  */&lt;br /&gt; public static Message beanToMime(MessageBean msgBean) &lt;br /&gt;   throws MessagingException, IOException {&lt;br /&gt;  javax.mail.Session session = Session.getDefaultInstance(System.getProperties());&lt;br /&gt;  if (debugSession)&lt;br /&gt;   session.setDebug(true);&lt;br /&gt;  Message msg = new MimeMessage(session);&lt;br /&gt;&lt;br /&gt;  // First Set All Headers from a header List&lt;br /&gt;  List&amp;lt;MsgHeader&amp;gt; headers = msgBean.getHeaders();&lt;br /&gt;  if (headers != null) {&lt;br /&gt;   for (int i = 0; i &amp;lt; headers.size(); i++) {&lt;br /&gt;    MsgHeader header = headers.get(i);&lt;br /&gt;    if (!getReservedHeaders().contains(header.getName())) {&lt;br /&gt;     msg.setHeader(header.getName(), header.getValue());&lt;br /&gt;    }&lt;br /&gt;    if (isDebugEnabled) {&lt;br /&gt;     logger.debug("beanToMime() - Header Line - " + header.getName() + ": "&lt;br /&gt;       + header.getValue());&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  // override certain headers with the data from MesssageBean&lt;br /&gt;  if (msgBean.getFrom() != null) {&lt;br /&gt;   for (int i = 0; i &amp;lt; msgBean.getFrom().length; i++) {&lt;br /&gt;    // just for safety&lt;br /&gt;    if (msgBean.getFrom()[i] != null) {&lt;br /&gt;     msg.setFrom(msgBean.getFrom()[i]);&lt;br /&gt;     break;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   logger.warn("beanToMime() - MessageBean.getFrom() returns a null");&lt;br /&gt;   msg.setFrom();&lt;br /&gt;  }&lt;br /&gt;  if (msgBean.getTo() != null) {&lt;br /&gt;   msg.setRecipients(Message.RecipientType.TO, msgBean.getTo());&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   logger.warn("beanToMime() - MessageBean.getTo() returns a null");&lt;br /&gt;  }&lt;br /&gt;  if (msgBean.getCc() != null) {&lt;br /&gt;   msg.setRecipients(Message.RecipientType.CC, msgBean.getCc());&lt;br /&gt;  }&lt;br /&gt;  if (msgBean.getBcc() != null) {&lt;br /&gt;   msg.setRecipients(Message.RecipientType.BCC, msgBean.getBcc());&lt;br /&gt;  }&lt;br /&gt;  if (msgBean.getReplyto() != null) {&lt;br /&gt;   msg.setReplyTo(msgBean.getReplyto());&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  if (msgBean.getReturnPath() != null &amp;amp;&amp;amp; msgBean.getReturnPath().trim().length() &amp;gt; 0) {&lt;br /&gt;   msg.setHeader(RETURN_PATH, msgBean.getReturnPath());&lt;br /&gt;  }&lt;br /&gt;  msg.setHeader(XHEADER_PRIORITY, getMsgPriority(msgBean.getPriority()));&lt;br /&gt;  if (msgBean.getXmailer() != null &amp;amp;&amp;amp; msgBean.getXmailer().length &amp;gt; 0) {&lt;br /&gt;   msg.setHeader(XHEADER_MAILER, msgBean.getXmailer()[0]);&lt;br /&gt;  }&lt;br /&gt;  msg.setSentDate(new Date());&lt;br /&gt;&lt;br /&gt;  msg.setSubject(msgBean.getSubject() == null ? "" : msgBean.getSubject());&lt;br /&gt;&lt;br /&gt;  // construct message body part&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; aNodes = msgBean.getNodes();&lt;br /&gt;  if (msgBean.getMimeType().startsWith("multipart")) {&lt;br /&gt;   Multipart mp = new MimeMultipart(msgBean.getMimeSubType());&lt;br /&gt;   msg.setContent(mp);&lt;br /&gt;   constructMultiPart(mp, (BodypartBean) msgBean, 0);&lt;br /&gt;  }&lt;br /&gt;  else if (aNodes != null &amp;amp;&amp;amp; aNodes.size() &amp;gt; 0) {&lt;br /&gt;   Multipart mp = new MimeMultipart("mixed"); // make up a default&lt;br /&gt;   msg.setContent(mp);&lt;br /&gt;   if (msgBean.getValue()!=null) {&lt;br /&gt;    BodyPart bp = new MimeBodyPart();&lt;br /&gt;    mp.addBodyPart(bp);&lt;br /&gt;    constructSinglePart(bp, (BodypartBean) msgBean, 0);&lt;br /&gt;   }&lt;br /&gt;   constructMultiPart(mp, (BodypartBean) msgBean, 0);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   constructSinglePart(msg, (BodypartBean) msgBean, 0);&lt;br /&gt;  }&lt;br /&gt;  msg.saveChanges(); // please remember to save the message&lt;br /&gt;  &lt;br /&gt;  return msg;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /**&lt;br /&gt;  * create MessageBean from SMTP raw stream&lt;br /&gt;  * @param mailStream&lt;br /&gt;  * @return a MessageBean&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  */&lt;br /&gt; public static MessageBean createMessageBeanFromStream(byte[] mailStream)&lt;br /&gt;   throws MessagingException {&lt;br /&gt;  Message msg = createMimeMessageFromStream(mailStream);&lt;br /&gt;  try {&lt;br /&gt;   MessageBean msgBean = mimeToBean(msg);&lt;br /&gt;   return msgBean;&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;   logger.error("IOException caught", e);&lt;br /&gt;   throw new MessagingException(e.toString());&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * create JavaMail Message from SMTP raw stream&lt;br /&gt;  * &lt;br /&gt;  * @param mailStream&lt;br /&gt;  * @return a JavaMail Message&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  */&lt;br /&gt; public static Message createMimeMessageFromStream(byte[] mailStream) &lt;br /&gt;   throws MessagingException {&lt;br /&gt;  javax.mail.Session session = Session.getDefaultInstance(System.getProperties());&lt;br /&gt;  session.setDebug(true);&lt;br /&gt;  ByteArrayInputStream bais = new ByteArrayInputStream(mailStream);&lt;br /&gt;  Message msg = new MimeMessage(session, bais);&lt;br /&gt;  msg.saveChanges();&lt;br /&gt;  session.setDebug(debugSession);&lt;br /&gt;  return msg;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static void constructMultiPart(Multipart mp, BodypartBean aNode, int level)&lt;br /&gt;   throws MessagingException, IOException {&lt;br /&gt;  &lt;br /&gt;  if (isDebugEnabled) {&lt;br /&gt;   logger.debug("constructMultiPart() - MultipartHL - " + StringUtil.getPeriods(level)&lt;br /&gt;     + "Content Type: " + mp.getContentType());&lt;br /&gt;  }&lt;br /&gt;  List&amp;lt;BodypartBean&amp;gt; aNodes = aNode.getNodes();&lt;br /&gt;  for (int i = 0; aNodes != null &amp;amp;&amp;amp; i &amp;lt; aNodes.size(); i++) {&lt;br /&gt;   BodypartBean subNode = aNodes.get(i);&lt;br /&gt;   if (subNode.getMimeType().startsWith("multipart")) {&lt;br /&gt;    Multipart subMp = new MimeMultipart(subNode.getMimeSubType());&lt;br /&gt;    BodyPart multiBody = new MimeBodyPart();&lt;br /&gt;    multiBody.setContent(subMp);&lt;br /&gt;    mp.addBodyPart(multiBody);&lt;br /&gt;    constructMultiPart(subMp, subNode, level + 1);&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;    BodyPart bodyPart = new MimeBodyPart();&lt;br /&gt;    mp.addBodyPart(bodyPart);&lt;br /&gt;    constructSinglePart(bodyPart, subNode, level + 1);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private static final Set&amp;lt;String&amp;gt; reservedHeaders = new HashSet&amp;lt;String&amp;gt;();&lt;br /&gt; private static Set&amp;lt;String&amp;gt; getReservedHeaders() {&lt;br /&gt;  if (reservedHeaders.isEmpty()) {&lt;br /&gt;   reservedHeaders.add("Delivered-To");&lt;br /&gt;   reservedHeaders.add("Received");&lt;br /&gt;   reservedHeaders.add("Message-ID");&lt;br /&gt;   reservedHeaders.add("Subject");&lt;br /&gt;   reservedHeaders.add("Return-Path");&lt;br /&gt;   //reservedHeaders.add("User-Agent");&lt;br /&gt;  }&lt;br /&gt;  return reservedHeaders;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static void constructSinglePart(Part part, BodypartBean aNode, int level)&lt;br /&gt;   throws MessagingException, IOException {&lt;br /&gt;  // Set All Headers&lt;br /&gt;  List&amp;lt;MsgHeader&amp;gt; headers = aNode.getHeaders();&lt;br /&gt;  if (headers != null) {&lt;br /&gt;   for (int i = 0; i &amp;lt; headers.size(); i++) {&lt;br /&gt;    MsgHeader header = headers.get(i);&lt;br /&gt;    if (!getReservedHeaders().contains(header.getName())) {&lt;br /&gt;     part.setHeader(header.getName(), header.getValue());&lt;br /&gt;    }&lt;br /&gt;    if (isDebugEnabled) {&lt;br /&gt;     logger.debug("constructSinglePart() - Header Line - "&lt;br /&gt;       + StringUtil.getPeriods(level) + header.getName() + ": "&lt;br /&gt;       + header.getValue());&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  part.setDisposition(aNode.getDisposition());&lt;br /&gt;  part.setDescription(aNode.getDescription());&lt;br /&gt;&lt;br /&gt;  if (aNode.getMimeType().startsWith("text")) {&lt;br /&gt;   part.setContent(new String(aNode.getValue()), aNode.getContentType());&lt;br /&gt;   if (aNode.getMimeType().startsWith("text/html")) {&lt;br /&gt;    if (aNode.getDisposition() == null) {&lt;br /&gt;     // part.setDisposition(Part.INLINE);&lt;br /&gt;     /* &lt;br /&gt;      Do not uncomment above line, as some SMTP server will insert &lt;br /&gt;      "-----Inline Attachment Follows-----" at the beginning of the message.&lt;br /&gt;      */ &lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   if (aNode.getDescription() == null) {&lt;br /&gt;    // not sure why do this, consistency?&lt;br /&gt;    part.setDescription(getFileName(aNode.getContentType()));&lt;br /&gt;   }&lt;br /&gt;   ByteArrayDataSource bads = new ByteArrayDataSource(aNode.getValue(), aNode&lt;br /&gt;     .getContentType());&lt;br /&gt;   part.setDataHandler(new DataHandler(bads));&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static String getMsgPriority(String[] priority) {&lt;br /&gt;  String outPriority = "2 (Normal)";&lt;br /&gt;  if (priority != null &amp;amp;&amp;amp; priority[0] != null) {&lt;br /&gt;   String in_p = priority[0].trim();&lt;br /&gt;   if (in_p.equalsIgnoreCase("HIGH"))&lt;br /&gt;    outPriority = "1 (High)";&lt;br /&gt;   else if (in_p.equalsIgnoreCase("NORM"))&lt;br /&gt;    outPriority = "2 (Normal)";&lt;br /&gt;   else if (in_p.equalsIgnoreCase("LOW"))&lt;br /&gt;    outPriority = "3 (Low)";&lt;br /&gt;  }&lt;br /&gt;  return (outPriority);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt;&lt;br /&gt; private static List&amp;lt;String&amp;gt; getMessageBeanMethodNames() {&lt;br /&gt;  Method methods[] = MessageBean.class.getMethods();&lt;br /&gt;  List&amp;lt;String&amp;gt; methodNameList = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;  &lt;br /&gt;  for (int i = 0; i &amp;lt; methods.length; i++) {&lt;br /&gt;   Method method = (Method) methods[i];&lt;br /&gt;   Class&amp;lt;?&amp;gt; parmTypes[] = method.getParameterTypes();&lt;br /&gt;   int mod = method.getModifiers();&lt;br /&gt;   if (Modifier.isPublic(mod) &amp;amp;&amp;amp; !Modifier.isAbstract(mod) &amp;amp;&amp;amp; !Modifier.isStatic(mod)) {&lt;br /&gt;    if (method.getName().length() &amp;gt; 3 &amp;amp;&amp;amp; method.getName().startsWith("get")&lt;br /&gt;      &amp;amp;&amp;amp; parmTypes.length == 0) {&lt;br /&gt;     String name = method.getName().substring(3);&lt;br /&gt;     &lt;br /&gt;     if (method.getReturnType().getName().equals("java.lang.String")&lt;br /&gt;       || method.getReturnType().getName().equals("java.lang.Long")) {&lt;br /&gt;      &lt;br /&gt;      // ignore following methods&lt;br /&gt;      if ("BodyContentType".equals(name)) continue;&lt;br /&gt;      if (name.startsWith("Dsn")) continue;&lt;br /&gt;      if (name.startsWith("Des")) continue;&lt;br /&gt;      if (name.startsWith("Dia")) continue;&lt;br /&gt;      if (name.startsWith("Dis")) continue;&lt;br /&gt;      if (name.endsWith("AsString")) continue;&lt;br /&gt;      // end of ignore&lt;br /&gt;      &lt;br /&gt;      methodNameList.add(name);&lt;br /&gt;     }&lt;br /&gt;     else if (method.getReturnType().getCanonicalName().equals("javax.mail.Address[]")) {&lt;br /&gt;      methodNameList.add(name);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  Collections.sort(methodNameList);&lt;br /&gt;  return methodNameList;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private static String invokeMethod(MessageBean msgBean, String name) {&lt;br /&gt;  if (msgBean == null || name == null) {&lt;br /&gt;   logger.warn("invokeMethod() - Either msgBean or name is null.");&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  if (name.startsWith("Email_")) {&lt;br /&gt;   // strip off prefix&lt;br /&gt;   name = name.substring(6); &lt;br /&gt;  }&lt;br /&gt;  name = "get" + name;&lt;br /&gt;  &lt;br /&gt;  try {&lt;br /&gt;   if (isDebugEnabled)&lt;br /&gt;    logger.debug("invoking method: " + name + "()");&lt;br /&gt;   Method method = msgBean.getClass().getMethod(name, (Class[])null);&lt;br /&gt;   Object obj =  method.invoke(msgBean, (Object[])null);&lt;br /&gt;   if (obj instanceof String) {&lt;br /&gt;    return (String) obj;&lt;br /&gt;   }&lt;br /&gt;   else if (obj instanceof Long) {&lt;br /&gt;    return ((Long)obj).toString();&lt;br /&gt;   }&lt;br /&gt;   else if (obj instanceof Address[]) {&lt;br /&gt;    return StringUtil.addrToString((Address[])obj);&lt;br /&gt;   }&lt;br /&gt;   else if (obj != null) {&lt;br /&gt;    logger.warn("invokeMethod() - invalid return type: " + obj.getClass().getName());&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   logger.error("invokeMethod() - Exception caught", e);&lt;br /&gt;  }&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public static void main(String[] args) {&lt;br /&gt;  List&amp;lt;String&amp;gt; methodNameList = getMessageBeanMethodNames();&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  for (int i=0; i&amp;lt;methodNameList.size(); i++) {&lt;br /&gt;   sb.append(methodNameList.get(i) + LF);&lt;br /&gt;  }&lt;br /&gt;  System.out.println(sb.toString());&lt;br /&gt;  &lt;br /&gt;  MessageBean msgBean = new MessageBean();&lt;br /&gt;  msgBean.setSubject("test subject");&lt;br /&gt;  msgBean.setBody("test body text");&lt;br /&gt;  System.out.println("Invoke getBody(): " + invokeMethod(msgBean, "Body"));&lt;br /&gt;  System.out.println("Invoke getSubject(): " + invokeMethod(msgBean, "Subject"));&lt;br /&gt;  try {&lt;br /&gt;   Message msg = beanToMime(msgBean);&lt;br /&gt;   MessageBean bean = mimeToBean(msg);&lt;br /&gt;   System.out.println("########## MessageBean Before:");&lt;br /&gt;   System.out.println(msgBean);&lt;br /&gt;   System.out.println("########## MessageBean After:");&lt;br /&gt;   System.out.println(bean);&lt;br /&gt;  }&lt;br /&gt;  catch (Exception e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-2832114107692107198?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/2832114107692107198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-4.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2832114107692107198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/2832114107692107198'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-4.html' title='Portable java mail message bean - Part 4'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-3823386453698084743</id><published>2009-09-03T09:25:00.000-04:00</published><updated>2009-09-04T09:17:50.833-04:00</updated><title type='text'>Portable java mail message bean - Part 3</title><content type='html'>&lt;div id="wikicontent" style="padding: 0pt 3em 1.2em 0pt;"&gt;In Part 1 and Part 2 we defined body part bean class and message bean class. Two more classes need to be defined. One is message node class that represent a body part, another is the direct replacement of java mail message header class that implements the Serializable interface.&lt;br /&gt;&lt;br /&gt;1) Message Node class:&lt;br /&gt;&lt;pre&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;/**&lt;br /&gt;* define an extended BodypartBean class to store additional data&lt;br /&gt;*/&lt;br /&gt;public final class MessageNode implements Serializable {&lt;br /&gt;private static final long serialVersionUID = 750866110886999439L;&lt;br /&gt;&lt;br /&gt;BodypartBean aNode = null;&lt;br /&gt;&lt;br /&gt;int level = 0;&lt;br /&gt;&lt;br /&gt;public MessageNode(BodypartBean aNode, int level) {&lt;br /&gt;this.aNode = aNode;&lt;br /&gt;this.level = level;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public BodypartBean getBodypartNode() {&lt;br /&gt;return aNode;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public int getLevel() {&lt;br /&gt;return level;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;2) Message header class:&lt;br /&gt;&lt;pre&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* A portable Message Header.&lt;br /&gt;*&lt;br /&gt;* @author JackW&lt;/pre&gt;&lt;pre&gt;*/&lt;br /&gt;public final class MsgHeader implements java.io.Serializable {&lt;br /&gt;private static final long serialVersionUID = -5722833299002367057L;&lt;br /&gt;private String name;&lt;br /&gt;private String value;&lt;br /&gt;&lt;br /&gt;public String getName() {&lt;br /&gt;return name;&lt;br /&gt;}&lt;br /&gt;public void setName(String name) {&lt;br /&gt;this.name = name;&lt;br /&gt;}&lt;br /&gt;public String getValue() {&lt;br /&gt;return value;&lt;br /&gt;}&lt;br /&gt;public void setValue(String value) {&lt;br /&gt;this.value = value;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Stay tuned for the utility class that converts java mail message to the portable message, or vise verse.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-3823386453698084743?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/3823386453698084743/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3823386453698084743'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/3823386453698084743'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-3.html' title='Portable java mail message bean - Part 3'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-370290214928925866</id><published>2009-09-03T09:13:00.001-04:00</published><updated>2009-09-03T14:27:50.799-04:00</updated><title type='text'>Portable java mail message bean - Part 2</title><content type='html'>&lt;div id="wikicontent" style="padding: 0pt 3em 1.2em 0pt;"&gt;In Part 1 we defined the body part bean class, now let's define the message bean class. This class extends BodypartBean class and implements the rest of the mime message properties, mostly mime header fields. This class can be considered as the replacement of java mail Message class.&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/MessageBean.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;/pre&gt;&lt;pre&gt;* &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;import javax.mail.Address;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * A portable class that holds properties of an email mine message.&lt;br /&gt; * &lt;br /&gt; * @author JackW&lt;/pre&gt;&lt;pre&gt;*/&lt;br /&gt;public final class MessageBean extends BodypartBean implements java.io.Serializable {&lt;br /&gt; private static final long serialVersionUID = -7651754840464120630L;&lt;br /&gt;&lt;br /&gt; static final Logger logger = Logger.getLogger(MessageBean.class);&lt;br /&gt;&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; private Address[] from, to, cc, bcc, replyto, forward;&lt;br /&gt;&lt;br /&gt; private Address[] toEnvelope;&lt;br /&gt;&lt;br /&gt; private String returnPath;&lt;br /&gt;&lt;br /&gt; private String[] xmailer, priority;&lt;br /&gt;&lt;br /&gt; private String smtpMessageId;&lt;br /&gt;&lt;br /&gt; private String subject;&lt;br /&gt;&lt;br /&gt; private java.util.Date sentDate;&lt;br /&gt;&lt;br /&gt; private int attachCount = 0;&lt;br /&gt;&lt;br /&gt; // to be set by BodypartUtil.retrieveAttachments()&lt;br /&gt; private MessageNode rfc822, report;&lt;br /&gt;&lt;br /&gt; private List&amp;lt;MessageNode&amp;gt; attachments; // list of MessageNode&lt;br /&gt; // end&lt;br /&gt;&lt;br /&gt; // to be set by MessageParser.parse()&lt;br /&gt; private String origRcpt, finalRcpt, dsnAction, dsnStatus;&lt;br /&gt;&lt;br /&gt; private String origSubject, diagnosticCode, rfcMessageId;&lt;br /&gt;&lt;br /&gt; private String dsnRfc822, dsnText, dsnDlvrStat;&lt;br /&gt; // end&lt;br /&gt;&lt;br /&gt; // properties about the mailbox&lt;br /&gt; private String mailboxHost, mailboxUser;&lt;br /&gt;&lt;br /&gt; // for incoming only, stores sizes of each attachment.&lt;br /&gt; private final List&amp;lt;Integer&amp;gt; componentsSize;&lt;br /&gt;&lt;br /&gt; // name used to store message body to the hashMap&lt;br /&gt; public static final String MSG_BODY_TEXT = "msg_body_text";&lt;br /&gt;&lt;br /&gt; final static String LF = System.getProperty("line.separator", "\n");&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * default constructor&lt;br /&gt;  */&lt;br /&gt; public MessageBean() {&lt;br /&gt;  componentsSize = new ArrayList&amp;lt;Integer&amp;gt;();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private String addrToString(Address[] addr) {&lt;br /&gt;  if (addr == null || addr.length == 0)&lt;br /&gt;   return null;&lt;br /&gt;&lt;br /&gt;  String str = addr[0].toString();&lt;br /&gt;  for (int i = 1; i &amp;lt; addr.length; i++) {&lt;br /&gt;   str = str + "," + addr[i].toString();&lt;br /&gt;  }&lt;br /&gt;  return str;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /* getters and setters start from here */&lt;br /&gt; /**&lt;br /&gt;  * @return from address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getFrom() {&lt;br /&gt;  return this.from;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return from address as a string&lt;br /&gt;  */&lt;br /&gt; public String getFromAsString() {&lt;br /&gt;  return addrToString(this.from);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return to address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getTo() {&lt;br /&gt;  return this.to;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return to address as a string&lt;br /&gt;  */&lt;br /&gt; public String getToAsString() {&lt;br /&gt;  return addrToString(this.to);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return cc address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getCc() {&lt;br /&gt;  return this.cc;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return cc address as a string&lt;br /&gt;  */&lt;br /&gt; public String getCcAsString() {&lt;br /&gt;  return addrToString(this.cc);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return bcc address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getBcc() {&lt;br /&gt;  return this.bcc;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return bcc address as a string&lt;br /&gt;  */&lt;br /&gt; public String getBccAsString() {&lt;br /&gt;  return addrToString(this.bcc);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return replyto address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getReplyto() {&lt;br /&gt;  return this.replyto;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return replyto address as a string&lt;br /&gt;  */&lt;br /&gt; public String getReplytoAsString() {&lt;br /&gt;  return addrToString(this.replyto);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return forward address as an Address arrayNode&lt;br /&gt;  */&lt;br /&gt; public Address[] getForward() {&lt;br /&gt;  return this.forward;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return forward address as a string&lt;br /&gt;  */&lt;br /&gt; public String getForwardAsString() {&lt;br /&gt;  return addrToString(this.forward);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getReturnPath() {&lt;br /&gt;  return returnPath;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public Address[] getToEnvelope() {&lt;br /&gt;  return this.toEnvelope;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; String getToEnvelopeAsString() {&lt;br /&gt;  return addrToString(this.toEnvelope);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return x_mailer as a string arrayNode&lt;br /&gt;  */&lt;br /&gt; public String[] getXmailer() {&lt;br /&gt;  return this.xmailer;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return x-priority as a string arrayNode&lt;br /&gt;  */&lt;br /&gt; public String[] getPriority() {&lt;br /&gt;  return this.priority;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return SMTP message id as a string&lt;br /&gt;  */&lt;br /&gt; public String getSmtpMessageId() {&lt;br /&gt;  return this.smtpMessageId;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return subject as a string&lt;br /&gt;  */&lt;br /&gt; public String getSubject() {&lt;br /&gt;  return subject;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return send DateField&lt;br /&gt;  */&lt;br /&gt; public java.util.Date getSentDate() {&lt;br /&gt;  return sentDate;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Get Body Content Type&lt;br /&gt;  * &lt;br /&gt;  * @return content type of the body&lt;br /&gt;  */&lt;br /&gt; public String getBodyContentType() {&lt;br /&gt;  String type = getBodyContentType(0);&lt;br /&gt;  if (type == null || type.trim().length() == 0) {&lt;br /&gt;   type = DEFAULT_CONTENT_TYPE;&lt;br /&gt;  }&lt;br /&gt;  return type;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * get email message body.&lt;br /&gt;  * &lt;br /&gt;  * @return the email body&lt;br /&gt;  */&lt;br /&gt; public String getBody() {&lt;br /&gt;  String msgBody = getBody(0);&lt;br /&gt;&lt;br /&gt;  return msgBody;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * get a BodypartBean that holds message body data&lt;br /&gt;  * &lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; public BodypartBean getBodyNode() {&lt;br /&gt;  return getBodyNode(0);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return the number of attachments.&lt;br /&gt;  */&lt;br /&gt; public int getAttachCount() {&lt;br /&gt;  return attachCount;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return the mailbox host name&lt;br /&gt;  */&lt;br /&gt; public String getMailboxHost() {&lt;br /&gt;  return mailboxHost;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return the mailbox user id&lt;br /&gt;  */&lt;br /&gt; public String getMailboxUser() {&lt;br /&gt;  return mailboxUser;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return rfc822&lt;br /&gt;  */&lt;br /&gt; public MessageNode getRfc822() {&lt;br /&gt;  return rfc822;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return report&lt;br /&gt;  */&lt;br /&gt; public MessageNode getReport() {&lt;br /&gt;  return report;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return attachments&lt;br /&gt;  */&lt;br /&gt; public List&amp;lt;MessageNode&amp;gt; getAttachments() {&lt;br /&gt;  return attachments;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getRfcMessageId() {&lt;br /&gt;  return rfcMessageId;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public List&amp;lt;Integer&amp;gt; getComponentsSize() {&lt;br /&gt;  return componentsSize;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /* all setters start from here */&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set from address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param from&lt;br /&gt;  */&lt;br /&gt; public void setFrom(Address[] from) {&lt;br /&gt;  this.from = from;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set to address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param to&lt;br /&gt;  */&lt;br /&gt; public void setTo(Address[] to) {&lt;br /&gt;  this.to = to;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set cc address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param cc&lt;br /&gt;  */&lt;br /&gt; public void setCc(Address[] cc) {&lt;br /&gt;  this.cc = cc;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set bcc address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param bcc&lt;br /&gt;  */&lt;br /&gt; public void setBcc(Address[] bcc) {&lt;br /&gt;  this.bcc = bcc;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set replyto address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param replyto&lt;br /&gt;  */&lt;br /&gt; public void setReplyto(Address[] replyto) {&lt;br /&gt;  this.replyto = replyto;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set forward address from an Address arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param forward&lt;br /&gt;  */&lt;br /&gt; public void setForward(Address[] forward) {&lt;br /&gt;  this.forward = forward;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setReturnPath(String returnPath) {&lt;br /&gt;  this.returnPath = returnPath;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set x-mailer from a string arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param xmailer&lt;br /&gt;  */&lt;br /&gt; public void setXmailer(String[] xmailer) {&lt;br /&gt;  this.xmailer = xmailer;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set x-priority from a string arrayNode&lt;br /&gt;  * &lt;br /&gt;  * @param priority&lt;br /&gt;  */&lt;br /&gt; public void setPriority(String[] priority) {&lt;br /&gt;  this.priority = priority;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set SMTP message id&lt;br /&gt;  * &lt;br /&gt;  * @param smtpMessageId&lt;br /&gt;  */&lt;br /&gt; public void setSmtpMessageId(String smtpMessageId) {&lt;br /&gt;  this.smtpMessageId = smtpMessageId;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set subject from a string&lt;br /&gt;  * &lt;br /&gt;  * @param subject&lt;br /&gt;  */&lt;br /&gt; public void setSubject(String subject) {&lt;br /&gt;  this.subject = subject;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set send DateField&lt;br /&gt;  * &lt;br /&gt;  * @param DateField&lt;br /&gt;  */&lt;br /&gt; public void setSentDate(java.util.Date date) {&lt;br /&gt;  this.sentDate = date;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set mailbox host&lt;br /&gt;  * &lt;br /&gt;  * @param mailboxHost&lt;br /&gt;  */&lt;br /&gt; public void setMailboxHost(String mailboxHost) {&lt;br /&gt;  this.mailboxHost = mailboxHost;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set mailbox user id&lt;br /&gt;  * &lt;br /&gt;  * @param mailboxUser&lt;br /&gt;  */&lt;br /&gt; public void setMailboxUser(String mailboxUser) {&lt;br /&gt;  this.mailboxUser = mailboxUser;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set rfc822&lt;br /&gt;  * &lt;br /&gt;  * @param rfc822&lt;br /&gt;  */&lt;br /&gt; public void setRfc822(MessageNode rfc822) {&lt;br /&gt;  this.rfc822 = rfc822;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set report&lt;br /&gt;  * &lt;br /&gt;  * @param report&lt;br /&gt;  */&lt;br /&gt; public void setReport(MessageNode report) {&lt;br /&gt;  this.report = report;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set attachments&lt;br /&gt;  * &lt;br /&gt;  * @param attachments&lt;br /&gt;  */&lt;br /&gt; public void setAttachments(List&amp;lt;MessageNode&amp;gt; attachments) {&lt;br /&gt;  this.attachments = attachments;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set Message Body, a convenient method, not recommended in production code.&lt;br /&gt;  * &lt;br /&gt;  * @param body&lt;br /&gt;  */&lt;br /&gt; public void setBody(String body) {&lt;br /&gt;  setValue(body);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDiagnosticCode() {&lt;br /&gt;  return diagnosticCode;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDiagnosticCode(String diagnosticCode) {&lt;br /&gt;  this.diagnosticCode = diagnosticCode;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDsnAction() {&lt;br /&gt;  return dsnAction;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDsnAction(String dsnAction) {&lt;br /&gt;  this.dsnAction = dsnAction;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDsnDlvrStat() {&lt;br /&gt;  return dsnDlvrStat;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDsnDlvrStat(String dsnDlvrStat) {&lt;br /&gt;  this.dsnDlvrStat = dsnDlvrStat;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDsnRfc822() {&lt;br /&gt;  return dsnRfc822;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDsnRfc822(String dsnRfc822) {&lt;br /&gt;  this.dsnRfc822 = dsnRfc822;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDsnStatus() {&lt;br /&gt;  return dsnStatus;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDsnStatus(String dsnStatus) {&lt;br /&gt;  this.dsnStatus = dsnStatus;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getDsnText() {&lt;br /&gt;  return dsnText;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setDsnText(String dsnText) {&lt;br /&gt;  this.dsnText = dsnText;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getFinalRcpt() {&lt;br /&gt;  return finalRcpt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setFinalRcpt(String finalRcpt) {&lt;br /&gt;  this.finalRcpt = finalRcpt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getOrigRcpt() {&lt;br /&gt;  return origRcpt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setOrigRcpt(String origRcpt) {&lt;br /&gt;  this.origRcpt = origRcpt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getOrigSubject() {&lt;br /&gt;  return origSubject;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setOrigSubject(String origSubject) {&lt;br /&gt;  this.origSubject = origSubject;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setToEnvelope(Address[] toEnvelope) {&lt;br /&gt;  this.toEnvelope = toEnvelope;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setRfcMessageId(String dsnMessageId) {&lt;br /&gt;  this.rfcMessageId = dsnMessageId;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * update the counter of attachments&lt;br /&gt;  * &lt;br /&gt;  * @param attachCount&lt;br /&gt;  */&lt;br /&gt; public void updateAttachCount(int attachCount) {&lt;br /&gt;  this.attachCount = this.attachCount + attachCount;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * clear up all parameters&lt;br /&gt;  */&lt;br /&gt; public void clearParameters() {&lt;br /&gt;  super.clearParameters();&lt;br /&gt;  from = null;&lt;br /&gt;  to = null;&lt;br /&gt;  cc = null;&lt;br /&gt;  bcc = null;&lt;br /&gt;  replyto = null;&lt;br /&gt;  forward = null;&lt;br /&gt;  returnPath = null;&lt;br /&gt;  toEnvelope = null;&lt;br /&gt;  xmailer = null;&lt;br /&gt;  priority = null;&lt;br /&gt;  smtpMessageId = null;&lt;br /&gt;  subject = null;&lt;br /&gt;&lt;br /&gt;  sentDate = null;&lt;br /&gt;  synchronized (this) {&lt;br /&gt;   attachCount = 0;&lt;br /&gt;  }&lt;br /&gt;  mailboxHost = null;&lt;br /&gt;  mailboxUser = null;&lt;br /&gt;&lt;br /&gt;  rfc822 = null;&lt;br /&gt;  report = null;&lt;br /&gt;  attachments = null;&lt;br /&gt;  componentsSize.clear();&lt;br /&gt;&lt;br /&gt;  origRcpt = null;&lt;br /&gt;  finalRcpt = null;&lt;br /&gt;  dsnAction = null;&lt;br /&gt;  dsnStatus = null;&lt;br /&gt;  origSubject = null;&lt;br /&gt;  diagnosticCode = null;&lt;br /&gt;  dsnText = null;&lt;br /&gt;  dsnRfc822 = null;&lt;br /&gt;  dsnDlvrStat = null;&lt;br /&gt;  rfcMessageId = null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * format the message to a string for printing.&lt;br /&gt;  * &lt;br /&gt;  * @return string&lt;br /&gt;  */&lt;br /&gt; public String toString() {&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  if (from != null) {&lt;br /&gt;   sb.append("From: " + getFromAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (to != null) {&lt;br /&gt;   sb.append("To: " + getToAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (toEnvelope != null) {&lt;br /&gt;   sb.append("To from Envelope: " + getToEnvelopeAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (cc != null) {&lt;br /&gt;   sb.append("Cc: " + getCcAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (bcc != null) {&lt;br /&gt;   sb.append("Bcc: " + getBccAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (replyto != null) {&lt;br /&gt;   sb.append("Replyto: " + getReplytoAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (forward != null) {&lt;br /&gt;   sb.append("Forward: " + getForwardAsString() + LF);&lt;br /&gt;  }&lt;br /&gt;  if (returnPath != null) {&lt;br /&gt;   sb.append("Return-Path: " + returnPath + LF);&lt;br /&gt;  }&lt;br /&gt;  if (xmailer != null &amp;amp;&amp;amp; xmailer.length &amp;gt; 0) { &lt;br /&gt;   for (int i = 0; i &amp;lt; xmailer.length; i++) {&lt;br /&gt;    sb.append("X-Mailer: " + xmailer[i] + LF);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (priority != null &amp;amp;&amp;amp; priority.length &amp;gt; 0) {&lt;br /&gt;   for (int i = 0; i &amp;lt; priority.length; i++) {&lt;br /&gt;    sb.append("Priority: " + priority[i] + LF);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (smtpMessageId != null) {&lt;br /&gt;   sb.append("SMTP Message Id: " + smtpMessageId + LF);&lt;br /&gt;  }&lt;br /&gt;  sb.append("Subject: " + subject + LF);&lt;br /&gt;  if (sentDate != null) {&lt;br /&gt;   sb.append("Sent Date: " + sentDate + LF);&lt;br /&gt;  }&lt;br /&gt;  if (mailboxHost != null) {&lt;br /&gt;   sb.append("MailBox Host: " + mailboxHost + LF);&lt;br /&gt;  }&lt;br /&gt;  if (mailboxUser != null) {&lt;br /&gt;   sb.append("Mailbox User: " + mailboxUser + LF);&lt;br /&gt;  }&lt;br /&gt;  if (finalRcpt != null) {&lt;br /&gt;   sb.append("Final Recipient: " + finalRcpt + LF);&lt;br /&gt;  }&lt;br /&gt;  if (origRcpt != null) {&lt;br /&gt;   sb.append("Original Recipient: " + origRcpt + LF);&lt;br /&gt;  }&lt;br /&gt;  if (dsnAction != null) {&lt;br /&gt;   sb.append("DSN Action: " + dsnAction + LF);&lt;br /&gt;  }&lt;br /&gt;  if (dsnStatus != null) {&lt;br /&gt;   sb.append("DSN Status: " + dsnStatus + LF);&lt;br /&gt;  }&lt;br /&gt;  if (diagnosticCode != null) {&lt;br /&gt;   sb.append("Diagnostic Code: " + diagnosticCode + LF);&lt;br /&gt;  }&lt;br /&gt;  if (origSubject != null) {&lt;br /&gt;   sb.append("Original Subject: " + origSubject + LF);&lt;br /&gt;  }&lt;br /&gt;  if (rfcMessageId != null) {&lt;br /&gt;   sb.append("RFC MessageId: " + rfcMessageId + LF);&lt;br /&gt;  }&lt;br /&gt;  if (dsnText != null) {&lt;br /&gt;   sb.append(LF + "List DSN Text: " + LF + dsnText + LF);&lt;br /&gt;  }&lt;br /&gt;  if (dsnRfc822 != null) {&lt;br /&gt;   sb.append(LF + "List DSN RFC822: " + LF + dsnRfc822 + LF);&lt;br /&gt;  }&lt;br /&gt;  if (dsnDlvrStat != null) {&lt;br /&gt;   sb.append(LF + "List DSN Delivery Status: " + LF + dsnDlvrStat + LF);&lt;br /&gt;  }&lt;br /&gt;  if (headers.size() &amp;gt; 0) {&lt;br /&gt;   sb.append(LF + "List Header Lines:" + LF);&lt;br /&gt;  }&lt;br /&gt;  for (Iterator&amp;lt;MsgHeader&amp;gt; it=headers.iterator(); it.hasNext();) {&lt;br /&gt;   MsgHeader hdr = it.next();&lt;br /&gt;   sb.append("Header Line - " + hdr.getName() + ": " + hdr.getValue() + LF);&lt;br /&gt;  }&lt;br /&gt;  sb.append(LF + "List Body Parts:" + LF);&lt;br /&gt;  sb.append(super.toString(0));&lt;br /&gt;  if (attachments != null &amp;amp;&amp;amp; attachments.size() &amp;gt; 0) {&lt;br /&gt;   sb.append(LF + "List Attachments:" + LF);&lt;br /&gt;   for (Iterator&amp;lt;MessageNode&amp;gt; it=attachments.iterator(); it.hasNext();) {&lt;br /&gt;    MessageNode node = it.next();&lt;br /&gt;    BodypartBean anode = node.getBodypartNode();&lt;br /&gt;    sb.append(anode.toString(node.getLevel()));&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-370290214928925866?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/370290214928925866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-1.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/370290214928925866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/370290214928925866'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean-part-1.html' title='Portable java mail message bean - Part 2'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3874086622685697943.post-1014432669700698694</id><published>2009-09-02T15:15:00.000-04:00</published><updated>2009-09-04T09:17:50.833-04:00</updated><title type='text'>Portable java mail message bean - Part 1</title><content type='html'>&lt;div id="wikicontent" style="padding: 0pt 3em 1.2em 0pt;"&gt;The light weight java mail message classes are not serializable and thus it is impossible to save a java mail mime message instance directly to a persistent media such as a file system or a database, or send it across a wire, such as to a JMS queue. &lt;br /&gt;Presented here is an attempt to implement a portable message bean, made of four serializable classes, that maintains the same properties and structures of the java mail mime message, but makes it possible to save the email mime message to a media or pass it through a wire.&lt;br /&gt;I will provide a utility class later that can be used to convert a java mail mime message instance to a portable message bean instance.&lt;br /&gt;&lt;br /&gt;First let's define the body part bean class which is a kind of a replacement of java mail's Bodypart class:&lt;br /&gt;&lt;pre&gt;/*&lt;br /&gt; * blog/javaclue/javamail/BodypartBean.java&lt;br /&gt; * &lt;br /&gt; * Copyright (C) 2009 JackW&lt;/pre&gt;&lt;pre&gt;* &lt;br /&gt; * This program is free software: you can redistribute it and/or modify it under the terms of the&lt;br /&gt; * GNU Lesser General Public License as published by the Free Software Foundation, either version 3&lt;br /&gt; * of the License, or (at your option) any later version.&lt;br /&gt; * &lt;br /&gt; * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without&lt;br /&gt; * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU&lt;br /&gt; * Lesser General Public License for more details.&lt;br /&gt; * &lt;br /&gt; * You should have received a copy of the GNU Lesser General Public License along with this library.&lt;br /&gt; * If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;package blog.javaclue.javamail;&lt;br /&gt;&lt;br /&gt;import java.io.BufferedInputStream;&lt;br /&gt;import java.io.ByteArrayOutputStream;&lt;br /&gt;import java.io.DataInputStream;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStream;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;import java.io.UnsupportedEncodingException;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Enumeration;&lt;br /&gt;import java.util.Iterator;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.StringTokenizer;&lt;br /&gt;&lt;br /&gt;import javax.mail.Header;&lt;br /&gt;import javax.mail.MessagingException;&lt;br /&gt;import javax.mail.Part;&lt;br /&gt;&lt;br /&gt;import org.apache.log4j.Logger;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Represents a portable message body part structure.&lt;br /&gt; * &lt;br /&gt; * The construction unit of email body parts. Consists of a group of fields and&lt;br /&gt; * a list of links that points to its child body parts if any, accessible by&lt;br /&gt; * name and Iterator.&lt;br /&gt; * &lt;br /&gt; * @author JackW&lt;/pre&gt;&lt;pre&gt;*/&lt;br /&gt;public class BodypartBean implements Serializable {&lt;br /&gt; private static final long serialVersionUID = -6689047851876614015L;&lt;br /&gt;&lt;br /&gt; static final Logger logger = Logger.getLogger(BodypartBean.class);&lt;br /&gt;&lt;br /&gt; static final boolean isDebugEnabled = logger.isDebugEnabled();&lt;br /&gt;&lt;br /&gt; protected static final String DEFAULT_CONTENT_TYPE = "text/plain";&lt;br /&gt;&lt;br /&gt; protected final List&amp;lt;BodypartBean&amp;gt; attachParts = new ArrayList&amp;lt;BodypartBean&amp;gt;();&lt;br /&gt;&lt;br /&gt; protected final List&amp;lt;MsgHeader&amp;gt; headers = new ArrayList&amp;lt;MsgHeader&amp;gt;();&lt;br /&gt;&lt;br /&gt; protected int size = 0;&lt;br /&gt;&lt;br /&gt; protected String disposition = null, description = null;&lt;br /&gt;&lt;br /&gt; protected String fileName = null;&lt;br /&gt;&lt;br /&gt; protected byte[] value = null;&lt;br /&gt;&lt;br /&gt; protected String contentType = DEFAULT_CONTENT_TYPE;&lt;br /&gt;&lt;br /&gt; protected final static String LF = System.getProperty("line.separator", "\n");&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Constructs a body part.&lt;br /&gt;  */&lt;br /&gt; public BodypartBean() {&lt;br /&gt;  this(DEFAULT_CONTENT_TYPE, null);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Constructs a body part of specified mime type.&lt;br /&gt;  * &lt;br /&gt;  * @param contectType -&lt;br /&gt;  *            mime type&lt;br /&gt;  */&lt;br /&gt; public BodypartBean(String contentType) {&lt;br /&gt;  this(contentType, null);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Constructs a body part of specified mime type and value.&lt;br /&gt;  * &lt;br /&gt;  * @param contectType -&lt;br /&gt;  *            mime type&lt;br /&gt;  * @param value -&lt;br /&gt;  *            body part node value&lt;br /&gt;  */&lt;br /&gt; public BodypartBean(String contentType, Object value) {&lt;br /&gt;  if (contentType == null)&lt;br /&gt;   this.contentType = DEFAULT_CONTENT_TYPE;&lt;br /&gt;  else&lt;br /&gt;   this.contentType = contentType;&lt;br /&gt;  setValue(value);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * add a child body part&lt;br /&gt;  * &lt;br /&gt;  * @param subNode -&lt;br /&gt;  *            part to be added as a child&lt;br /&gt;  */&lt;br /&gt; public void put(BodypartBean subNode) {&lt;br /&gt;  attachParts.add(subNode);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Return an iterator of its child body parts&lt;br /&gt;  * &lt;br /&gt;  * @return an iterator of this object&lt;br /&gt;  */&lt;br /&gt; public Iterator&amp;lt;BodypartBean&amp;gt; getIterator() {&lt;br /&gt;  return attachParts.iterator();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Return the Content type of this body part&lt;br /&gt;  * &lt;br /&gt;  * @return content type&lt;br /&gt;  */&lt;br /&gt; public String getContentType() {&lt;br /&gt;  return contentType;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Return the mime type of this body part, all characters are converted to&lt;br /&gt;  * lower case.&lt;br /&gt;  * &lt;br /&gt;  * @return mime type, the first part of content type&lt;br /&gt;  */&lt;br /&gt; public String getMimeType() {&lt;br /&gt;  String str = "unknown";&lt;br /&gt;  StringTokenizer st = new StringTokenizer(contentType.trim(), ";");&lt;br /&gt;  if (st.hasMoreTokens()) {&lt;br /&gt;   str = st.nextToken();&lt;br /&gt;   // str should look like:&lt;br /&gt;   // text/plain, text/html, multipart/mixed, multipart/alternative,&lt;br /&gt;   // multipart/related, or application/octec-stream, etc.&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return str.toLowerCase(); // iPlanet uses Upper case for mime type&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Return the mime sub-type of this body part&lt;br /&gt;  * &lt;br /&gt;  * @return mime subtype&lt;br /&gt;  */&lt;br /&gt; public String getMimeSubType() {&lt;br /&gt;  String mimeType = getMimeType();&lt;br /&gt;  int pos = mimeType.indexOf("/");&lt;br /&gt;  if (pos &amp;gt; 0 &amp;amp;&amp;amp; pos &amp;lt; mimeType.length()) {&lt;br /&gt;   return mimeType.substring(pos + 1);&lt;br /&gt;  }&lt;br /&gt;  else {&lt;br /&gt;   return "";&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * &lt;br /&gt;  * Typical message structures&lt;br /&gt;  * &lt;br /&gt;  * level mime type disposition description body ----- ------------------&lt;br /&gt;  * ----------- ----------- ----&lt;br /&gt;  * &lt;br /&gt;  * 1) plain text email 0 text/plain null null Yes&lt;br /&gt;  * &lt;br /&gt;  * 2) plain text email with attachments 0 multipart/mixed null null 1&lt;br /&gt;  * .text/plain inline null Yes 1 .text/html inline filename 1 .image/gif&lt;br /&gt;  * inline filename 1 .application/msword attachment filename&lt;br /&gt;  * &lt;br /&gt;  * 3) html text email 0 multipart/ alternative null null 1 .text/plain null&lt;br /&gt;  * null Yes 1 .text/html null null Yes&lt;br /&gt;  * &lt;br /&gt;  * 4) html text email with attachments 0 multipart/mixed null null 1&lt;br /&gt;  * .multipart/ alternative null null 2 ..text/plain null null Yes 2&lt;br /&gt;  * ..text/html null null Yes 1 .text/html inline filename 1 .image/gif&lt;br /&gt;  * inline filename&lt;br /&gt;  * &lt;br /&gt;  * 5) html text email with inline image 0 multipart/ alternative null null 1&lt;br /&gt;  * .text/plain null null Yes 1 .multipart/related null null 2 ..text/html&lt;br /&gt;  * inline null Yes 2 ..image/gif inline null Yes&lt;br /&gt;  * &lt;br /&gt;  * 6) bounced email 0 multipart/report null null 1 .text/plain null null Yes&lt;br /&gt;  * 1 .message/ delivery-status null null error status 1 .message/rfc822 null&lt;br /&gt;  * null 2 ..text/plain null null error message&lt;br /&gt;  * &lt;br /&gt;  */&lt;br /&gt; /**&lt;br /&gt;  * return the message body text. The initial call must be originated from&lt;br /&gt;  * MessageBean.&lt;br /&gt;  * &lt;br /&gt;  * @param level -&lt;br /&gt;  *            structure tree level&lt;br /&gt;  * @return body text&lt;br /&gt;  */&lt;br /&gt; String getBody(int level) {&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  String label = LF + "content-type: ";&lt;br /&gt;  boolean showInlineContentType = false;&lt;br /&gt;  String this_mtype = this.getMimeType();&lt;br /&gt;  if (this_mtype.startsWith("text")) {&lt;br /&gt;   // exclude attachment body parts&lt;br /&gt;   if (!Part.ATTACHMENT.equals(getDisposition())) {&lt;br /&gt;    if (level &amp;gt; 0 &amp;amp;&amp;amp; !this_mtype.startsWith("text/html")) {&lt;br /&gt;     if (showInlineContentType)&lt;br /&gt;      sb.append(label + this.getContentType() + LF + LF);&lt;br /&gt;    }&lt;br /&gt;    if (getValue() != null) {&lt;br /&gt;     sb.append(new String(getValue()));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart/alternative")) {&lt;br /&gt;   // alternative sub type, get the last text alternative&lt;br /&gt;   String content_type = "text/plain";&lt;br /&gt;   byte[] textBody = null;&lt;br /&gt;   // get the last text alternative (order: text/plain -&amp;gt; text/html)&lt;br /&gt;   for (BodypartBean subNode : getNodes()) {&lt;br /&gt;    if (subNode.getMimeType().startsWith("text")) {&lt;br /&gt;     content_type = subNode.getContentType();&lt;br /&gt;     textBody = subNode.getValue();&lt;br /&gt;    }&lt;br /&gt;    else if (subNode.getMimeType().startsWith("multipart/related")) {&lt;br /&gt;     String bodyStr = subNode.getBody(level + 1);&lt;br /&gt;     content_type = subNode.getBodyContentType(level + 1);&lt;br /&gt;     if (content_type != null &amp;amp;&amp;amp; content_type.startsWith("text")) {&lt;br /&gt;      textBody = bodyStr.getBytes();&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   } // end of for loop&lt;br /&gt;   if (textBody != null) {&lt;br /&gt;    String txt = new String(textBody);&lt;br /&gt;    if (level &amp;gt; 0 &amp;amp;&amp;amp; !content_type.startsWith("text/html")) {&lt;br /&gt;     if (showInlineContentType)&lt;br /&gt;      sb.append(label + content_type + LF + LF);&lt;br /&gt;    }&lt;br /&gt;    sb.append(txt);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart")) {&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean subNode = it.next();&lt;br /&gt;    String strBody = subNode.getBody(level + 1);&lt;br /&gt;    sb.append(strBody);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("application")) {&lt;br /&gt;   // application mime type, ignore&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("message")) {&lt;br /&gt;   // message mime type, merge its text contents into the body.&lt;br /&gt;   if (this.getHeaders() != null) {&lt;br /&gt;    // include header data into the body&lt;br /&gt;    for (int i = 0; i &amp;lt; this.getHeaders().size(); i++) {&lt;br /&gt;     MsgHeader header = this.getHeaders().get(i);&lt;br /&gt;     sb.append(header.getName() + ": " + header.getValue() + LF);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   // if it contains body text, include it into the body&lt;br /&gt;   byte[] textBody = (byte[]) getValue();&lt;br /&gt;   if (textBody != null) {&lt;br /&gt;    String txt = new String(textBody);&lt;br /&gt;    if (txt.trim().length() &amp;gt; 0) { // contains text&lt;br /&gt;     if (showInlineContentType) {&lt;br /&gt;      sb.append(label + this.getContentType() + LF + LF);&lt;br /&gt;     }&lt;br /&gt;     sb.append(new String(textBody));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   // if there are body parts, get them first (rfc822 sub type contains&lt;br /&gt;   // a body part)&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean subNode = it.next();&lt;br /&gt;    String strBody = subNode.getBody(level + 1);&lt;br /&gt;    if (strBody.trim().length() &amp;gt; 0) {&lt;br /&gt;     if (showInlineContentType) {&lt;br /&gt;      sb.append(label + getContentType() + LF + LF);&lt;br /&gt;     }&lt;br /&gt;     sb.append(strBody);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * return the message body content type by looking for "text/*" content&lt;br /&gt;  * types. The initial call must be originated from MessageBean.&lt;br /&gt;  * &lt;br /&gt;  * @param level -&lt;br /&gt;  *            structure tree level&lt;br /&gt;  * @return body content type&lt;br /&gt;  */&lt;br /&gt; String getBodyContentType(int level) {&lt;br /&gt;  String bodyType = null;&lt;br /&gt;  String this_mtype = this.getMimeType();&lt;br /&gt;  if (this_mtype.startsWith("text")) {&lt;br /&gt;   // exclude obvious body parts&lt;br /&gt;   if (!Part.ATTACHMENT.equals(getDisposition())) {&lt;br /&gt;    bodyType = getContentType();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart/alternative")) {&lt;br /&gt;   // get the last text alternative (order: text/plain -&amp;gt; text/html)&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean aNode = it.next();&lt;br /&gt;    if (aNode.getMimeType().startsWith("text")) {&lt;br /&gt;     bodyType = aNode.getBodyContentType(level + 1);&lt;br /&gt;    }&lt;br /&gt;    else if (aNode.getMimeType().startsWith("multipart/related")) {&lt;br /&gt;     String type = aNode.getBodyContentType(level + 1);&lt;br /&gt;     if (type != null &amp;amp;&amp;amp; type.startsWith("text")) {&lt;br /&gt;      bodyType = type;&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart")) {&lt;br /&gt;   // if it's again a multipart, go deeper for the body content type&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean subNode = it.next();&lt;br /&gt;    bodyType = subNode.getBodyContentType(level + 1);&lt;br /&gt;    if (bodyType != null)&lt;br /&gt;     break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return bodyType;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * get a BodypartBean that holds the message text. The initial call must be&lt;br /&gt;  * originated from MessageBean.&lt;br /&gt;  * &lt;br /&gt;  * @param level&lt;br /&gt;  * @return a BodypartBean&lt;br /&gt;  */&lt;br /&gt; BodypartBean getBodyNode(int level) {&lt;br /&gt;  BodypartBean bodyNode = null;&lt;br /&gt;  String this_mtype = this.getMimeType();&lt;br /&gt;  if (this_mtype.startsWith("text")) {&lt;br /&gt;   // exclude obvious body parts&lt;br /&gt;   if (!Part.ATTACHMENT.equals(getDisposition())) {&lt;br /&gt;    bodyNode = this;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart/alternative")) {&lt;br /&gt;   // get the last text alternative (order: text/plain -&amp;gt; text/html)&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean aNode = it.next();&lt;br /&gt;    if (aNode.getMimeType().startsWith("text")) {&lt;br /&gt;     bodyNode = aNode.getBodyNode(level + 1);&lt;br /&gt;    }&lt;br /&gt;    else if (aNode.getMimeType().startsWith("multipart/related")) {&lt;br /&gt;     BodypartBean node = aNode.getBodyNode(level + 1);&lt;br /&gt;     if (node != null &amp;amp;&amp;amp; node.getMimeType().startsWith("text")) {&lt;br /&gt;      bodyNode = node;&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  else if (this_mtype.startsWith("multipart")) {&lt;br /&gt;   // if it's again a multipart, go deeper for the body node&lt;br /&gt;   for (Iterator&amp;lt;BodypartBean&amp;gt; it = getIterator(); it.hasNext();) {&lt;br /&gt;    BodypartBean subNode = it.next();&lt;br /&gt;    bodyNode = subNode.getBodyNode(level + 1);&lt;br /&gt;    if (bodyNode != null)&lt;br /&gt;     break;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return bodyNode;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return disposition&lt;br /&gt;  */&lt;br /&gt; public String getDisposition() {&lt;br /&gt;  return disposition;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return description&lt;br /&gt;  */&lt;br /&gt; public String getDescription() {&lt;br /&gt;  return description;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getFileName() {&lt;br /&gt;  return fileName;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return value as a byte array&lt;br /&gt;  */&lt;br /&gt; public byte[] getValue() {&lt;br /&gt;  return this.value;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return headers of the body part&lt;br /&gt;  */&lt;br /&gt; public List&amp;lt;MsgHeader&amp;gt; getHeaders() {&lt;br /&gt;  return this.headers;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return size of the body part&lt;br /&gt;  */&lt;br /&gt; public int getSize() {&lt;br /&gt;  return this.size;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set content type of this body part&lt;br /&gt;  * &lt;br /&gt;  * @param contentType -&lt;br /&gt;  *            content type&lt;br /&gt;  */&lt;br /&gt; public void setContentType(String contentType) {&lt;br /&gt;  this.contentType = contentType;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set the value of this body part from an input object. Valid object type:&lt;br /&gt;  * &lt;br /&gt;  * @param value -&lt;br /&gt;  *            node value&lt;br /&gt;  */&lt;br /&gt; public final void setValue(Object value) {&lt;br /&gt;  if (value instanceof String)&lt;br /&gt;   setValue((String) value);&lt;br /&gt;  else if (value instanceof InputStream)&lt;br /&gt;   setValue((InputStream) value);&lt;br /&gt;  else if (value instanceof byte[])&lt;br /&gt;   this.value = (byte[]) value;&lt;br /&gt;  else if (value == null)&lt;br /&gt;   this.value = null;&lt;br /&gt;  else&lt;br /&gt;   throw new IllegalArgumentException("The input was not a type as expected");&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set disposition&lt;br /&gt;  * &lt;br /&gt;  * @param disposition&lt;br /&gt;  */&lt;br /&gt; public void setDisposition(String disposition) {&lt;br /&gt;  this.disposition = disposition;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set description&lt;br /&gt;  * &lt;br /&gt;  * @param description&lt;br /&gt;  */&lt;br /&gt; public void setDescription(String description) {&lt;br /&gt;  this.description = description;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setFileName(String fileName) {&lt;br /&gt;  this.fileName = fileName;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set a string value&lt;br /&gt;  * &lt;br /&gt;  * @param value -&lt;br /&gt;  *            part value&lt;br /&gt;  */&lt;br /&gt; protected final void setValue(String value) {&lt;br /&gt;  try {&lt;br /&gt;   this.value = value.getBytes("iso-8859-1"); // mail-safe&lt;br /&gt;  }&lt;br /&gt;  catch (UnsupportedEncodingException uex) {&lt;br /&gt;   // fall back&lt;br /&gt;   this.value = value.getBytes();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set value from an input stream. The input stream will be read into a byte&lt;br /&gt;  * array.&lt;br /&gt;  * &lt;br /&gt;  * @param value -&lt;br /&gt;  *            an InputStream&lt;br /&gt;  */&lt;br /&gt; protected final void setValue(InputStream value) {&lt;br /&gt;  // if the stream is not buffered, wrap it with a BufferedInputStream&lt;br /&gt;  if (!(value instanceof BufferedInputStream)) {&lt;br /&gt;   value = new BufferedInputStream(value);&lt;br /&gt;  }&lt;br /&gt;  DataInputStream dis = new DataInputStream(value);&lt;br /&gt;  ByteArrayOutputStream baos = new ByteArrayOutputStream();&lt;br /&gt;  byte[] buf = new byte[512];&lt;br /&gt;  int len;&lt;br /&gt;  try {&lt;br /&gt;   while ((len = dis.read(buf)) &amp;gt; 0) {&lt;br /&gt;    baos.write(buf, 0, len);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  catch (IOException e) {&lt;br /&gt;   logger.error("IOExcetion caught", e);&lt;br /&gt;  }&lt;br /&gt;  this.value = baos.toByteArray();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set headers for this body part&lt;br /&gt;  * &lt;br /&gt;  * @param headers -&lt;br /&gt;  *            headers in a List&lt;br /&gt;  */&lt;br /&gt; public void setHeaders(List&amp;lt;MsgHeader&amp;gt; headers) {&lt;br /&gt;  this.headers.clear();&lt;br /&gt;  this.headers.addAll(headers);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * Set headers for this body part&lt;br /&gt;  * &lt;br /&gt;  * @param part -&lt;br /&gt;  *            Part holds the data&lt;br /&gt;  * @throws MessagingException&lt;br /&gt;  */&lt;br /&gt; public void setHeaders(Part part) throws MessagingException {&lt;br /&gt;  this.headers.clear();&lt;br /&gt;  if (part == null)&lt;br /&gt;   return;&lt;br /&gt;  Enumeration&amp;lt;?&amp;gt; enu = part.getAllHeaders();&lt;br /&gt;  while (enu.hasMoreElements()) {&lt;br /&gt;   Header jmHdr = (Header) enu.nextElement();&lt;br /&gt;   MsgHeader header = new MsgHeader();&lt;br /&gt;   header.setName(jmHdr.getName());&lt;br /&gt;   header.setValue(jmHdr.getValue());&lt;br /&gt;   this.headers.add(header);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * set the value size of this body part&lt;br /&gt;  * &lt;br /&gt;  * @param size&lt;br /&gt;  */&lt;br /&gt; public void setSize(int size) {&lt;br /&gt;  this.size = size;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * clean up this object&lt;br /&gt;  */&lt;br /&gt; public void clearParameters() {&lt;br /&gt;  disposition = null;&lt;br /&gt;  description = null;&lt;br /&gt;  fileName = null;&lt;br /&gt;  contentType = DEFAULT_CONTENT_TYPE;&lt;br /&gt;  value = null;&lt;br /&gt;  headers.clear();&lt;br /&gt;  size = 0;&lt;br /&gt;  attachParts.clear();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @return child body parts in a list&lt;br /&gt;  */&lt;br /&gt; public List&amp;lt;BodypartBean&amp;gt; getNodes() {&lt;br /&gt;  return attachParts;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * convert the body part object to a string for printing&lt;br /&gt;  * &lt;br /&gt;  * @param level -&lt;br /&gt;  *            structure tree level&lt;br /&gt;  * @return string&lt;br /&gt;  */&lt;br /&gt; public String toString(int level) {&lt;br /&gt;  StringBuffer sb = new StringBuffer();&lt;br /&gt;  sb.append("-&amp;gt; Level(" + level + ")****** BEGIN BodypartBean ******" + LF);&lt;br /&gt;&lt;br /&gt;  sb.append("Mime Type: " + getContentType() + ", Disposition: " + disposition&lt;br /&gt;    + ", Description: " + description + LF);&lt;br /&gt;  if (!(this instanceof MessageBean)) {&lt;br /&gt;   for (int i = 0; i &amp;lt; headers.size(); i++) {&lt;br /&gt;    MsgHeader hdr = (MsgHeader) headers.get(i);&lt;br /&gt;    sb.append("Header Line - " + hdr.getName() + ": " + hdr.getValue() + LF);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  if (value != null) {&lt;br /&gt;   if (getMimeType().indexOf("text") &amp;gt;= 0 || getMimeType().indexOf("message") &amp;gt;= 0)&lt;br /&gt;    sb.append(new String(value) + LF);&lt;br /&gt;   else&lt;br /&gt;    sb.append("Data contains nonprintable content." + LF);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  Iterator&amp;lt;?&amp;gt; it = getIterator();&lt;br /&gt;  while (it.hasNext()) {&lt;br /&gt;   sb.append(((BodypartBean) it.next()).toString(level + 1));&lt;br /&gt;  }&lt;br /&gt;  sb.append("&amp;lt;- Level(" + level + ")****** END BodypartBean ******" + LF);&lt;br /&gt;  return sb.toString();&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3874086622685697943-1014432669700698694?l=javaclue.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javaclue.blogspot.com/feeds/1014432669700698694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean_02.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/1014432669700698694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3874086622685697943/posts/default/1014432669700698694'/><link rel='alternate' type='text/html' href='http://javaclue.blogspot.com/2009/09/portable-java-mail-message-bean_02.html' title='Portable java mail message bean - Part 1'/><author><name>Jack W.</name><uri>http://www.blogger.com/profile/18038342933149451913</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
