Use JavaMail 1.5 with XPages

I have an XPage application that needs to read emails from GMail over IMAP (and SSL) and store them in an nsf database.

For this I need JavaMail 1.5.

After reading some posts, I came to the conclusion that I need to create my own OSGI plugin. Following John Dalsgaard's blog http://www.dalsgaard-data.eu/blog/wrap-an-existing-jar-file-into-a-plug-in/ I was able to wrap the JavaMail 1.5 jar file in OSGI-Plugin. The plugin exports all JavaMail packages so I can use them in my XPage application.

There is some Java code in my XPage that is trying to establish a connection to GMail. But the connection was always disconnected, so I enabled the debug option for javamail. Debugging showed that my Java code is still using javamail 1.3 (the one provided by the domino server).

So I moved my Java code to OSGI-Plugin and exported the package, so I can still use it in my XPage. Enabling debugging for javamail showed correct version 1.5. But now I am getting an exception javax.mail.NoSuchProviderException

no matter if I use imap

or imaps

how protocol.

What am I missing? Why can't I use javamail 1.5 jar file in osgi plugin?

+3


source to share


3 answers


The javax.mail code is not really OSGi friendly. It uses the Thread ClassLoader context to find implementations.

You can cheat it like this:

  • install both javax.mail and com.sun.mail.imap into OSGi container. Version 1.5.2 contains OSGi manifest headers, so it should work
  • When you call the javax.mail api function that loads the Provider, set the thread context classloader to a classloader that most likely knows the imap classes.

eg:.



Thread thread = Thread.currentThread();
ClassLoader previousCl = thread.getContextClassLoader();
thread.setContextClassLoader(IMAPStore.class.getClassLoader());
try {
    session.getStore("imaps");
} finally {
    thread.setContextClassLoader(previousCl);
}

      

Note that I have not tested this, but the solution should look similar. You may need to add a few javax.mail commands to the try block (get session for example).

Less ugly solution [/ strong>

Less ugly solution - if you skip using the javax.mail API and use the imap classes directly.

eg:.



Session session = Session.getDefaultInstance(...);
Store imapStore = new IMAPStore(session, url);

      

Very often, when SPI or a similar solution is used in Java EE, the same functionality can be achieved by skipping the factory mechanism (which uses a streaming thread loader and other tricks) and creating the implementation classes directly.

Update

The command states that there is an exception:

com.sun.mail.handlers.multipart_mixed incompatible with javax.activation.DataContentHandler

      

The reason is that the javax.activation. * available twice in the OSGi container. It comes from the JDK and it also comes from one of the packages. The problem is similar here and the reason also comes from duplicate packages.

However, the real reason is to use the Thread Context ClassLoader again. If you look in the source where the exception is thrown, you can see that the thread context classloader is used to load the class. This is a problem as I think TCC is the system classloader and javax.mail is connected to the javax.activation package.

I can imagine the following parameters right now:

  • Remove the javax.activation package. This may solve the problem, as in this case each kit will be connected to the system package. However, you can still get ClassNotFoundExceptions later ... Dealing with obscene classloader practice :).
  • Remove javax.activation from system packages. In this case, everyone will connect to the kit. However, the same problem can happen that the javax.activation (or TCC) package cannot see the class that needs to be loaded.
  • Find a point in the stacktrace where you can change the TCC and install a ClassLoader that sees all the required classes. This is again a common trick for monogolithic technologies (as in the original example, but now to call this function).
  • Find a technology that has features that are more OSGi friendly. If you can find it, post it here :).
+1


source


I found this jar in maven center:

http://search.maven.org/#artifactdetails|javax.mail|javax.mail-api|1.5.2|jar



It seems to be a valid OSGi package. Maybe this helps?

0


source


Do you really need Java 1.5.2 mail? I do not think so. You need Google's advanced IMAP classes and the code I wrote in January . In a nutshell:

