// --------------------------------------------------------------------------- // jWebSocket - jWebSocket Twitter Plug-In // Copyright (c) 2010 Innotrade GmbH, jWebSocket.org // --------------------------------------------------------------------------- // THIS CODE IS FOR RESEARCH, EVALUATION AND TEST PURPOSES ONLY! // THIS CODE MAY BE SUBJECT TO CHANGES WITHOUT ANY NOTIFICATION! // --------------------------------------------------------------------------- // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by the // Free Software Foundation; either version 3 of the License, or (at your // option) any later version. // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for // more details. // You should have received a copy of the GNU Lesser General Public License along // with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>. // --------------------------------------------------------------------------- package org.jwebsocket.plugins.twitter; import java.net.URL; import java.util.List; import java.util.Map; import java.util.Set; import javolution.util.FastList; import javolution.util.FastMap; import javolution.util.FastSet; import org.apache.log4j.Logger; import org.jwebsocket.api.PluginConfiguration; import org.jwebsocket.api.WebSocketConnector; import org.jwebsocket.api.WebSocketEngine; import org.jwebsocket.config.JWebSocketServerConstants; import org.jwebsocket.kit.CloseReason; import org.jwebsocket.kit.PlugInResponse; import org.jwebsocket.logging.Logging; import org.jwebsocket.plugins.TokenPlugIn; import org.jwebsocket.server.TokenServer; import org.jwebsocket.token.Token; import org.jwebsocket.token.TokenFactory; import twitter4j.FilterQuery; import twitter4j.Query; import twitter4j.QueryResult; import twitter4j.ResponseList; import twitter4j.Status; import twitter4j.StatusDeletionNotice; import twitter4j.StatusListener; import twitter4j.Trend; import twitter4j.Trends; import twitter4j.Tweet; import twitter4j.Twitter; import twitter4j.TwitterFactory; import twitter4j.TwitterStream; import twitter4j.TwitterStreamFactory; import twitter4j.User; import twitter4j.http.AccessToken; import twitter4j.http.RequestToken; /** * * @author aschulze * logout see: http://stackoverflow.com/questions/1960957/twitter-api-logout * http://groups.google.com/group/twitter4j/browse_thread/thread/5957722d596e269c/c2956d43a46b31b5?lnk=gst&q=stateless */ public class TwitterPlugIn extends TokenPlugIn { private static Logger mLog = Logging.getLogger(TwitterPlugIn.class); private static final String TWITTER_VAR = "$twitter"; private static final String OAUTH_REQUEST_TOKEN = "$twUsrReqTok"; private static final String OAUTH_VERIFIER = "$twUsrVerifier"; private static final String TWITTER_STREAM = "$twStream"; private static String CONSUMER_KEY = null; private static final String CONSUMER_KEY_KEY = "consumer_key"; private static String CONSUMER_SECRET = null; private static final String CONSUMER_SECRET_KEY = "consumer_secret"; private static Integer APP_ID = null; private static final String APP_ID_KEY = "app_id"; private static String ACCESSTOKEN_KEY = null; private static final String ACCESSTOKEN_KEY_KEY = "accesstoken_key"; private static String ACCESSTOKEN_SECRET = null; private static final String ACCESSTOKEN_SECRET_KEY = "accesstoken_secret"; // if namespace changed update client plug-in accordingly! private static final String NS_TWITTER = JWebSocketServerConstants.NS_BASE + ".plugins.twitter"; private Twitter mTwitter = null; private final static int MAX_STREAM_KEYWORDS_PER_CONNECTION = 5; private final static int MAX_STREAM_KEYWORDS_TOTAL = 50; private static int mStatsMaxConnectors = 0; private static int mStatsMaxKeywords = 0; private TwitterStream mTwitterStream = null; // connections registered to the twitter stream, // list contains the keywords per connection private Map<WebSocketConnector, Set<String>> mConnectors = new FastMap<WebSocketConnector, Set<String>>(); // for every keyword the connectors are counted, // if connectors are 0 the keyword gets deleted private Map<String, Integer> mKeywords = new FastMap<String, Integer>(); private TokenServer mServer = null; /** * */ public TwitterPlugIn() { super(null); } /** * * @param aConfiguration */ public TwitterPlugIn(PluginConfiguration aConfiguration) { super(aConfiguration); if (mLog.isDebugEnabled()) { mLog.debug("Instantiating Twitter plug-in..."); } // specify default name space for admin plugin this.setNamespace(NS_TWITTER); mGetSettings(); } private void mGetSettings() { CONSUMER_KEY = getSetting(CONSUMER_KEY_KEY, null); CONSUMER_SECRET = getSetting(CONSUMER_SECRET_KEY, null); try { APP_ID = Integer.parseInt(getSetting(APP_ID_KEY, "0")); } catch (Exception lEx) { APP_ID = 0; } ACCESSTOKEN_KEY = getSetting(ACCESSTOKEN_KEY_KEY, null); ACCESSTOKEN_SECRET = getSetting(ACCESSTOKEN_SECRET_KEY, null); } @Override public void processToken(PlugInResponse aResponse, WebSocketConnector aConnector, Token aToken) { String lType = aToken.getType(); String lNS = aToken.getNS(); if (lType != null && getNamespace().equals(lNS)) { if (lType.equals("tweet")) { tweet(aConnector, aToken); } else if (lType.equals("requestAccessToken")) { requestAccessToken(aConnector, aToken); } else if (lType.equals("login")) { login(aConnector, aToken); } else if (lType.equals("logout")) { logout(aConnector, aToken); } else if (lType.equals("getTimeline")) { getTimeline(aConnector, aToken); } else if (lType.equals("getStatistics")) { getStatistics(aConnector, aToken); } else if (lType.equals("getTrends")) { getTrends(aConnector, aToken); } else if (lType.equals("getPublicTimeline")) { getPublicTimeline(aConnector, aToken); } else if (lType.equals("query")) { query(aConnector, aToken); } else if (lType.equals("getUserData")) { getUserData(aConnector, aToken); } else if (lType.equals("setVerifier")) { setVerifier(aConnector, aToken); } else if (lType.equals("setStream")) { setStream(aConnector, aToken); } } } /** * * @param aConnector */ @Override public void connectorStopped(WebSocketConnector aConnector, CloseReason aCloseReason) { mRemoveConnector(aConnector); aConnector.removeVar(TWITTER_VAR); } @Override public void engineStopped(WebSocketEngine aEngine) { super.engineStopped(aEngine); // stop Twitter stream if used for this connection mStopStream(); } private boolean mCheckAuth(Token aToken) { String lMsg; try { if (mTwitter == null) { if (mLog.isDebugEnabled()) { mLog.debug("Authenticating against Twitter..."); } // The factory instance is re-useable and thread safe. TwitterFactory lTwitterFactory = new TwitterFactory(); mTwitter = lTwitterFactory.getInstance(); mTwitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); AccessToken lAccessToken = new AccessToken(ACCESSTOKEN_KEY, ACCESSTOKEN_SECRET); mTwitter.setOAuthAccessToken(lAccessToken); lMsg = "Successfully authenticated against Twitter."; } else { lMsg = "Already authenticated against Twitter."; } if (mLog.isInfoEnabled()) { mLog.info(lMsg); } return true; } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); aToken.setInteger("code", -1); aToken.setString("msg", lMsg); mLog.error(lMsg); } return false; } private void requestAccessToken(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; String lCallbackURL = aToken.getString("callbackURL"); try { if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { TwitterFactory lTwitterFactory = new TwitterFactory(); Twitter lTwitter = lTwitterFactory.getInstance(); lTwitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); // pass callback URL to Twitter API if given RequestToken lReqToken; lReqToken = (lCallbackURL != null ? lTwitter.getOAuthRequestToken(lCallbackURL) : lTwitter.getOAuthRequestToken()); String lAuthenticationURL = lReqToken.getAuthenticationURL(); String lAuthorizationURL = lReqToken.getAuthorizationURL(); lResponse.setString("authenticationURL", lAuthenticationURL); lResponse.setString("authorizationURL", lAuthorizationURL); lMsg = "authenticationURL: " + lAuthenticationURL + ", authorizationURL: " + lAuthorizationURL; lResponse.setString("msg", lMsg); if (mLog.isInfoEnabled()) { mLog.info(lMsg); } aConnector.setVar(OAUTH_REQUEST_TOKEN, lReqToken); aConnector.setVar(TWITTER_VAR, lTwitter); } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } /** * is called by the jWebSocket OAuth confirmation window to pass the * OAuth AccessToken Verifier from the Browser to the Server so that * the server is able to run Twitter API commands w/o knowing the user's * credentials. * @param aConnector * @param aToken */ private void setVerifier(WebSocketConnector aConnector, Token aToken) { // TokenServer lServer = getServer(); // instantiate response token String lVerifier = aToken.getString("verifier"); aConnector.setString(OAUTH_VERIFIER, lVerifier); } private void login(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; try { if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { /* TwitterFactory lTwitterFactory = new TwitterFactory(); Twitter lTwitter = lTwitterFactory.getInstance(); lTwitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); // pass callback URL to Twitter API RequestToken lReqToken = lTwitter.getOAuthRequestToken("http://localhost/demos/twitter/twauth.htm?isAuth=true"); String lAuthenticationURL = lReqToken.getAuthenticationURL(); String lAuthorizationURL = lReqToken.getAuthorizationURL(); lResponse.setString("authenticationURL", lAuthenticationURL); lResponse.setString("authorizationURL", lAuthorizationURL); lMsg = "authenticationURL: " + lAuthenticationURL + ", authorizationURL: " + lAuthorizationURL; lResponse.setString("msg", lMsg); if (mLog.isInfoEnabled()) { mLog.info(lMsg); } // every connector maintains it's own twitter connection aConnector.setVar(TWITTER_VAR, lTwitter); // persist the request token, it's required // to get access token from verifier aConnector.setVar(OAUTH_REQUEST_TOKEN, lReqToken); */ } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void logout(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg; try { if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { Twitter lTwitter = (Twitter) aConnector.getVar(TWITTER_VAR); if (lTwitter != null) { lTwitter.shutdown(); lResponse.setString("msg", "Twitter instance has been shut down."); } else { lResponse.setString("msg", "Twitter instance down (not up before)."); } aConnector.removeVar(TWITTER_VAR); aConnector.removeVar(OAUTH_REQUEST_TOKEN); aConnector.removeVar(OAUTH_VERIFIER); } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } /** * Gets the Twitter timeline for a given user. If no user is given * the user registered for the app is used as default. */ private void getTimeline(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; String lUsername = aToken.getString("username"); try { if (mLog.isDebugEnabled()) { mLog.debug("Receiving timeline for user '" + (lUsername != null ? lUsername : "[not given]") + "'..."); } if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { List<Status> lStatuses; // getting timelines is public so we can use the mTwitter object here if (lUsername != null && lUsername.length() > 0) { lStatuses = mTwitter.getUserTimeline(lUsername); } else { lStatuses = mTwitter.getUserTimeline(); } // return the list of messages as an array of strings... List<String> lMessages = new FastList<String>(); for (Status lStatus : lStatuses) { lMessages.add(lStatus.getUser().getName() + ": " + lStatus.getText()); /* // If each status is supposed to be sent separately... Token lItem = TokenFactory.createToken(NS_TWITTER, BaseToken.TT_EVENT); lItem.setString("username", lStatus.getUser().getName()); lItem.setString("message", lStatus.getText()); lServer.sendToken(aConnector, lItem); */ } lResponse.setList("messages", lMessages); if (mLog.isInfoEnabled()) { mLog.info("Twitter timeline for user '" + (lUsername != null ? lUsername : "[not given]") + "' successfully received"); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void getUserData(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lUsername = aToken.getString("username"); Integer lUserId = aToken.getInteger("userid"); try { User lUser = null; // if user id is given use this to get user data if (lUserId != null && lUserId != 0) { lUser = mTwitter.showUser(lUserId); // if user name is given use this to get user data } else if (lUsername != null && lUsername.length() > 0) { lUser = mTwitter.showUser(lUsername); // otherwise return user data of provider (ourselves) } else { lUser = mTwitter.verifyCredentials(); } if (lUser != null) { lResponse.setString("screenname", lUser.getScreenName()); lResponse.setInteger("id", lUser.getId()); lResponse.setString("description", lUser.getDescription()); lResponse.setString("location", lUser.getLocation()); lResponse.setString("lang", lUser.getLang()); lResponse.setString("name", lUser.getName()); } else { lResponse.setInteger("code", -1); lResponse.setString("msg", "Neither UserId nor Username passed."); } } catch (Exception lEx) { String lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } /** * posts a Twitter message on behalf of a OAtuh authenticated * user by using the retrieved AccessToken and its verifier. * @param aConnector * @param aToken */ private void tweet(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = aToken.getString("message"); try { // to send tweet we need an authenticated user Twitter lTwitter = (Twitter) aConnector.getVar(TWITTER_VAR); RequestToken lReqToken = (RequestToken) aConnector.getVar(OAUTH_REQUEST_TOKEN); String lVerifier = aConnector.getString(OAUTH_VERIFIER); if (lTwitter == null) { lResponse.setInteger("code", -1); lResponse.setString("msg", "Not yet authenticated against Twitter!"); } else if (lReqToken == null) { lResponse.setInteger("code", -1); lResponse.setString("msg", "No Access Token available!"); } else if (lVerifier == null) { lResponse.setInteger("code", -1); lResponse.setString("msg", "No Access Verifier available!"); } else if (lMsg == null || lMsg.length() <= 0) { lResponse.setInteger("code", -1); lResponse.setString("msg", "No message passed for tweet."); } else { AccessToken lAccessToken = lTwitter.getOAuthAccessToken(lReqToken, lVerifier); lTwitter.setOAuthAccessToken(lAccessToken); lTwitter.updateStatus(lMsg); lMsg = "Twitter status successfully updated for user '" + lTwitter.getScreenName() + "'."; lResponse.setString("msg", lMsg); if (mLog.isInfoEnabled()) { mLog.info(lMsg); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void query(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; String lQuery = aToken.getString("query"); try { if (mLog.isDebugEnabled()) { mLog.debug("Searching for '" + (lQuery != null ? lQuery : "[not given]") + "'..."); } if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { // return the list of messages as an array of strings... List<String> lMessages = new FastList<String>(); QueryResult lQueryRes; // getting timelines is public so we can use the mTwitter object here if (lQuery != null && lQuery.length() > 0) { Query lTwQuery = new Query(lQuery); lQueryRes = mTwitter.search(lTwQuery); List<Tweet> lTweets = lQueryRes.getTweets(); for (Tweet lTweet : lTweets) { lMessages.add(lTweet.getText()); } lResponse.setList("messages", lMessages); if (mLog.isInfoEnabled()) { mLog.info("Tweets for query '" + (lQuery != null ? lQuery : "[not given]") + "' successfully received"); } } else { lMsg = "No query string given"; mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void getTrends(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; try { if (mLog.isDebugEnabled()) { mLog.debug("Retreiving trends..."); } if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { // return the list of messages as an array of strings... List<String> lMessages = new FastList<String>(); Trends lTrends = mTwitter.getCurrentTrends(); Trend[] lTrendArray = lTrends.getTrends(); for (Trend lTrend : lTrendArray) { lMessages.add(lTrend.getName() + ": " + lTrend.getQuery() + ", URL: " + lTrend.getUrl()); } lResponse.setList("messages", lMessages); if (mLog.isInfoEnabled()) { mLog.info("Trends successfully received"); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } private void getStatistics(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; if (mLog.isDebugEnabled()) { mLog.debug("Retreiving statistics..."); } lResponse.setInteger("listenerCount", mConnectors.size()); lResponse.setInteger("keywordCount", mKeywords.size()); lResponse.setString("keywords", mKeywords.toString()); lResponse.setInteger("listenerMax", mStatsMaxConnectors); lResponse.setInteger("keywordMax", mStatsMaxKeywords); // send response to requester lServer.sendToken(aConnector, lResponse); } private void getPublicTimeline(WebSocketConnector aConnector, Token aToken) { TokenServer lServer = getServer(); // instantiate response token Token lResponse = lServer.createResponse(aToken); String lMsg = ""; try { if (mLog.isDebugEnabled()) { mLog.debug("Retreiving public timeline..."); } if (!mCheckAuth(lResponse)) { mLog.error(lResponse.getString("msg")); } else { // return the list of messages as an array of strings... List<String> lMessages = new FastList<String>(); ResponseList lRespList = mTwitter.getPublicTimeline(); for (int lIdx = 0; lIdx < lRespList.size(); lIdx++) { Status lStatus = (Status) lRespList.get(lIdx); lMessages.add(lStatus.getText()); } lResponse.setList("messages", lMessages); if (mLog.isInfoEnabled()) { mLog.info("Public timeline successfully received"); } } } catch (Exception lEx) { lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester lServer.sendToken(aConnector, lResponse); } StatusListener mTwitterStreamListener = new StatusListener() { @Override public void onStatus(Status aStatus) { Token lToken = TokenFactory.createToken(NS_TWITTER, "event"); lToken.setString("name", "status"); String lStatus = aStatus.getText(); lToken.setString("status", lStatus); User lUser = aStatus.getUser(); if (lUser != null) { lToken.setString("userName", lUser.getName()); lToken.setInteger("userId", lUser.getId()); URL lURL = lUser.getURL(); if (lURL != null) { lToken.setString("userURL", lURL.toString()); } else { lToken.setString("userURL", ""); } lURL = lUser.getProfileImageURL(); if (lURL != null) { lToken.setString("userImgURL", lURL.toString()); } else { lToken.setString("userImgURL", ""); } lToken.setString("userBckImgURL", lUser.getProfileBackgroundImageUrl()); lToken.setString("userBckCol", lUser.getProfileBackgroundColor()); } else { lToken.setString("userName", ""); lToken.setInteger("userId", -1); lToken.setString("userURL", ""); lToken.setString("userImgURL", ""); lToken.setString("userBckImgURL", ""); lToken.setString("userBckCol", ""); } if (null != lStatus) { lStatus = lStatus.toLowerCase(); for (WebSocketConnector lConnector : mConnectors.keySet()) { Set<String> lKeywords = mConnectors.get(lConnector); boolean lMatch = false; for (String lKeyword : lKeywords) { lMatch = lStatus.indexOf(lKeyword) >= 0; if (lMatch) { break; } } if (lMatch) { mServer.sendToken(lConnector, lToken); } } } } @Override public void onDeletionNotice(StatusDeletionNotice aStatusDeletionNotice) { Token lToken = TokenFactory.createToken(NS_TWITTER, "event"); lToken.setString("name", "deletion"); lToken.setInteger("userId", aStatusDeletionNotice.getUserId()); lToken.setInteger("statusId", (int) aStatusDeletionNotice.getStatusId()); for (WebSocketConnector lConnector : mConnectors.keySet()) { mServer.sendToken(lConnector, lToken); } } @Override public void onTrackLimitationNotice(int aNumberOfLimitedStatuses) { Token lToken = TokenFactory.createToken(NS_TWITTER, "event"); lToken.setString("name", "trackLimit"); lToken.setInteger("limit", aNumberOfLimitedStatuses); for (WebSocketConnector lConnector : mConnectors.keySet()) { mServer.sendToken(lConnector, lToken); } } @Override public void onException(Exception aEx) { Token lToken = TokenFactory.createToken(NS_TWITTER, "event"); lToken.setString("name", "exception"); lToken.setString("message", aEx.getMessage()); lToken.setString("exception", aEx.getClass().getSimpleName()); for (WebSocketConnector lConnector : mConnectors.keySet()) { mServer.sendToken(lConnector, lToken); } } }; private void mAddConnector(WebSocketConnector aConnector, Set<String> aKeywords) { if (null == aConnector || null == aKeywords) { return; } // put the connector as a listener to lthe list mConnectors.put(aConnector, aKeywords); // increment counts for the keyowrds list for (String lKeyword : aKeywords) { Integer lCount = mKeywords.get(lKeyword); if (null == lCount) { mKeywords.put(lKeyword, 1); } else { mKeywords.put(lKeyword, lCount + 1); } } if (mConnectors.size() > mStatsMaxConnectors) { mStatsMaxConnectors = mConnectors.size(); } } private void mRemoveConnector(WebSocketConnector aConnector) { if (null == aConnector) { return; } Set<String> lKeywords = mConnectors.get(aConnector); if (null == lKeywords) { return; } for (String lKeyword : lKeywords) { Integer lCount = mKeywords.get(lKeyword); if (null == lCount || 1 == lCount) { mKeywords.remove(lKeyword); } else { mKeywords.put(lKeyword, lCount - 1); } } mConnectors.remove(aConnector); } private void mUpdateStream(Token aToken) { try { String[] lKeywordArray = new String[mKeywords.size()]; int lIdx = 0; for (String lKeyword : mKeywords.keySet()) { lKeywordArray[lIdx] = lKeyword; lIdx++; if (lIdx >= MAX_STREAM_KEYWORDS_TOTAL) { break; } } if (lIdx > mStatsMaxKeywords) { mStatsMaxKeywords = lIdx; } FilterQuery lFilter = new FilterQuery( 0, new int[]{}, lKeywordArray); // if no TwitterStream object created up to now, create one... if (mTwitterStream == null) { mTwitterStream = new TwitterStreamFactory(mTwitterStreamListener).getInstance(); mTwitterStream.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); AccessToken lAccessToken = new AccessToken(ACCESSTOKEN_KEY, ACCESSTOKEN_SECRET); mTwitterStream.setOAuthAccessToken(lAccessToken); } // apply the filter to the stream object mTwitterStream.filter(lFilter); } catch (Exception lEx) { String lMsg = lEx.getClass().getSimpleName() + ": " + lEx.getMessage(); mLog.error(lMsg); aToken.setInteger("code", -1); aToken.setString("msg", lMsg); } } private void mStopStream() { // if (still) some stream is allocated shut it down and release it! if (mTwitterStream != null) { if (mLog.isDebugEnabled()) { mLog.debug("Cleaning up Twitter stream..."); } mTwitterStream.cleanUp(); if (mLog.isDebugEnabled()) { mLog.debug("Shutting down Twitter stream..."); } mTwitterStream.shutdown(); } } private void setStream(final WebSocketConnector aConnector, Token aToken) { if (null == mServer) { mServer = getServer(); } // instantiate response token Token lResponse = mServer.createResponse(aToken); String lMsg = ""; String lKeyWordString = aToken.getString("keywords"); String[] lKeyWordArray; Set<String> lKeywordSet = new FastSet<String>(); if (lKeyWordString != null) { lKeyWordArray = lKeyWordString.split(" "); int lAccepted = 0; for (int lIdx = 0, lCnt = lKeyWordArray.length; lIdx < lCnt && lAccepted < MAX_STREAM_KEYWORDS_PER_CONNECTION; lIdx++) { String lKeyword = lKeyWordArray[lIdx]; // validate keywords if (lKeyword != null && lKeyword.length() >= 4) { lKeywordSet.add(lKeyword.toLowerCase()); lAccepted++; } } mRemoveConnector(aConnector); if (lKeywordSet.size() > 0) { // remove existing connector from listener list // add connector with new keywords to listener list mAddConnector(aConnector, lKeywordSet); // and update the stream using ALL keywords from all clients mUpdateStream(aToken); } else { lMsg = "Twitter stream stopped."; lResponse.setString("msg", lMsg); } } else { lMsg = "No keywords argument passed, remaining in current state."; lResponse.setInteger("code", -1); lResponse.setString("msg", lMsg); } // send response to requester mServer.sendToken(aConnector, lResponse); } }