Hilfe zu Design mit EJB

Hallo zusammen,

ich möchte gerne folgendes realisieren:
- Verschiedene Emailadressen per idle() abrufen um zu prüfen, ob neue Email ankommen.

Hierzu habe ich folgenden Code gefunden, der auch genau das macht:
https://gist.github.com/DeBukkIt/19f194935a2007c8ac0a64e798cd7fb8

Nun ist mein Problem aber, dass ich ebenfalls EJB verwende.
Das heißt:
Ich möchte auch innerhalb der oben genannten Klasse (der Link) auf eines meiner anderen EJB Klassen machen, was aber so dann nicht geht....

Wie kann man das genau machen?
Wenn ich das richtig sehe habe ich:

MyEmailIdleService
a) Ruft alle Emailadresse ab, bei denen eine Prüfung erfolgen soll
b) Macht dann einen Thread per Emailadresse auf und überwacht diesen

Funktionen wären dann:
void start();
void stop(String emailAdresse);

Kann hier jemand bitte helfen wie das genau aussehen kann?
 
Habe ich noch nicht gesehen - benötige ich dann die oben geposteten Code von diesem Link überhaupt noch?
 
Zuletzt bearbeitet:
Habe ich noch nicht gesehen - benötige ich dann die oben geposteten Code von diesem Link überhaupt noch?
Denke mal nicht - ich hatte die Anforderung bisher nicht, in einer Java EE-Anwendung Postfächer überwachen zu müssen. Da JavaMail sowieso zum Einsatz kommt, frage ich mich halt, warum man nicht verwendet, was die Lib bietet. Sieht auf den ersten Blick jedenfalls recht einfach aus.
 
ok...
Muss ich dann für jedes meiner Emailadressen, die ich überwachen möchte ein Objekt von "IdleManager" erstellen?
 
ok...
Muss ich dann für jedes meiner Emailadressen, die ich überwachen möchte ein Objekt von "IdleManager" erstellen?
In der Doku steht: "The IdleManager is created with a Session, which it uses only to control debug output. A single IdleManager instance can watch multiple Folders from multiple Stores and multiple Sessions."
 
das habe ich auch gesehen...

Aber ich möchte die Möglichkeit haben einzelne Emailadresse auch nicht mehr zu überwachen...
Das brauche ich doch pro Emailadresse einen IdleManager?
Damit ich die stop() - Methode anwenden kann?

Wie kann ich das machen?
Ich könnte ja zB alle IdleManager Objekte in einer Liste speichern. Leider hat IdleManager keine ID, sonst könnte ich sowas machen wie

public void stop(String id){

// Suche ID aus der Liste heraus und wenn auf dieses Objekt dann stop() an...

}
 
Das brauche ich doch pro Emailadresse einen IdleManager?
Damit ich die stop() - Methode anwenden kann?
Du brauchst die stop()-Methode dafür nicht. Die Überwachung durch den IdleManager funktioniert andersrum. Auch das steht in der Doku (den wichtigen Part habe ich mal hervorgehoben): "Note that, after processing new messages in your listener, or doing any other operations on the folder in any other thread, you need to tell the IdleManager to watch for more new messages. Unless, of course, you close the folder."

Heißt: sobald Du irgendwas mit einem überwachten Folder machst, musst Du dem IdleManager erneut sagen, dass er den Folder überwachen soll.
 
hm...
Das heißt ich bräuchte eine while Schleife?

Im ersten Link wurde das ja auch so implementiert:
while(!stopped) {
....
}

Ich würde gerne auch loggen was das Programm so macht, aber wenn ich die Klasse aus der API nehme, kann ich das nicht machen?
 
ja, aber wenn ich dann von "außen" eingreifen möchte und die Überwachung eines einzelnen Postfachs stoppen möchte, dann brauche ich doch eine Methode wie stop() ?
 
