From 01217e6b7dda8fdaa46837abcfed05f0648a1a08 Mon Sep 17 00:00:00 2001 From: Evgeny Gryaznov Date: Sat, 23 Jan 2010 10:35:54 +0000 Subject: [PATCH] Notification application (without options dialog) git-svn-id: https://webim.svn.sourceforge.net/svnroot/webim/trunk@734 c66351dc-e62f-0410-b875-e3a5c0b9693f --- .../src/org/mibew/api/MibewAgent.java | 16 +- .../src/org/mibew/api/MibewAgentOptions.java | 28 +++- .../src/org/mibew/api/MibewThread.java | 158 ++++++++++++++++-- .../src/org/mibew/api/MibewTracker.java | 8 +- .../org/mibew/api/handlers/UpdateHandler.java | 29 +++- .../src/org/mibew/jabber/Application.java | 2 +- .../org.mibew.notifier/src/mibew.ini | 5 + .../src/org/mibew/notifier/BrowserUtil.java | 42 +++++ .../src/org/mibew/notifier/ConsoleApp.java | 8 +- .../src/org/mibew/notifier/NotifyApp.java | 9 +- .../src/org/mibew/notifier/Options.java | 55 ++++++ .../src/org/mibew/notifier/TrayNotifier.java | 62 ++++++- 12 files changed, 378 insertions(+), 44 deletions(-) create mode 100644 src/mibewjava/org.mibew.notifier/src/mibew.ini create mode 100644 src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/BrowserUtil.java create mode 100644 src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/Options.java 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 index 5a8de298..87a1bcd5 100644 --- a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgent.java +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgent.java @@ -45,9 +45,13 @@ public class MibewAgent { return fThread.isOnline(); } - private void logError(String message, Throwable th) { + protected void logError(String message, Throwable th) { System.err.println(message); } + + public MibewAgentOptions getOptions() { + return fOptions; + } private class MibewUpdateThread extends Thread { @@ -114,17 +118,21 @@ public class MibewAgent { }); long maxTime = System.currentTimeMillis() + fOptions.getConnectionRefreshTimeout()*1000; + int errorsCount = 0; while(!fExiting && (System.currentTimeMillis() < maxTime)) { try { createdThreads.clear(); mt.update(); fListener.updated(mt.getThreads(), createdThreads.toArray(new MibewThread[createdThreads.size()])); - } catch (Exception e) { + errorsCount = 0; + setOnline(true); + } catch (Throwable th) { setOnline(false); - interruptableSleep(fOptions.getPollingInterval() / 2); + errorsCount++; + logError("not updated", th); + interruptableSleep(errorsCount < 10 ? fOptions.getPollingInterval() / 2 : fOptions.getPollingInterval() * 2); continue; } - setOnline(true); interruptableSleep(fOptions.getPollingInterval()); } 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 index ecea7a04..94cc7593 100644 --- a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentOptions.java +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewAgentOptions.java @@ -1,5 +1,8 @@ package org.mibew.api; +import java.io.IOException; +import java.util.Properties; + /** * @author inspirer */ @@ -8,8 +11,8 @@ public class MibewAgentOptions { private String fUrl; private String fLogin; private String fPassword; - private int fConnectionRefreshTimeout = 900; // 15 minutes - private int fPollingInterval = 3000; // 2 sec + private int fConnectionRefreshTimeout = 900; // in seconds (15 minutes by default) + private int fPollingInterval = 2000; // 2 sec (in milliseconds) public MibewAgentOptions(String fUrl, String fLogin, String fPassword) { super(); @@ -17,7 +20,7 @@ public class MibewAgentOptions { this.fLogin = fLogin; this.fPassword = fPassword; } - + public String getLogin() { return fLogin; } @@ -37,4 +40,23 @@ public class MibewAgentOptions { public int getPollingInterval() { return fPollingInterval; } + + private static String getProperty(Properties p, String name, String defaultValue) throws IOException { + String result = p.getProperty(name); + if(result == null || result.trim().length() == 0) { + if(defaultValue != null) { + return defaultValue; + } + throw new IOException("No '"+name+"' property"); + } + return result; + } + + public static MibewAgentOptions create(Properties p) throws IOException { + String url = getProperty(p, "mibew.host", null); + String login = getProperty(p, "mibew.login", null); + String password = getProperty(p, "mibew.password", null); + + return new MibewAgentOptions(url, login, password); + } } diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewThread.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewThread.java index f9d31280..fd4f9fa9 100644 --- a/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewThread.java +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/MibewThread.java @@ -1,20 +1,23 @@ package org.mibew.api; +import java.text.MessageFormat; + /** * @author inspirer */ -public class MibewThread { +public class MibewThread implements Comparable { - public final long fId; - public String fState; - public String fClientName = ""; - public String fAgent = ""; - public String fAddress = ""; - public String fFirstMessage = ""; - public boolean fCanOpen = false; - public boolean fCanView = false; - public boolean fCanBan = false; - public String fStateText; + private final long fId; + private String fState; + private String fClientName = ""; + private String fAgent = ""; + private String fAddress = ""; + private String fFirstMessage = ""; + private boolean fCanOpen = false; + private boolean fCanView = false; + private boolean fCanBan = false; + private String fStateText; + private long fWaitingTime; public MibewThread(long id, String state) { fId = id; @@ -34,5 +37,138 @@ public class MibewThread { fCanView = updated.fCanView; fCanBan = updated.fCanBan; fStateText = updated.fStateText; + fWaitingTime = updated.fWaitingTime; + } + + public long getId() { + return fId; + } + + public String getState() { + return fState; + } + + public String getStateText() { + return fStateText; + } + + public void setStateText(String stateText) { + fStateText = stateText; + } + + public String getAddress() { + return fAddress; + } + + public void setAddress(String address) { + fAddress = address; + } + + public String getAgent() { + return fAgent; + } + + public void setAgent(String agent) { + fAgent = agent; + } + + public String getClientName() { + return fClientName; + } + + public void setClientName(String clientName) { + fClientName = clientName; + } + + public String getFirstMessage() { + return fFirstMessage; + } + + public void setFirstMessage(String firstMessage) { + fFirstMessage = firstMessage; + } + + public boolean isCanBan() { + return fCanBan; + } + + public void setCanBan(boolean canBan) { + fCanBan = canBan; + } + + public boolean isCanOpen() { + return fCanOpen; + } + + public void setCanOpen(boolean canOpen) { + fCanOpen = canOpen; + } + + public boolean isCanView() { + return fCanView; + } + + public void setCanView(boolean canView) { + fCanView = canView; + } + + public long getWaitingTime() { + return fWaitingTime; + } + + public void setWaitingTime(long value) { + fWaitingTime = value; + } + + @Override + public int compareTo(MibewThread o) { + int res = index(this).compareTo(index(o)); + if(res != 0) { + return res; + } + return getClientName().compareTo(o.getClientName()); + } + + private Integer index(MibewThread th) { + if("prio".equals(th.getState())) { + return -1; + } + if("wait".equals(th.getState())) { + return 0; + } + return 1; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + boolean isChat = "chat".equals(getState()); + if(isChat) { + sb.append("(chat) "); + } + sb.append(getClientName()); + if(!isCanOpen() && isCanView()) { + sb.append(" (view only)"); + } + if(!isChat) { + sb.append(" - "); + sb.append(formatWaitingTime((System.currentTimeMillis() - getWaitingTime())/1000)); + } + return sb.toString(); + } + + private static String atLeast2(long i) { + return i < 10 ? "0" + i : Long.toString(i); + } + + private static String formatWaitingTime(long time) { + String s = atLeast2(time/60%60) + ":" + atLeast2(time%60); + if(time >= 3600) { + s = atLeast2(time/3600%24) + ":" + s; + if(time >= 24*3600) { + s = time/24/3600 + "d, " + s; + } + } + return s; } } 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 6a391ad1..61b6b4ae 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 @@ -57,15 +57,15 @@ public class MibewTracker { private void processUpdate(List updated) { for (MibewThread mt : updated) { - MibewThread existing = fThreads.get(mt.fId); - boolean isClosed = mt.fState.equals("closed"); + MibewThread existing = fThreads.get(mt.getId()); + boolean isClosed = mt.getState().equals("closed"); if (existing == null) { if (!isClosed) { - fThreads.put(mt.fId, mt); + fThreads.put(mt.getId(), mt); fListener.threadCreated(mt); } } else if (isClosed) { - fThreads.remove(mt.fId); + fThreads.remove(mt.getId()); fListener.threadClosed(existing); } else { existing.updateFrom(mt); diff --git a/src/mibewjava/org.mibew.api/src/org/mibew/api/handlers/UpdateHandler.java b/src/mibewjava/org.mibew.api/src/org/mibew/api/handlers/UpdateHandler.java index 58dc8b44..b941c4bf 100644 --- a/src/mibewjava/org.mibew.api/src/org/mibew/api/handlers/UpdateHandler.java +++ b/src/mibewjava/org.mibew.api/src/org/mibew/api/handlers/UpdateHandler.java @@ -48,10 +48,10 @@ public class UpdateHandler extends DefaultHandler { fCurrentThread = new MibewThread(id, stateid); if(!stateid.equals("closed")) { - fCurrentThread.fStateText = attributes.getValue("state"); - fCurrentThread.fCanOpen = booleanAttribute(attributes.getValue("canopen")); - fCurrentThread.fCanView = booleanAttribute(attributes.getValue("canview")); - fCurrentThread.fCanBan = booleanAttribute(attributes.getValue("canban")); + fCurrentThread.setStateText(attributes.getValue("state")); + fCurrentThread.setCanOpen(booleanAttribute(attributes.getValue("canopen"))); + fCurrentThread.setCanView(booleanAttribute(attributes.getValue("canview"))); + fCurrentThread.setCanBan(booleanAttribute(attributes.getValue("canban"))); } } @@ -68,6 +68,14 @@ public class UpdateHandler extends DefaultHandler { return false; } + private long longValue(String value) throws SAXException { + try { + return Long.parseLong(value); + } catch(NumberFormatException ex) { + throw new SAXException(ex); + } + } + @Override public void endElement(String uri, String localName, String name) throws SAXException { @@ -99,13 +107,18 @@ public class UpdateHandler extends DefaultHandler { String subvar = fPath.peek(); String value = new String(ch, start, length); if("name".equals(subvar)) { - fCurrentThread.fClientName += value; + fCurrentThread.setClientName(fCurrentThread.getClientName() + value); } else if("addr".equals(subvar)) { - fCurrentThread.fAddress += value; + fCurrentThread.setAddress(fCurrentThread.getAddress() + value); } else if("message".equals(subvar)) { - fCurrentThread.fFirstMessage += value; + fCurrentThread.setFirstMessage(fCurrentThread.getFirstMessage() + value); } else if("agent".equals(subvar)) { - fCurrentThread.fAgent += value; + fCurrentThread.setAgent(fCurrentThread.getAgent() + value); + } else if("modified".equals(subvar)) { + if(fCurrentThread.getWaitingTime() != 0) { + throw new SAXException("error: waiting time is already set"); + } + fCurrentThread.setWaitingTime(longValue(value) - fTime + System.currentTimeMillis()); } // TODO 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 072ab3c1..8733ddbe 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 @@ -49,7 +49,7 @@ public class Application { @Override public void threadCreated(MibewThread thread) { try { - chat.sendMessage(thread.fId + ": " + thread.fAddress + " " + thread.fClientName); + chat.sendMessage(thread.getId() + ": " + thread.getAddress() + " " + thread.getClientName()); } catch (XMPPException e) { e.printStackTrace(); } diff --git a/src/mibewjava/org.mibew.notifier/src/mibew.ini b/src/mibewjava/org.mibew.notifier/src/mibew.ini new file mode 100644 index 00000000..2bd8740b --- /dev/null +++ b/src/mibewjava/org.mibew.notifier/src/mibew.ini @@ -0,0 +1,5 @@ +# Mibew parameters + +mibew.host=http://localhost:8080/webim/ +mibew.login=admin +mibew.password=1 diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/BrowserUtil.java b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/BrowserUtil.java new file mode 100644 index 00000000..6ee074bf --- /dev/null +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/BrowserUtil.java @@ -0,0 +1,42 @@ +package org.mibew.notifier; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; + +public class BrowserUtil { + + static final String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "seamonkey", "galeon", + "kazehakase", "mozilla", "netscape" }; + + /** + * Bare Bones Browser Launch + * Version 2.0 (May 26, 2009) + * By Dem Pilafian + * @param url + */ + public static void openURL(String url) throws IOException { + String osName = System.getProperty("os.name"); + try { + if (osName.startsWith("Mac OS")) { + Class fileMgr = Class.forName("com.apple.eio.FileManager"); + Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class }); + openURL.invoke(null, new Object[] { url }); + } else if (osName.startsWith("Windows")) { + Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); + } else { // assume Unix or Linux + boolean found = false; + for (String browser : browsers) + if (!found) { + found = Runtime.getRuntime().exec(new String[] { "which", browser }).waitFor() == 0; + if (found) + Runtime.getRuntime().exec(new String[] { browser, url }); + } + if (!found) + throw new Exception(Arrays.toString(browsers)); + } + } catch (Throwable th) { + throw new IOException("Error attempting to launch web browser\n" + th.toString()); + } + } +} \ No newline at end of file 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 index ec0d85dd..005b52e7 100644 --- a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/ConsoleApp.java +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/ConsoleApp.java @@ -2,12 +2,16 @@ 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() { + Options options = new Options(args); + if(!options.load()) { + return; + } + + MibewAgent agent = new MibewAgent(options.getAgentOptions(), new MibewAgentListener() { @Override protected void onlineStateChanged(boolean isOnline) { System.out.println("now " + (isOnline ? "online" : "offline")); 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 2c914609..eabeaebf 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,15 +1,20 @@ package org.mibew.notifier; import org.mibew.api.MibewAgent; -import org.mibew.api.MibewAgentOptions; +import org.mibew.notifier.Options.JOptions; public class NotifyApp { public static void main(String[] args) { + Options options = new JOptions(args); + if(!options.load()) { + return; + } + TrayNotifier tn = new TrayNotifier(); tn.init(); - MibewAgent agent = new MibewAgent(new MibewAgentOptions("http://localhost:8080/webim/", "admin", "1"), tn); + MibewAgent agent = new MibewAgent(options.getAgentOptions(), tn); agent.launch(); tn.setAgent(agent); diff --git a/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/Options.java b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/Options.java new file mode 100644 index 00000000..30e77d4c --- /dev/null +++ b/src/mibewjava/org.mibew.notifier/src/org/mibew/notifier/Options.java @@ -0,0 +1,55 @@ +package org.mibew.notifier; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import javax.swing.JOptionPane; + +import org.mibew.api.MibewAgentOptions; + +public class Options { + + private MibewAgentOptions agentOptions; + private Properties myProperties; + + public Options(String[] args) { + } + + public boolean load() { + try { + InputStream is = getClass().getClassLoader().getResourceAsStream("mibew.ini"); + if (is != null) { + myProperties = new Properties(); + myProperties.load(is); + agentOptions = MibewAgentOptions.create(myProperties); + return true; + } else { + handleError("cannot find mibew.ini"); + } + } catch (IOException e) { + handleError(e.getMessage()); + } + return false; + } + + protected void handleError(String message) { + System.err.println(message); + } + + public MibewAgentOptions getAgentOptions() { + return agentOptions; + } + + public static class JOptions extends Options { + + public JOptions(String[] args) { + super(args); + } + + @Override + protected void handleError(String message) { + JOptionPane.showMessageDialog(null, message); + } + } +} 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 f8e77827..baafa311 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 @@ -1,6 +1,7 @@ package org.mibew.notifier; import java.awt.AWTException; +import java.awt.EventQueue; import java.awt.Image; import java.awt.MenuItem; import java.awt.MenuShortcut; @@ -12,6 +13,11 @@ import java.awt.TrayIcon.MessageType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; + +import javax.swing.JOptionPane; import org.mibew.api.MibewAgent; import org.mibew.api.MibewAgentListener; @@ -69,11 +75,20 @@ public class TrayNotifier extends MibewAgentListener { } @Override - protected void updated(MibewThread[] all, MibewThread[] created) { - PopupMenu pm = new PopupMenu(); + protected void updated(MibewThread[] all, final MibewThread[] created) { + Arrays.sort(all); + + final PopupMenu pm = new PopupMenu(); + boolean beforeChat = false; for(MibewThread mt : all) { - MenuItem mi = new MenuItem(mt.fClientName); + boolean isChat = "chat".equals(mt.getState()); + if(beforeChat && isChat) { + pm.addSeparator(); + } + MenuItem mi = new MenuItem(mt.toString()); + mi.addActionListener(new LinkActionListener(agent.getOptions().getUrl() + "operator/agent.php?thread=" + mt.getId())); pm.add(mi); + beforeChat = !isChat; } if(all.length > 0) { pm.addSeparator(); @@ -81,16 +96,45 @@ public class TrayNotifier extends MibewAgentListener { MenuItem exitItem = new MenuItem("Exit", new MenuShortcut(KeyEvent.VK_X)); exitItem.addActionListener(fExit); pm.add(exitItem); - trayIcon.setPopupMenu(pm); - if(created.length == 1) { - trayIcon.displayMessage("New visitor", created[0].fClientName + "\n" + created[0].fFirstMessage, MessageType.INFO); - } else if(created.length > 1) { - trayIcon.displayMessage("New visitors", "New " + created.length + " visitors", MessageType.INFO); - } + try { + EventQueue.invokeAndWait(new Runnable() { + @Override + public void run() { + trayIcon.setPopupMenu(pm); + + if(created.length == 1) { + trayIcon.displayMessage("New visitor", created[0].getClientName() + "\n" + created[0].getFirstMessage(), MessageType.INFO); + } else if(created.length > 1) { + trayIcon.displayMessage("New visitors", "New " + created.length + " visitors", MessageType.INFO); + } + } + }); + } catch (InterruptedException e) { + /* skip cycle */ + } catch (InvocationTargetException e) { + /* hmm */ + } } public void setAgent(MibewAgent agent) { this.agent = agent; } + + private static class LinkActionListener implements ActionListener { + String link; + + public LinkActionListener(String link) { + this.link = link; + } + + @Override + public void actionPerformed(ActionEvent e) { + try { + BrowserUtil.openURL(link); + } catch (IOException e1) { + JOptionPane.showMessageDialog(null, e1.getMessage()); + } + } + } }