/* * Copyright 2007 Yusuke Yamamoto * * 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 twitter4j; import twitter4j.conf.Configuration; import twitter4j.internal.async.Dispatcher; import twitter4j.internal.http.HttpResponse; import twitter4j.internal.json.DataObjectFactoryUtil; import twitter4j.internal.json.z_T4JInternalFactory; import twitter4j.internal.json.z_T4JInternalJSONImplFactory; import twitter4j.internal.logging.Logger; import twitter4j.internal.org.json.JSONArray; import twitter4j.internal.org.json.JSONException; import twitter4j.internal.org.json.JSONObject; import twitter4j.json.JSONObjectType; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; /** * @author Yusuke Yamamoto - yusuke at mac.com * @since Twitter4J 2.1.8 */ abstract class StatusStreamBase implements StatusStream { protected static final Logger logger = Logger.getLogger(StatusStreamImpl.class); private boolean streamAlive = true; private BufferedReader br; private InputStream is; private HttpResponse response; protected final Dispatcher dispatcher; protected final Configuration CONF; protected z_T4JInternalFactory factory; /*package*/ StatusStreamBase(Dispatcher dispatcher, InputStream stream, Configuration conf) throws IOException { this.is = stream; this.br = new BufferedReader(new InputStreamReader(stream, "UTF-8")); this.dispatcher = dispatcher; this.CONF = conf; this.factory = new z_T4JInternalJSONImplFactory(conf); } /*package*/ StatusStreamBase(Dispatcher dispatcher, HttpResponse response, Configuration conf) throws IOException { this(dispatcher, response.asStream(), conf); this.response = response; } protected String parseLine(String line) { return line; } abstract class StreamEvent implements Runnable { String line; StreamEvent(String line) { this.line = line; } } protected void handleNextElement(final StreamListener[] listeners, final RawStreamListener[] rawStreamListeners) throws TwitterException { if (!streamAlive) { throw new IllegalStateException("Stream already closed."); } try { String line = br.readLine(); if (null == line) { //invalidate this status stream throw new IOException("the end of the stream has been reached"); } dispatcher.invokeLater(new StreamEvent(line) { public void run() { try { if (rawStreamListeners.length > 0) { onMessage(line, rawStreamListeners); } // SiteStreamsImpl will parse "forUser" attribute line = parseLine(line); if (line != null && line.length() > 0) { // parsing JSON is an expensive process and can be avoided when all listeners are instanceof RawStreamListener if (listeners.length > 0) { if (CONF.isJSONStoreEnabled()) { DataObjectFactoryUtil.clearThreadLocalMap(); } JSONObject json = new JSONObject(line); JSONObjectType.Type event = JSONObjectType.determine(json); if (logger.isDebugEnabled()) { logger.debug("Received:", CONF.isPrettyDebugEnabled() ? json.toString(1) : json.toString()); } switch (event) { case SENDER: onSender(json, listeners); break; case STATUS: onStatus(json, listeners); break; case DIRECT_MESSAGE: onDirectMessage(json, listeners); break; case DELETE: onDelete(json, listeners); break; case LIMIT: onLimit(json, listeners); break; case STALL_WARNING: onStallWarning(json, listeners); break; case SCRUB_GEO: onScrubGeo(json, listeners); break; case FRIENDS: onFriends(json, listeners); break; case FAVORITE: onFavorite(json.getJSONObject("source"), json.getJSONObject("target"), json.getJSONObject("target_object"), listeners); break; case UNFAVORITE: onUnfavorite(json.getJSONObject("source"), json.getJSONObject("target"), json.getJSONObject("target_object"), listeners); break; case FOLLOW: onFollow(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case UNFOLLOW: onUnfollow(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case USER_LIST_MEMBER_ADDED: onUserListMemberAddition(json.getJSONObject("target"), json.getJSONObject("source"), json.getJSONObject("target_object"), listeners); break; case USER_LIST_MEMBER_DELETED: onUserListMemberDeletion(json.getJSONObject("target"), json.getJSONObject("source"), json.getJSONObject("target_object"), listeners); break; case USER_LIST_SUBSCRIBED: onUserListSubscription(json.getJSONObject("source"), json.getJSONObject("target"), json.getJSONObject("target_object"), listeners); break; case USER_LIST_UNSUBSCRIBED: onUserListUnsubscription(json.getJSONObject("source"), json.getJSONObject("target"), json.getJSONObject("target_object"), listeners); break; case USER_LIST_CREATED: onUserListCreation(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case USER_LIST_UPDATED: onUserListUpdated(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case USER_LIST_DESTROYED: onUserListDestroyed(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case USER_UPDATE: onUserUpdate(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case BLOCK: onBlock(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case UNBLOCK: onUnblock(json.getJSONObject("source"), json.getJSONObject("target"), listeners); break; case DISCONNECTION: onDisconnectionNotice(line, listeners); break; case UNKNOWN: default: logger.warn("Received unknown event:", CONF.isPrettyDebugEnabled() ? json.toString(1) : json.toString()); } } } } catch (Exception ex) { onException(ex, listeners); } } }); } catch (IOException ioe) { try { is.close(); } catch (IOException ignore) { } boolean isUnexpectedException = streamAlive; streamAlive = false; if (isUnexpectedException) { throw new TwitterException("Stream closed.", ioe); } } } protected void onMessage(String rawString, RawStreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onMessage"); } protected void onSender(JSONObject json, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onSender"); } protected void onStatus(JSONObject json, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onStatus"); } protected void onDirectMessage(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onDirectMessage"); } protected void onDelete(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onDelete"); } protected void onLimit(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onLimit"); } protected void onStallWarning(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onStallWarning"); } protected void onScrubGeo(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onScrubGeo"); } protected void onFriends(JSONObject json, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onFriends"); } protected void onFavorite(JSONObject source, JSONObject target, JSONObject targetObject, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onFavorite"); } protected void onUnfavorite(JSONObject source, JSONObject target, JSONObject targetObject, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onUnfavorite"); } protected void onFollow(JSONObject source, JSONObject target, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onFollow"); } protected void onUnfollow(JSONObject source, JSONObject target, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onUnfollow"); } protected void onUserListMemberAddition(JSONObject addedMember, JSONObject owner, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListMemberAddition"); } protected void onUserListMemberDeletion(JSONObject deletedMember, JSONObject owner, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListMemberDeletion"); } protected void onUserListSubscription(JSONObject source, JSONObject owner, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListSubscription"); } protected void onUserListUnsubscription(JSONObject source, JSONObject owner, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListUnsubscription"); } protected void onUserListCreation(JSONObject source, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListCreation"); } protected void onUserListUpdated(JSONObject source, JSONObject userList, StreamListener[] listeners) throws TwitterException, JSONException { logger.warn("Unhandled event: onUserListUpdated"); } protected void onUserListDestroyed(JSONObject source, JSONObject userList, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onUserListDestroyed"); } protected void onUserUpdate(JSONObject source, JSONObject target, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onUserUpdate"); } protected void onBlock(JSONObject source, JSONObject target, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onBlock"); } protected void onUnblock(JSONObject source, JSONObject target, StreamListener[] listeners) throws TwitterException { logger.warn("Unhandled event: onUnblock"); } protected void onDisconnectionNotice(String line, StreamListener[] listeners) { logger.warn("Unhandled event: ", line); } protected void onException(Exception e, StreamListener[] listeners) { logger.warn("Unhandled event: ", e.getMessage()); } public void close() throws IOException { streamAlive = false; is.close(); br.close(); if (response != null) { response.disconnect(); } } protected Status asStatus(JSONObject json) throws TwitterException { Status status = factory.createStatus(json); if (CONF.isJSONStoreEnabled()) { DataObjectFactoryUtil.registerJSONObject(status, json); } return status; } protected DirectMessage asDirectMessage(JSONObject json) throws TwitterException { DirectMessage directMessage; try { directMessage = factory.createDirectMessage(json.getJSONObject("direct_message")); } catch (JSONException e) { throw new TwitterException(e); } if (CONF.isJSONStoreEnabled()) { DataObjectFactoryUtil.registerJSONObject(directMessage, json); } return directMessage; } protected long[] asFriendList(JSONObject json) throws TwitterException { JSONArray friends; try { friends = json.getJSONArray("friends"); long[] friendIds = new long[friends.length()]; for (int i = 0; i < friendIds.length; ++i) { friendIds[i] = Long.parseLong(friends.getString(i)); } return friendIds; } catch (JSONException e) { throw new TwitterException(e); } } protected User asUser(JSONObject json) throws TwitterException { User user = factory.createUser(json); if (CONF.isJSONStoreEnabled()) { DataObjectFactoryUtil.registerJSONObject(user, json); } return user; } protected UserList asUserList(JSONObject json) throws TwitterException { UserList userList = factory.createAUserList(json); if (CONF.isJSONStoreEnabled()) { DataObjectFactoryUtil.registerJSONObject(userList, json); } return userList; } public abstract void next(StatusListener listener) throws TwitterException; public abstract void next(StreamListener[] listeners, RawStreamListener[] rawStreamListeners) throws TwitterException; public void onException(Exception e, StreamListener[] listeners, RawStreamListener[] rawStreamListeners) { for (StreamListener listener : listeners) { listener.onException(e); } for (RawStreamListener listener : rawStreamListeners) { listener.onException(e); } } }