Du kannst Dir natürlich eine Methode stop() schreiben, die muss nichts anderes tun, als irgendeine Operation auf dem Folder auszuführen.
 
Also so ganz komme ich damit leider noch nicht klar.... Ich habe jetzt doch mal den Beispielcode aus meinem ersten Post genommen, da das Grundprinzip hier schon passt - und auch Dinge wie alle 9 Minuten der Server connected wird um einen Timeout vermeiden etc.

Das Überwachen der Email funktioniert, aber mein Problem ist:
a) Aufruf einer anderen EJB dann

Vielleicht mal vom Grundprinzip, was ich machen will:
Ich will mehrere Emailadressen überwachen, ob neue Email vorhanden sind.
Wenn ja (und hier kommt dann der Aufruf einer anderen EJB zum Einsatz), möchte ich in meinem Backend prüfen, ob es zu dem Betreff und dem Sender in meiner Datenbank bereits einen Eintrag gibt.
Wenn ja, dann werden weitere Dinge geschehen (Speicherung in DB etc. - soll jetzt aber nicht das Thema sein)...

Im Moment sieht es nun so aus:
1) TicketEmailWatcherService...
-> Ich denke, diese muss ich später noch mit @Startup deklarieren, dass diese beim Starten der Applikation gestartet wird und die startListener() - Methode mit @PostConstruct ?

Hier hole ich mir die Emailadressen aus der Datenbank, die überprüft werden sollen.
Ebenfalls erstelle ich das Store - Objekt, um mich später zu connecten....

Grundproblem:
Am liebsten wäre mir in der "TicketEmailWatcherService", dass ich dann eine Liste der Emails zurückbekomme, die neu sind.
Und dann kann ich mit den "Message" - Objekten machen, was ich will. Nachricht in der DB speichern usw.
Aber wie kann ich das machen??

Java:
public class TicketEmailWatcherService {

    private final Logger LOGGER = LoggerFactory.getLogger(TicketEmailWatcherService.class);

    @EJB
    private TicketService ticketService;

    @EJB
    private EmployeeService employeeService;

    @EJB
    private EmailSettingPasswordService emailSettingPasswordService;

    @EJB
    private TicketEmailSettingService ticketEmailSettingService;

    public void startListener()
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException,
            BadPaddingException, EmailSettingNotFoundException, IOException, MessagingException {

        LOGGER.info("START startListener");

        List<TicketEmailSetting> list = ticketEmailSettingService.findAllTicketEmailSettingListByStatusActive();

        if (list == null || list.isEmpty())
            return;

        for (TicketEmailSetting ticketEmailSetting : list) {

            Store store = connect(ticketEmailSetting.getEmailSetting());
            MailPushEventHandler mailEventHandler = new MailPushEventHandler(store);

        }

        LOGGER.info("END startListener");
    }
Java:
import java.io.IOException;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Store;

public class MailPushEventHandler extends MailPushEmpfaenger {

    public MailPushEventHandler(Store store) {
        super(store);
    }