/** ========================================================================= *
 * Copyright (C) 2014 Stephan H. Wissel                                       *
 *                                                                            *
 *  @author     Stephan H. Wissel <stephan@wissel.net>                        *  
 *                                                                            *
 * @version     0.1                                                           *
 * ========================================================================== *
 *                                                                            *
 * Licensed under the  Apache License, Version 2.0  (the "License").  You may *
 * not use this file except in compliance with the License.  You may obtain a *
 * copy of the License at <http://www.apache.org/licenses/LICENSE-2.0>.       *
 *                                                                            *
 * Unless  required  by applicable  law or  agreed  to  in writing,  software *
 * distributed under the License is distributed on an  "AS IS" BASIS, WITHOUT *
 * WARRANTIES OR  CONDITIONS OF ANY KIND, either express or implied.  See the *
 * License for the  specific language  governing permissions  and limitations *
 * under the License.                                                         *
 *                                                                            *
 * ========================================================================== */
package com.notessensei.gimap;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.Item;
import lotus.domino.NotesException;
import lotus.domino.NotesFactory;
import lotus.domino.NotesThread;

import com.sun.mail.gimap.GmailFolder;
import com.sun.mail.gimap.GmailMessage;
import com.sun.mail.gimap.GmailSSLStore;

public class GMail2Notes {

    public static int   MAX_TEST_COUNT  = 50;

    public static void main(String[] args) throws NotesException, MessagingException {

        if (args.length < 3) {
            System.out.println("GMail2Notes username@gmail.com password nsfFileName");
            System.exit(1);
        }

        System.out.println("Starting for " + args[0] + " to " + args[2]);
        NotesThread.sinitThread();
        lotus.domino.Session s = NotesFactory.createSession();
        Database db = s.getDatabase("", args[2]);
        GMail2Notes g2n = new GMail2Notes(args[0], args[1]);
        g2n.addExcludedLabel("Spam");
        g2n.addExcludedLabel("Trash");
        int resultCount = g2n.importMessages(s, db, "All Mail");
        System.out.print("Messages imported:");
        System.out.println(resultCount);
        NotesThread.stermThread();
        System.out.println("Done");
    }

    private final String        userName;
    private final String        passWord;
    private GmailSSLStore       store           = null;
    // set it to false for complete import
    private boolean             isTestMode      = true;

    // Labels we don not want to import
    private final List<String>    excludedLabels  = new ArrayList<String>();

    public GMail2Notes(String usr, String pwd) {
        this.userName = usr;
        this.passWord = pwd;
    }

    /**
     * Add a folder name to the list we are not interested in
     *
     * @param label
     */
    public void addExcludedLabel(String label) {
        this.excludedLabels.add(label);
    }

    public List<String> getSystemFolderNames() throws MessagingException {
        List<String> result = new ArrayList<String>();
        GmailSSLStore store = this.getStore();
        Folder[] folders = store.getFolder("[Gmail]").list();
        for (Folder f : folders) {
            result.add(f.getName());
        }
        return result;
    }

    public int importMessages(lotus.domino.Session s, Database db, String systemFolderName) throws MessagingException {
        int result = 0;

        // The object to move message to
        Mime2Doc md = new Mime2Doc();

        // Getting gMail ready
        GmailFolder f = this.getSystemFolder(systemFolderName);
        f.open(Folder.READ_ONLY);
        Message[] messages = f.getMessages();
        FetchProfile profile = new FetchProfile();
        profile.add(GmailFolder.FetchProfileItem.CONTENT_INFO);
        profile.add(GmailFolder.FetchProfileItem.LABELS);
        profile.add(GmailFolder.FetchProfileItem.MSGID);
        profile.add(GmailFolder.FetchProfileItem.SIZE);

        f.fetch(messages, profile);
        int count = 0;
        for (Message message : messages) {
            result += this.importOneMessage(s, db, md, message);

            // For testing we don't run through all of them
            count++;
            if (this.isTestMode && count >= MAX_TEST_COUNT) {
                break;
            }
        }
        if (f.isOpen()) {
            f.close(false);
        }
        this.cleanup();
        System.out.println("Done");

        return result;
    }

