package er.imadaptor; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.kano.joscar.logging.LoggingSystem; import org.apache.log4j.Logger; import com.webobjects.appserver.WOAdaptor; import com.webobjects.appserver.WOApplication; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WODynamicURL; import com.webobjects.appserver.WORequest; import com.webobjects.appserver.WOResponse; import com.webobjects.appserver.WOSession; import com.webobjects.appserver._private.WOURLEncoder; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSDelayedCallbackCenter; import com.webobjects.foundation.NSDictionary; import com.webobjects.foundation.NSMutableDictionary; import com.webobjects.foundation.NSNotification; import com.webobjects.foundation.NSNotificationCenter; import com.webobjects.foundation.NSSelector; import er.extensions.foundation.ERXProperties; public class InstantMessengerAdaptor extends WOAdaptor implements IMessageListener { static { LoggingSystem.setLogManager(new JOscarLogManager()); } public static Logger log = Logger.getLogger(InstantMessengerAdaptor.class); public static final String IM_FACTORY_KEY = "IMFactory"; public static final String SCREEN_NAME_KEY = "IMScreenName"; public static final String PASSWORD_KEY = "IMPassword"; public static final String CONVERSATION_TIMEOUT_KEY = "IMTimeout"; public static final String CONVERSATION_ACTION_NAME_KEY = "IMConversationActionName"; public static final String IM_ACTION_URL_KEY = "IMActionURL"; public static final String AUTO_LOGIN_KEY = "IMAutoLogin"; public static final String WATCHER_ENABLED_KEY = "IMWatcherEnabled"; public static final String WATCHER_IM_FACTORY_KEY = "IMWatcherFactory"; public static final String WATCHER_SCREEN_NAME_KEY = "IMWatcherScreenName"; public static final String WATCHER_PASSWORD_KEY = "IMWatcherPassword"; public static final String CENTRALIZE_SCREEN_NAME_KEY = "IMCentralizeScreenName"; public static final String IS_IM_KEY = "IsIM"; public static final String CONVERSATION_KEY = "IMConversation"; public static final String BUDDY_NAME_KEY = "BuddyName"; public static final String MESSAGE_KEY = "Message"; public static final String RAW_MESSAGE_KEY = "RawMessage"; private WOApplication _application; private IInstantMessengerFactory _factory; private Map<String, InstantMessengerConnection> _instantMessengers; private String _centralizeScreenName; private String _defaultScreenName; private String _conversationActionName; private long _conversationTimeout; private boolean _autoLogin; private boolean _running; public InstantMessengerAdaptor(String name, NSDictionary parameters) { super(name, parameters); NSNotificationCenter.defaultCenter().addObserver(this, new NSSelector("sessionDidCreate", new Class[] { NSNotification.class }), WOSession.SessionDidCreateNotification, null); _application = WOApplication.application(); _instantMessengers = new HashMap<>(); _centralizeScreenName = ERXProperties.stringForKey(InstantMessengerAdaptor.CENTRALIZE_SCREEN_NAME_KEY); _factory = getFactory(InstantMessengerAdaptor.IM_FACTORY_KEY); _conversationTimeout = ERXProperties.longForKeyWithDefault(InstantMessengerAdaptor.CONVERSATION_TIMEOUT_KEY, 1000 * 60 * 5); _conversationActionName = ERXProperties.stringForKeyWithDefault(InstantMessengerAdaptor.CONVERSATION_ACTION_NAME_KEY, "imConversation"); _autoLogin = ERXProperties.booleanForKeyWithDefault(InstantMessengerAdaptor.AUTO_LOGIN_KEY, false); String defaultScreenName = ERXProperties.stringForKey(InstantMessengerAdaptor.SCREEN_NAME_KEY); String defaultPassword = ERXProperties.stringForKey(InstantMessengerAdaptor.PASSWORD_KEY); if (defaultScreenName != null) { setDefaultInstantMessenger(defaultScreenName, defaultPassword); } boolean watcherEnabled = ERXProperties.booleanForKeyWithDefault(InstantMessengerAdaptor.WATCHER_ENABLED_KEY, false); if (watcherEnabled) { String watcherScreenName = ERXProperties.stringForKey(InstantMessengerAdaptor.WATCHER_SCREEN_NAME_KEY); String watcherPassword = ERXProperties.stringForKey(InstantMessengerAdaptor.WATCHER_PASSWORD_KEY); if (watcherScreenName == null || watcherPassword == null) { throw new IllegalArgumentException("You must set both '" + InstantMessengerAdaptor.WATCHER_SCREEN_NAME_KEY + "' and '" + InstantMessengerAdaptor.WATCHER_PASSWORD_KEY + "' if '" + InstantMessengerAdaptor.WATCHER_ENABLED_KEY + "' is true."); } IInstantMessengerFactory watcherFactory = getFactory(InstantMessengerAdaptor.WATCHER_IM_FACTORY_KEY); InstantMessengerConnection defaultInstantMessengerConnection = _defaultInstantMessengerConnection(); if (defaultInstantMessengerConnection != null) { defaultInstantMessengerConnection.setWatchDog(watcherScreenName, watcherPassword, watcherFactory); } } } public static InstantMessengerAdaptor instantMessengerAdaptor() { NSArray adaptors = WOApplication.application().adaptors(); InstantMessengerAdaptor matchingAdaptor = null; Enumeration adaptorsEnum = adaptors.objectEnumerator(); while (matchingAdaptor == null && adaptorsEnum.hasMoreElements()) { WOAdaptor adaptor = (WOAdaptor) adaptorsEnum.nextElement(); if (adaptor instanceof InstantMessengerAdaptor) { matchingAdaptor = (InstantMessengerAdaptor) adaptor; } } if (matchingAdaptor == null) { throw new IllegalStateException("You must set WOAdditionalAdaptors=({WOAdaptor=\"er.imadaptor.InstantMessengerAdaptor\";})"); } return matchingAdaptor; } public InstantMessengerConnection setDefaultInstantMessenger(String screenName, String password) { _defaultScreenName = screenName; return _addInstantMessenger(screenName, password); } public InstantMessengerConnection addInstantMessenger(String screenName, String password) { return _addInstantMessenger(screenName, password); } public InstantMessengerConnection _addInstantMessenger(String screenName, String password) { InstantMessengerConnection existingConnection = _instantMessengers.get(screenName); if (existingConnection != null) { existingConnection.disconnect(); } InstantMessengerConnection connection = new InstantMessengerConnection(screenName, password, _factory); _instantMessengers.put(screenName, connection); if (_running && _autoLogin) { connection.connect(this); } return connection; } public void _removeInstantMessengerConnection(InstantMessengerConnection connection) { connection.disconnect(); _instantMessengers.remove(connection.instantMessenger().getScreenName()); } public void removeInstantMessenger(String screenName) { InstantMessengerConnection existingConnection = _instantMessengers.remove(screenName); if (existingConnection != null) { existingConnection.disconnect(); } } public IInstantMessenger instantMessengerForScreenName(String screenName) { IInstantMessenger instantMessenger = null; InstantMessengerConnection connection = _instantMessengerConnectionNamed(screenName); if (connection != null) { instantMessenger = connection.instantMessenger(); } return instantMessenger; } public IInstantMessenger defaultInstantMessenger() { return instantMessengerForScreenName(_defaultScreenName); } public static boolean isIMRequest(WOContext context) { return InstantMessengerAdaptor.isIMRequest(context.request()); } public static boolean isIMRequest(WORequest request) { NSDictionary userInfo = request.userInfo(); return (userInfo != null && userInfo.objectForKey(InstantMessengerAdaptor.IS_IM_KEY) != null); } public static String message(WORequest request) { return (String) request.userInfo().objectForKey(InstantMessengerAdaptor.MESSAGE_KEY); } public static String rawMessage(WORequest request) { return (String) request.userInfo().objectForKey(InstantMessengerAdaptor.RAW_MESSAGE_KEY); } public static String buddyName(WORequest request) { return (String) request.userInfo().objectForKey(InstantMessengerAdaptor.BUDDY_NAME_KEY); } public static Conversation conversation(WORequest request) { NSDictionary userInfo = request.userInfo(); Conversation conversation = null; if (userInfo != null) { conversation = (Conversation) userInfo.objectForKey(InstantMessengerAdaptor.CONVERSATION_KEY); } return conversation; } @Override public void registerForEvents() { if (_autoLogin) { Iterator instantMessengerIter = _instantMessengers.entrySet().iterator(); while (instantMessengerIter.hasNext()) { Map.Entry instantMessengerEntry = (Map.Entry) instantMessengerIter.next(); // String screenName = (String) instantMessengerEntry.getKey(); InstantMessengerConnection connection = (InstantMessengerConnection) instantMessengerEntry.getValue(); connection.connect(this); } } _running = true; Thread conversationExpiration = new Thread(new ConversationExpirationRunnable()); conversationExpiration.start(); } @Override public void unregisterForEvents() { _running = false; Iterator instantMessengerIter = _instantMessengers.entrySet().iterator(); while (instantMessengerIter.hasNext()) { Map.Entry instantMessengerEntry = (Map.Entry) instantMessengerIter.next(); // String screenName = (String) instantMessengerEntry.getKey(); InstantMessengerConnection connection = (InstantMessengerConnection) instantMessengerEntry.getValue(); connection.disconnect(); instantMessengerIter.remove(); } } @Override public boolean dispatchesRequestsConcurrently() { return true; } public synchronized void messageReceived(IInstantMessenger instantMessenger, String buddyName, String rawMessage) { if (log.isInfoEnabled()) { log.info("Received message from '" + buddyName + "': " + rawMessage); } String screenName = instantMessenger.getScreenName(); String message = rawMessage; if (message != null) { message = message.replaceAll("<[^>]+>", ""); message = message.trim(); } StringBuilder uri = new StringBuilder(); Conversation conversation = _instantMessengerConnectionNamed(screenName).conversationForBuddyNamed(buddyName, _conversationTimeout); String requestUrl = conversation.requestUrl(); if (requestUrl == null) { String cgiAdaptorURL = _application.cgiAdaptorURL(); WODynamicURL imConversationUrl = new WODynamicURL(); int j = cgiAdaptorURL.indexOf("//"); int i = 0; if (j > 0 && cgiAdaptorURL.length() - j > 2) { i = cgiAdaptorURL.indexOf('/', j + 2); } if (i > 0) { imConversationUrl.setPrefix(cgiAdaptorURL.substring(i)); } else { imConversationUrl.setPrefix(_application.applicationBaseURL()); } imConversationUrl.setRequestHandlerKey(_application.directActionRequestHandlerKey()); imConversationUrl.setApplicationName(_application.name()); imConversationUrl.setApplicationNumber(_application.number()); imConversationUrl.setRequestHandlerPath(_conversationActionName); uri.append(imConversationUrl.toString()); } else { uri.append(requestUrl); } uri.append('?'); uri.append(InstantMessengerAdaptor.BUDDY_NAME_KEY); uri.append('='); uri.append(WOURLEncoder.encode(buddyName)); uri.append('&'); uri.append(InstantMessengerAdaptor.MESSAGE_KEY); uri.append('='); uri.append(WOURLEncoder.encode(message)); uri.append('&'); uri.append(InstantMessengerAdaptor.RAW_MESSAGE_KEY); uri.append('='); uri.append(WOURLEncoder.encode(rawMessage)); String sessionID = conversation.sessionID(); if (sessionID != null) { uri.append('&'); uri.append(WOApplication.application().sessionIdKey()); uri.append('='); uri.append(sessionID); } NSMutableDictionary headers = new NSMutableDictionary(); NSMutableDictionary userInfo = new NSMutableDictionary(); userInfo.setObjectForKey(Boolean.TRUE, InstantMessengerAdaptor.IS_IM_KEY); userInfo.setObjectForKey(screenName, InstantMessengerAdaptor.SCREEN_NAME_KEY); userInfo.setObjectForKey(buddyName, InstantMessengerAdaptor.BUDDY_NAME_KEY); userInfo.setObjectForKey(message, InstantMessengerAdaptor.MESSAGE_KEY); userInfo.setObjectForKey(rawMessage, InstantMessengerAdaptor.RAW_MESSAGE_KEY); userInfo.setObjectForKey(conversation, InstantMessengerAdaptor.CONVERSATION_KEY); WORequest request = _application.createRequest("GET", uri.toString(), "HTTP/1.0", headers, null, userInfo); WOResponse response; try { response = _application.dispatchRequest(request); // String newSessionID = request.sessionID(); // if (newSessionID != null) { // conversation.setSessionID(newSessionID); // } if (response != null) { String nextRequestUrl = response.headerForKey(InstantMessengerAdaptor.IM_ACTION_URL_KEY); conversation.setRequestUrl(nextRequestUrl); String responseMessage = response.contentString(); if (responseMessage != null) { responseMessage = responseMessage.trim(); } if (responseMessage != null && responseMessage.length() > 0) { if (log.isInfoEnabled()) { log.info("Sending message to '" + buddyName + "': " + responseMessage); } sendMessage(screenName, buddyName, responseMessage, true); } } } catch (Throwable t) { InstantMessengerAdaptor.log.error(toString() + " Exception occurred while responding to client: " + t.toString(), t); } NSDelayedCallbackCenter.defaultCenter().eventEnded(); } public void sessionDidCreate(NSNotification notification) { WOSession session = (WOSession) notification.object(); WORequest request = session.context().request(); Conversation conversation = InstantMessengerAdaptor.conversation(request); if (conversation != null) { conversation.setSessionID(session.sessionID()); } } public void sendMessage(String screenName, String buddyName, String message, boolean block) throws MessageException { if (_centralizeScreenName != null) { log.warn("IM's are centralized; replacing '" + buddyName + "' with '" + _centralizeScreenName + "'"); buddyName = _centralizeScreenName; } IInstantMessenger instantMessenger = instantMessengerForScreenName(screenName); if (instantMessenger == null) { log.error("There is no connection for the screen name '" + screenName + "'."); } else { instantMessenger.sendMessage(buddyName, message, true); } } public InstantMessengerConnection _instantMessengerConnectionNamed(String screenName) { return _instantMessengers.get(screenName); } public InstantMessengerConnection _defaultInstantMessengerConnection() { return _instantMessengerConnectionNamed(_defaultScreenName); } protected IInstantMessengerFactory getFactory(String key) { String factoryClass = ERXProperties.stringForKey(key); try { IInstantMessengerFactory factory; if (factoryClass == null) { factory = new JOscarInstantMessenger.Factory(); } else { factory = (IInstantMessengerFactory) Class.forName(factoryClass).newInstance(); } return factory; } catch (Throwable t) { throw new RuntimeException("Invalid InstantMessengerFactory: " + factoryClass, t); } } protected void removeExpiredConversations() { Iterator instantMessengerIter = _instantMessengers.entrySet().iterator(); while (instantMessengerIter.hasNext()) { Map.Entry instantMessengerEntry = (Map.Entry) instantMessengerIter.next(); // String screenName = (String) instantMessengerEntry.getKey(); InstantMessengerConnection connection = (InstantMessengerConnection) instantMessengerEntry.getValue(); connection.removeExpiredConversations(_conversationTimeout); } } protected class ConversationExpirationRunnable implements Runnable { public void run() { while (_running) { try { Thread.sleep(1000 * 60); } catch (InterruptedException t) { // ignore } removeExpiredConversations(); } } } }