    @Override
    public void onMailReceived(Object[] messages) {

        if (messages != null && messages.length > 0) {

            for (Object msg : messages) {

                try {
                    String textFromMessage = getTextFromMessage((Message) msg);

                } catch (MessagingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


[CODE=java]public class MailPushEmpfaenger {

    private final Logger LOGGER = LoggerFactory.getLogger(MailPushEmpfaenger.class);

    private String randomInstanceID;
    private boolean stopped;

    private IMAPFolder inbox;

    private Thread listeningThread;
    private Thread reconnectionThread;

    /**
     * Constructor
     *
     * @param host
     * @param username
     * @param password
     */
    public MailPushEmpfaenger(Store store) {

        randomInstanceID = String.valueOf(new Random().nextInt(Integer.MAX_VALUE));

        listeningThread = new Thread(new Runnable() {

            @Override
            public void run() {

                while (!stopped) {

                    try {

                        LOGGER.info("[MailRX " + randomInstanceID + "] Connecting and logging in...");

//                        // Connect with IMAP Server
//                        Properties props = new Properties();
//                        props.setProperty("mail.store.protocol", "imaps");
//                        Session session = Session.getDefaultInstance(props, null);
//                        Store store = session.getStore("imaps");
//                        store.connect(host, username, password);

                        LOGGER.info("[MailRX " + randomInstanceID + "] Connected.");

                        // Open inbox
                        inbox = (IMAPFolder) store.getFolder("INBOX");
                        inbox.open(IMAPFolder.READ_WRITE);

                        // initial Mail Check
                        checkNewMessages();

                        while (!stopped) {
                            LOGGER.info("[MailRX " + randomInstanceID + "] IDLE-ing");

                            inbox.idle(true);
                            checkNewMessages();
                        }

                    } catch (MessagingException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        reconnectionThread = new Thread(new Runnable() {
            @Override
            public void run() {

                while (!stopped) {

                    try {
                        // Sleep 9 minutes
                        Thread.sleep(9 * 60 * 1000);

                        LOGGER.info("[MailRX " + randomInstanceID + "] NOOP");

                        inbox.doCommand(new IMAPFolder.ProtocolCommand() {
                            @Override
                            public Object doCommand(IMAPProtocol protocol) throws ProtocolException {
                                protocol.simpleCommand("NOOP", null);
                                return null;
                            }
                        });

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        });

        listeningThread.start();
        reconnectionThread.start();
    }

    /**
     * Check for new Emails
     *
     * @throws MessagingException
     */
    private Object[] checkNewMessages() throws MessagingException {

        LOGGER.info("[MailRX " + randomInstanceID + "] Checking for new messages...");

        if (!inbox.isOpen()) {
            inbox.open(IMAPFolder.READ_WRITE);
        }

        if (inbox.getMessageCount() > 0) {

            ArrayList<Message> msgs = new ArrayList<Message>();

            Message[] tempMsgs = inbox.getMessages(inbox.getMessageCount() - 10, inbox.getMessageCount());
            for (Message tempMsg : tempMsgs) {
                if (!tempMsg.isSet(Flags.Flag.SEEN)) {
                    msgs.add(tempMsg);
                    tempMsg.setFlag(Flags.Flag.SEEN, true);
                }
            }

            onMailReceived(msgs.toArray());
            LOGGER.info("[MailRX " + randomInstanceID + "] Forwarded " + msgs.size() + " E-Mails.");

            return msgs.toArray();
        }
        return null;
    }

    public void onMailReceived(Object[] messages) {
        // Overwrite me
    }

    /**
     * Return the primary text content of the message.</br>
     * Source: https://javaee.github.io/javamail/FAQ#mainbody
     *
     * @param p The message
     * @return The message's main body text content
     * @throws MessagingException If something went wrong
     * @throws IOException        If something else went wrong
     */
    public String getTextFromMessage(Part p) throws MessagingException, IOException {
        if (p.isMimeType("text/*")) {
            String s = (String) p.getContent();
            return s;
        }

        if (p.isMimeType("multipart/alternative")) {
            Multipart mp = (Multipart) p.getContent();
            String text = null;
            for (int i = 0; i < mp.getCount(); i++) {
                Part bp = mp.getBodyPart(i);
                if (bp.isMimeType("text/plain")) {
                    if (text == null)
                        text = getTextFromMessage(bp);
                    continue;
                } else if (bp.isMimeType("text/html")) {
                    String s = getTextFromMessage(bp);
                    if (s != null)
                        return s;
                } else {
                    return getTextFromMessage(bp);
                }
            }
            return text;
        } else if (p.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) p.getContent();
            for (int i = 0; i < mp.getCount(); i++) {
                String s = getTextFromMessage(mp.getBodyPart(i));
                if (s != null)
                    return s;
            }
        }

        return null;
    }

    public void stop() {
        this.stopped = true;
    }

}
}[/CODE]
 
Das Überwachen der Email funktioniert, aber mein Problem ist:
a) Aufruf einer anderen EJB dann
Die EJB Spec "verbietet", dass Du selbst Threads erstellst. Natürlich kann Dich die Spec nicht daran hindern, aber Du solltest keinesfalls aus einem solchen Thread Dinge verwenden, die unter der Fuchtel des Application Servers stehen. Außerdem ist es keine gute Idee, für jede Verbindung einen eigenen Thread zu verbraten.

Wenn Du trotzdem daran festhalten willst, würde mir spontan einfallen, eine LinkedBlockingQueue aus diesen Threads heraus zu füllen und diese Queue aus einem managed Thread (ManagedExecutorService) heraus zu lesen. Die EJB und Deine Threads teilen sich also eine Queue.
 
hm, so wirklich ganz verstehe ich nicht was du meinst...
Hast du ein Beispiel und was muss ich dann umbauen von dem Code oben?
 
Schemenhaft: in Deiner EJB injectest Du einen ManagedExecutorService und erstellst die Queue.
Java:
@Resource
private ManagedExecutorService executor;
private Queue<Object> messages;
private volatile boolean running;

@PostConstruct 
protected void init() {
    messages = new LinkedBlockingQueue<>();
    running = true;
    executor.submit(() -> {
        while (running) {
            Object obj = messages.take();
            doSomethingWith(obj);
        }
    });
}
Bei der Erzeugung der MailPushHandler gibst Du die Queue messages mit, in onMailReceived schiebst Du die Objekte in die Queue (queue.offer()).
 
Nur als kleine Info am Rande: achte doch bitte darauf, deine Beiträge in einem halbwegs passenden Unterforum zu erstellen, dies hier ist ebenso wie deine anderen Beiträge alles andere als ein Anfängerthema :)
 
Ok, danke...

die Fragen, die sich mir dann allerdings ergeben:

a) Was benötige ich dann noch von dem obigen Code? Muss ich das mit den Threads komplett löschen?
b) Was macht: Object obj = messages.take();
c) Wie kann ich einzelne Postfächer stoppen? (Watch stoppen) ?
d) Was ist mit: queue.offer() gemeint?
 
a) alles - es ging ja nur darum, eine Verbindung zwischen den von Dir und den vom Application Server verwalteten Threads herzustellen, um auf die Ressourcen (wie EJBs) zugreifen zu können. Diese Verbindung erfolgt über eine Queue. Daher:
Bei der Erzeugung der MailPushHandler gibst Du die Queue messages mit, in onMailReceived schiebst Du die Objekte in die Queue (queue.offer()).
b) https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/LinkedBlockingQueue.html#take()
c) wie bisher - Du rufst die stop-Methode deines MailPushHandlers auf.
d) https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/LinkedBlockingQueue.html#offer(E)

Das Prinzip ist ganz einfach: Du hast einen Postfach-Thread. Der erhält irgendwann neue Nachrichten und schiebt diese in einen FIFO-Puffer (Queue). Auf der anderen Seite hast Du einen vom Application Server verwalteten Worker-Thread, der einfach eine Nachricht nach der anderen aus der Queue liest und damit irgendwas macht.
 
OK - also habe ich immernoch Threads, die dann aber vom Application Server bearbeitet werden...
Und wie sieht der Aufbau dann der Klassen aus? Welche Modifikation brauche ich in den oberen Klassen?

a) Wie starte ich denn den Listener, der prüft, ob neue Email da sind oder nicht?

b) Im Idealfall rufe ich dann in der public void onMailReceived(Object[] messages) eine Methode via EJB auf, die dann weiteres macht (Ticket erstellen aus den Infos der Message)
Demnach bräuchte ich noch eine Instanzierung in public class MailPushEventHandler extends MailPushEmpfaenger ?

c) Wie muss doSomethingWith(obj); abgeändert werden?
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben