org.jwebsocket.plugins.twitter.TwitterPlugIn.java Source code

Java tutorial

Introduction

Here is the source code for org.jwebsocket.plugins.twitter.TwitterPlugIn.java

Source

//   ---------------------------------------------------------------------------
//   jWebSocket Twitter Plug-In (Community Edition, CE)
//   ---------------------------------------------------------------------------
//   Copyright 2010-2015 Innotrade GmbH (jWebSocket.org)
//   Alexander Schulze, Germany (NRW)
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//   ---------------------------------------------------------------------------
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.JWebSocketCommonConstants;
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 org.jwebsocket.util.Tools;
import org.springframework.context.ApplicationContext;
import twitter4j.*;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

/**
 *
 * @author Alexander Schulze 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 final Logger mLog = Logging.getLogger();
    // if namespace changed update client plug-in accordingly!
    private static final String NS_TWITTER = JWebSocketServerConstants.NS_BASE + ".plugins.twitter";
    private final static String VERSION = "1.0.0";
    private final static String VENDOR = JWebSocketCommonConstants.VENDOR_CE;
    private final static String LABEL = "jWebSocket TwitterPlugIn";
    private final static String COPYRIGHT = JWebSocketCommonConstants.COPYRIGHT_CE;
    private final static String LICENSE = JWebSocketCommonConstants.LICENSE_CE;
    private final static String DESCRIPTION = "jWebSocket TwitterPlugIn - Community Edition";
    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";
    // the global Twitter object is used for general, non user specific information
    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;
    private static ApplicationContext mBeanFactory;
    private static Settings mSettings;
    // the twitter facttory can be shared to generate multiple twitter object, one per user for streams and tweeting
    private static TwitterFactory mTwitterFactory;

    /**
     *
     * @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);

        try {
            mBeanFactory = getConfigBeanFactory();
            if (null == mBeanFactory) {
                mLog.error(
                        "No or invalid spring configuration for Twitter plug-in, some features may not be available.");
            } else {
                mSettings = (Settings) mBeanFactory.getBean("org.jwebsocket.plugins.twitter.settings");
                if (mLog.isInfoEnabled()) {
                    mLog.info("Twitter plug-in successfully instantiated.");
                }
            }
        } catch (Exception lEx) {
            mLog.error(Logging.getSimpleExceptionMessage(lEx, "instantiating Twitter plug-in"));
        }

    }

    @Override
    public String getVersion() {
        return VERSION;
    }

    @Override
    public String getLabel() {
        return LABEL;
    }

    @Override
    public String getDescription() {
        return DESCRIPTION;
    }

    @Override
    public String getVendor() {
        return VENDOR;
    }

    @Override
    public String getCopyright() {
        return COPYRIGHT;
    }

    @Override
    public String getLicense() {
        return LICENSE;
    }

    @Override
    public String getNamespace() {
        return NS_TWITTER;
    }

    @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 (mTwitterFactory == null) {
                if (mLog.isDebugEnabled()) {
                    mLog.debug("Authenticating against Twitter...");
                }
                // The factory instance is re-useable and thread safe.
                /*
                 ConfigurationBuilder lCB = new ConfigurationBuilder();
                 lCB.setDebugEnabled(true);
                 lCB.setOAuthConsumerKey(mSettings.getConsumerKey());
                 lCB.setOAuthConsumerSecret(mSettings.getConsumerSecret());
                 lCB.setOAuthAccessToken(mSettings.getAccessKey());
                 lCB.setOAuthAccessTokenSecret(mSettings.getAccessSecret());
                 mTwitterFactory = new TwitterFactory(lCB.build());
                 */
                mTwitterFactory = new TwitterFactory();
                mTwitter = mTwitterFactory.getInstance();
                // mTwitter.setOAuthConsumer(mSettings.getConsumerKey(), mSettings.getConsumerSecret());

                // AccessToken lAccessToken = new AccessToken(mSettings.getAccessKey(), mSettings.getAccessSecret());
                // 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 {
                // get a new Twitter object for the user
                TwitterFactory lTwitterFactory = new TwitterFactory();
                Twitter lTwitter = lTwitterFactory.getInstance();
                lTwitter.setOAuthConsumer(mSettings.getConsumerKey(), mSettings.getConsumerSecret());

                // pass callback URL to Twitter API if given
                RequestToken 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(mSettings.getConsumerKey(),
                 * mSettings.getConsumerSecret()); // 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;
            // 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.setLong("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...
                Map<String, List<String>> lAsOf = new FastMap<String, List<String>>();
                List<String> lMessages;
                ResponseList lTrendList = mTwitter.getDailyTrends();
                for (int lIdx = 0; lIdx < lTrendList.size(); lIdx++) {
                    lMessages = new FastList<String>();
                    Trends lTrends = (Trends) lTrendList.get(lIdx);
                    Trend[] lTrendArray = lTrends.getTrends();
                    for (Trend lTrend : lTrendArray) {
                        lMessages.add(lTrend.getName() + ": " + lTrend.getQuery() + ", URL: " + lTrend.getUrl());
                    }
                    lAsOf.put(Tools.DateToISO8601(lTrends.getTrendAt()), lMessages);
                }
                lResponse.setMap("messages", lAsOf);
                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);

        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.setLong("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.setLong("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);
            }
        }

        @Override
        public void onScrubGeo(long aArg0, long aArg1) {
            // not required for now
        }
    };

    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 long[] {}, lKeywordArray);
            // if no TwitterStream object created up to now, create one...
            if (mTwitterStream == null) {
                mTwitterStream = new TwitterStreamFactory().getInstance();
                mTwitterStream.addListener(mTwitterStreamListener);
                mTwitterStream.setOAuthConsumer(mSettings.getConsumerKey(), mSettings.getConsumerSecret());
                AccessToken lAccessToken = new AccessToken(mSettings.getAccessKey(), mSettings.getAccessSecret());
                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);
    }
}