    /**
     * We need a delivered date so documents don't show up in SEND
     *
     * @param doc
     * @param sender
     */
    private void adjustDeliveredDate(Document doc, Address sender) {
        String senderName = sender.toString();
        if (!senderName.equalsIgnoreCase((this.userName))) {
            try {
                Item PostedDate = doc.getFirstItem("PostedDate");
                doc.copyItem(PostedDate, "DeliveredDate");
                doc.save();
                PostedDate.recycle();
            } catch (NotesException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * Strips leading \ from messages
     *
     * @param rawLabel
     * @return label Stripped from Backslash
     */
    private String cleanLabel(String rawLabel) {
        return (rawLabel.startsWith("\\") ? rawLabel.substring(1) : rawLabel).trim();
    }

    private void cleanup() {
        if (this.store != null && this.store.isConnected()) {
            try {
                this.store.close();
            } catch (MessagingException e) {
                e.printStackTrace();
            }
        }
        this.store = null;
    }

    private GmailSSLStore getStore() throws MessagingException {
        if (this.store != null) {
            return this.store;
        }
        Properties props = System.getProperties();
        props.setProperty("mail.imaps.connectiontimeout", "5000");
        props.setProperty("mail.imaps.host", "imap.gmail.com");
        props.setProperty("mail.imaps.partialfetch", "false");
        props.setProperty("mail.imaps.port", "993");
        props.setProperty("mail.imaps.timeout", "5000");
        props.setProperty("mail.mime.base64.ignoreerrors", "true");
        props.setProperty("mail.store.protocol", "gimaps");

        Session session = Session.getDefaultInstance(props, null);
        this.store = (GmailSSLStore) session.getStore("gimaps");
        this.store.connect(this.userName, this.passWord);
        // Ready for connection
        return this.store;
    }

    /**
    * can be: All Mail, Drafts, Important, Sent Mail, Spam, Starred, Trash
    **/
    private GmailFolder getSystemFolder(String folderName) throws MessagingException {
        GmailSSLStore store = this.getStore();
        Folder folder = store.getFolder("[Gmail]").getFolder(folderName);
        return (GmailFolder) folder;

    }

    private int importOneMessage(lotus.domino.Session s, Database db, Mime2Doc md, Message message) {
        int result = 0;
        try {
            GmailMessage g = (GmailMessage) message;
            Address sender = g.getSender();
            String[] labels = g.getLabels();
            System.out.print(g.getMessageID());
            if (labels != null) {
                System.out.print(", ");
                System.out.print(Arrays.toString(labels));
            }

            if (this.processThisMessage(labels)) {
                result = 1;
                Document doc = db.createDocument();
                InputStream in = g.getMimeStream();
                md.importMail(s, in, doc);
                this.moveDocToFolders(doc, labels);
                this.adjustDeliveredDate(doc, sender);
                System.out.println(" - processed");
            } else {
                System.out.println(" - skipped");
            }
        } catch (Exception e) {
            // TODO: record the message for follow-up
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Moves doc to folders as needed
     *
     * @param doc
     * @param labels
     */
    private void moveDocToFolders(Document doc, String[] labels) {
        if (labels != null) {
            for (String label : labels) {
                this.movetoMatchingFolder(doc, label);
            }
        }
    }

    private void movetoMatchingFolder(Document doc, String folderCandidate) {
        // TODO handle the SENT folder, Draft folder
        if (folderCandidate != null && !folderCandidate.trim().equals("")) {
            try {
                String realFolder = this.cleanLabel(folderCandidate);
                if (realFolder.equalsIgnoreCase("inbox")) {
                    doc.putInFolder("($Inbox)");
                } else if (realFolder.equalsIgnoreCase("drafts")) {
                    // TODO handle Drafts
                } else if (realFolder.equalsIgnoreCase("sent")) {
                    // TODO handle SENT
                } else {
                    doc.putInFolder(realFolder, true);
                }
            } catch (NotesException e) {
                e.printStackTrace();
            }
        }
    }

    private boolean processThisMessage(String[] messageLabels) {
        boolean result = true;

        // If the message has no labels we do process it
        if (messageLabels != null && messageLabels.length < 1) {
            for (String rawLabel : messageLabels) {
                String cleanLabel = this.cleanLabel(rawLabel);
                if (this.excludedLabels.contains(cleanLabel)) {
                    result = false;
                    break;
                }
            }
        }
        return result;
    }

}

      

Let us know if this worked for you

0


source







All Articles