将JavaMail 1.5与XPage一起使用

时间:2014-11-13 15:56:33

标签: osgi xpages javamail

我有一个XPage应用程序应该通过IMAP(和SSL)从GMail读取邮件并将它们存储在nsf数据库中。

为此,我需要JavaMail 1.5。

在阅读了一些帖子之后,我得出的结论是我必须构建自己的OSGI-Plugin。 在John Dalsgaard关于http://www.dalsgaard-data.eu/blog/wrap-an-existing-jar-file-into-a-plug-in/的博客之后,我能够将JavaMail 1.5 jar文件包装到OSGI-Plugin中。该插件导出所有JavaMail包,因此我可以在我的XPage应用程序中使用它们。

在我的XPage中有一些java代码试图建立与GMail的连接。但是连接总是超时,所以我打开了javamail的调试选项。调试显示我的java代码仍然使用javamail 1.3(由domino服务器提供的)。

因此我将我的java代码移到了OSGI-Plugin并导出了包,所以我仍然可以在我的XPage中使用它。打开javamail的调试显示正确的版本1.5。但是现在我得到例外javax.mail.NoSuchProviderException,无论我是使用imap还是imaps作为协议。

我错过了什么?为什么我不能在osgi-plugin中使用javamail 1.5 jar文件?

3 个答案:

答案 0 :(得分:1)

javax.mail的代码实际上并不是OSGi友好的。它使用Thread上下文ClassLoader来查找实例化实现。

你可以通过以下方式欺骗它:

  • 将javax.mail和com.sun.mail.imap安装到您的OSGi容器中。版本1.5.2具有OSGi清单标题,因此它应该可以正常工作
  • 当您调用加载Provider的javax.mail api函数时,请将线程上下文类加载器设置为一个知道imap类的类加载器。

E.g:

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

请注意,我没有对此进行测试,但解决方案看起来应该类似。您可能必须将更多javax.mail命令放入try块(例如,获取会话)。

不那么丑陋的solutoin

如果你跳过javax.mail API的使用并直接使用imap的类,那么一个不那么难看的解决方案。

E.g:

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

通常在Java EE中使用SPI或类似解决方案时,可以通过跳过工厂机制(使用线程上下文类加载器和其他技巧)并直接实例化实现类来实现相同的功能。

<强>更新

在commant中声明有一个例外:

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

原因似乎是javax.activation。*包在OSGi容器中可用两次。它来自JDK,它也来自其中一个捆绑。问题类似here,原因也来自重复的包。

然而,真正的原因是再次使用Thread Context ClassLoader。如果您查看引发异常的源代码,您将看到Thread Context Classloader用于加载类。这是一个问题,因为我猜TCC是系统类加载器,而javax.mail连接到javax.activation包。

我现在可以想象以下选项:

  • 删除javax.activation包。这可能会解决问题,因为在这种情况下,每个捆绑包都将连接到系统包。但是,你仍然可以在以后获得ClassNotFoundExceptions ...与类加载器的不良做法作斗争:)。
  • 从系统软件包中删除javax.activation。在这种情况下,每个人都将连接到捆绑。但是,javax.activation包(或TCC)无法看到应该加载的类,也会出现同样的问题。
  • 在stacktrace中找到一个可以更改TCC的点,并设置一个可以看到所有必要类的ClassLoader。这又是单一技术的常见技巧(如原始示例,但现在用于此函数调用)。
  • 找到具有更多OSGi友好功能的技术。如果你能找到一个,请在这里发帖:)。

答案 1 :(得分:0)

我在maven中心找到了这个罐子:

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

它似乎是一个有效的OSGi包。也许这有帮助吗?

答案 2 :(得分:0)

你真的需要Java 1.5.2邮件吗?我不这么认为。您需要的是Google enhanced IMAP classes和代码I wrote in January。简而言之:

/** ========================================================================= *
 * 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;
    }

}

如果这对您有用,请告诉我们