diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgent.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgent.java new file mode 100644 index 00000000..5a8de298 --- /dev/null +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgent.java @@ -0,0 +1,143 @@ +package org.mibew.api; + +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.security.NoSuchAlgorithmException; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.parsers.ParserConfigurationException; + +import org.xml.sax.SAXException; + +/** + * @author inspirer + */ +public class MibewAgent { + + private final MibewAgentOptions fOptions; + private final MibewAgentListener fListener; + private final MibewUpdateThread fThread; + private volatile boolean started; + + public MibewAgent(MibewAgentOptions options, MibewAgentListener listener) { + fOptions = options; + fListener = listener; + fThread = new MibewUpdateThread(); + started = false; + } + + public synchronized void launch() { + if(!started) { + fThread.start(); + started = true; + } + } + + public synchronized void stop() { + if(started) { + fThread.disconnect(); + started = false; + } + } + + public boolean isOnline() { + return fThread.isOnline(); + } + + private void logError(String message, Throwable th) { + System.err.println(message); + } + + private class MibewUpdateThread extends Thread { + + private volatile boolean fExiting; + private volatile boolean isOnline = false; + private final Object fSync = new Object(); + + public MibewUpdateThread() { + setName("Mibew Connection thread"); + fExiting = false; + } + + public void disconnect() { + synchronized (fSync) { + fExiting = true; + fSync.notifyAll(); + } + } + + @Override + public void run() { + while(!fExiting) { + try { + connectAndTrack(); + } catch(InterruptedException ex) { + /* ignore */ + } catch(Throwable th) { + logError(th.getMessage(), th); + } + } + setOnline(false); + } + + private void setOnline(boolean online) { + if(isOnline != online) { + isOnline = online; + fListener.onlineStateChanged(online); + } + } + + public boolean isOnline() { + return isOnline; + } + + private void connectAndTrack() throws InterruptedException, UnsupportedEncodingException, NoSuchAlgorithmException, MalformedURLException, ParserConfigurationException, SAXException { + setOnline(false); + MibewConnection conn = new MibewConnection(fOptions.getUrl(), fOptions.getLogin(), fOptions.getPassword()) { + @Override + protected void handleError(String message, Exception ex) { + logError(message, ex); + } + }; + if(!conn.connect()) { + logError("Wrong server, login or password.", null); + interruptableSleep(fOptions.getPollingInterval() * 3); + return; + } + final List createdThreads = new LinkedList(); + MibewTracker mt = new MibewTracker(conn, new MibewTrackerListener(){ + @Override + public void threadCreated(MibewThread thread) { + createdThreads.add(thread); + } + }); + long maxTime = System.currentTimeMillis() + fOptions.getConnectionRefreshTimeout()*1000; + + while(!fExiting && (System.currentTimeMillis() < maxTime)) { + try { + createdThreads.clear(); + mt.update(); + fListener.updated(mt.getThreads(), createdThreads.toArray(new MibewThread[createdThreads.size()])); + } catch (Exception e) { + setOnline(false); + interruptableSleep(fOptions.getPollingInterval() / 2); + continue; + } + setOnline(true); + interruptableSleep(fOptions.getPollingInterval()); + } + + conn.disconnect(); + } + + private void interruptableSleep(long millis) throws InterruptedException { + synchronized (fSync ) { + if(fExiting) { + return; + } + fSync.wait(millis); + } + } + } +} diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentListener.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentListener.java new file mode 100644 index 00000000..78df236e --- /dev/null +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentListener.java @@ -0,0 +1,10 @@ +package org.mibew.api; + +public abstract class MibewAgentListener { + + protected void onlineStateChanged(boolean isOnline) { + } + + protected void updated(MibewThread[] all, MibewThread[] created) { + } +} diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentOptions.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentOptions.java new file mode 100644 index 00000000..ecea7a04 --- /dev/null +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentOptions.java @@ -0,0 +1,40 @@ +package org.mibew.api; + +/** + * @author inspirer + */ +public class MibewAgentOptions { + + private String fUrl; + private String fLogin; + private String fPassword; + private int fConnectionRefreshTimeout = 900; // 15 minutes + private int fPollingInterval = 3000; // 2 sec + + public MibewAgentOptions(String fUrl, String fLogin, String fPassword) { + super(); + this.fUrl = fUrl; + this.fLogin = fLogin; + this.fPassword = fPassword; + } + + public String getLogin() { + return fLogin; + } + + public String getPassword() { + return fPassword; + } + + public String getUrl() { + return fUrl; + } + + public int getConnectionRefreshTimeout() { + return fConnectionRefreshTimeout; + } + + public int getPollingInterval() { + return fPollingInterval; + } +} diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewTracker.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewTracker.java index 96aa3188..6a391ad1 100644 --- a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewTracker.java +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewTracker.java @@ -1,75 +1,70 @@ package org.mibew.api; import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.mibew.api.handlers.UpdateHandler; +import org.xml.sax.SAXException; /** - * @author inspirer + * @author inspirer */ public class MibewTracker { - + private final MibewConnection fConnection; private final MibewTrackerListener fListener; private long fSince = 0; private long fLastUpdate = 0; - - private final Map fThreads; + + private final Map fThreads; public MibewTracker(MibewConnection conn, MibewTrackerListener listener) { this.fConnection = conn; this.fListener = listener; this.fThreads = new HashMap(); } - - public void track() throws InterruptedException { - for(int i = 0; i < 5; i++) { - try { - MibewResponse response = fConnection.request("operator/update.php", "since="+fSince); - SAXParser sp = SAXParserFactory.newInstance().newSAXParser(); - UpdateHandler handler = new UpdateHandler(); - sp.parse(new ByteArrayInputStream(response.getResponse()), handler); - handleResponse(response, handler); - } catch(Exception e) { - System.err.println("update exception: " + e.getMessage()); - } - Thread.sleep(1000); - } + + public void update() throws IOException, SAXException, ParserConfigurationException { + MibewResponse response = fConnection.request("operator/update.php", "since=" + fSince); + SAXParser sp = SAXParserFactory.newInstance().newSAXParser(); + UpdateHandler handler = new UpdateHandler(); + sp.parse(new ByteArrayInputStream(response.getResponse()), handler); + handleResponse(response, handler); } - private void handleResponse(MibewResponse response, UpdateHandler handler) { - if(handler.getResponse() == UpdateHandler.UPD_ERROR) { - System.out.println("Update error: " + handler.getMessage()); - } else if(handler.getResponse() == UpdateHandler.UPD_THREADS) { - System.out.println("Updated.... " + handler.getRevision()); + private void handleResponse(MibewResponse response, UpdateHandler handler) throws IOException { + if (handler.getResponse() == UpdateHandler.UPD_ERROR) { + throw new IOException("Update error: " + handler.getMessage()); + } else if (handler.getResponse() == UpdateHandler.UPD_THREADS) { fSince = handler.getRevision(); fLastUpdate = handler.getTime(); List threads = handler.getThreads(); - if(threads != null && threads.size() > 0) { + if (threads != null && threads.size() > 0) { processUpdate(threads); } } else { - System.out.println("Update error"); - System.out.println(response.getResponseText()); + throw new IOException("Update error: " + response.getResponseText()); } } - + private void processUpdate(List updated) { - for(MibewThread mt : updated) { + for (MibewThread mt : updated) { MibewThread existing = fThreads.get(mt.fId); boolean isClosed = mt.fState.equals("closed"); - if(existing == null) { - if(!isClosed) { + if (existing == null) { + if (!isClosed) { fThreads.put(mt.fId, mt); fListener.threadCreated(mt); } - } else if(isClosed) { + } else if (isClosed) { fThreads.remove(mt.fId); fListener.threadClosed(existing); } else { @@ -82,4 +77,9 @@ public class MibewTracker { public long getLastUpdate() { return fLastUpdate; } + + public MibewThread[] getThreads() { + Collection values = fThreads.values(); + return values.toArray(new MibewThread[values.size()]); + } } diff --git a/src/mibewjava/org.mibew.jabber/src/org/mibew/jabber/Application.java b/src/mibewjava/org.mibew.jabber/src/org/mibew/jabber/Application.java index 4ba649b1..072ab3c1 100644 --- a/src/mibewjava/org.mibew.jabber/src/org/mibew/jabber/Application.java +++ b/src/mibewjava/org.mibew.jabber/src/org/mibew/jabber/Application.java @@ -56,7 +56,7 @@ public class Application { } }); - mt.track(); + //mt.track(); connection.disconnect(); } diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/ConsoleApp.java b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/ConsoleApp.java new file mode 100644 index 00000000..ec0d85dd --- /dev/null +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/ConsoleApp.java @@ -0,0 +1,24 @@ +package org.mibew.notifier; + +import org.mibew.api.MibewAgent; +import org.mibew.api.MibewAgentListener; +import org.mibew.api.MibewAgentOptions; + +public class ConsoleApp { + + public static void main(String[] args) { + MibewAgent agent = new MibewAgent(new MibewAgentOptions("http://localhost:8080/webim/", "admin", "1"), new MibewAgentListener() { + @Override + protected void onlineStateChanged(boolean isOnline) { + System.out.println("now " + (isOnline ? "online" : "offline")); + } + }); + agent.launch(); + try { + Thread.sleep(3500); + } catch (InterruptedException e) { + } + + agent.stop(); + } +} diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/NotifyApp.java b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/NotifyApp.java index 142e0526..2c914609 100644 --- a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/NotifyApp.java +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/NotifyApp.java @@ -1,11 +1,17 @@ package org.mibew.notifier; +import org.mibew.api.MibewAgent; +import org.mibew.api.MibewAgentOptions; + public class NotifyApp { public static void main(String[] args) { TrayNotifier tn = new TrayNotifier(); tn.init(); + MibewAgent agent = new MibewAgent(new MibewAgentOptions("http://localhost:8080/webim/", "admin", "1"), tn); + agent.launch(); + tn.setAgent(agent); } } diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/TrayNotifier.java b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/TrayNotifier.java index 1548744b..85a92368 100644 --- a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/TrayNotifier.java +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/TrayNotifier.java @@ -13,9 +13,17 @@ import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.net.URL; -public class TrayNotifier { +import org.mibew.api.MibewAgent; +import org.mibew.api.MibewAgentListener; +import org.mibew.api.MibewThread; + +public class TrayNotifier extends MibewAgentListener { private TrayIcon trayIcon; + private MibewAgent agent; + + private Image online; + private Image offline; public TrayNotifier() { } @@ -24,19 +32,22 @@ public class TrayNotifier { if (SystemTray.isSupported()) { SystemTray tray = SystemTray.getSystemTray(); - URL url = this.getClass().getResource("tray.png"); - Image image = Toolkit.getDefaultToolkit().getImage(url); + online = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("tray_on.png")); + offline = Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("tray_off.png")); PopupMenu popup = new PopupMenu(); MenuItem exitItem = new MenuItem("Exit", new MenuShortcut(KeyEvent.VK_X)); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { + if(agent != null) { + agent.stop(); + } System.exit(0); } }); popup.add(exitItem); - trayIcon = new TrayIcon(image, "Mibew Notifier", popup); + trayIcon = new TrayIcon(offline, "Mibew Notifier", popup); trayIcon.setImageAutoSize(true); try { @@ -50,7 +61,17 @@ public class TrayNotifier { System.exit(1); } } + + @Override + protected void onlineStateChanged(boolean isOnline) { + trayIcon.setImage(isOnline ? online : offline); + } + + @Override + protected void updated(MibewThread[] all, MibewThread[] created) { + } - public void setStatus(boolean online) { + public void setAgent(MibewAgent agent) { + this.agent = agent; } } diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray_off.png b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray_off.png new file mode 100644 index 00000000..daf8251b Binary files /dev/null and b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray_off.png differ diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray.png b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray_on.png similarity index 100% rename from src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray.png rename to src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/tray_on.png