/*
* 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 java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import twitter4j.conf.StreamConfiguration;
import twitter4j.http.HttpResponse;
import twitter4j.internal.async.Dispatcher;
import twitter4j.internal.json.DataObjectFactoryUtil;
import twitter4j.internal.json.InternalJSONFactory;
import twitter4j.internal.json.InternalJSONFactoryImpl;
import twitter4j.internal.logging.Logger;
import twitter4j.json.JSONObjectType;
/**
* @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 final BufferedReader br;
private final InputStream is;
private HttpResponse response;
protected final Dispatcher dispatcher;
protected final StreamConfiguration CONF;
protected InternalJSONFactory factory;
/* package */
StatusStreamBase(final Dispatcher dispatcher, final HttpResponse response, final StreamConfiguration conf)
throws IOException {
this(dispatcher, response.asStream(), conf);
this.response = response;
}
StatusStreamBase(final Dispatcher dispatcher, final InputStream stream, final StreamConfiguration conf)
throws IOException {
is = stream;
br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
this.dispatcher = dispatcher;
CONF = conf;
factory = new InternalJSONFactoryImpl(conf);
}
/* package */
@Override
public void close() throws IOException {
streamAlive = false;
is.close();
br.close();
if (response != null) {
response.disconnect();
}
}
@Override
public abstract void next(StatusListener listener) throws TwitterException;
public abstract void next(StreamListener[] listeners, RawStreamListener[] rawStreamListeners)
throws TwitterException;
public void onException(final Exception e, final StreamListener[] listeners,
final RawStreamListener[] rawStreamListeners) {
for (final StreamListener listener : listeners) {
listener.onException(e);
}
for (final RawStreamListener listener : rawStreamListeners) {
listener.onException(e);
}
}
protected DirectMessage asDirectMessage(final JSONObject json) throws TwitterException {
DirectMessage directMessage;
try {
directMessage = factory.createDirectMessage(json.getJSONObject("direct_message"));
} catch (final JSONException e) {
throw new TwitterException(e);
}
if (CONF.isJSONStoreEnabled()) {
DataObjectFactoryUtil.registerJSONObject(directMessage, json);
}
return directMessage;
}
protected long[] asFriendList(final JSONObject json) throws TwitterException {
JSONArray friends;
try {
friends = json.getJSONArray("friends");
final long[] friendIds = new long[friends.length()];
for (int i = 0; i < friendIds.length; ++i) {
friendIds[i] = Long.parseLong(friends.getString(i));
}
return friendIds;
} catch (final JSONException e) {
throw new TwitterException(e);
}
}
protected Status asStatus(final JSONObject json) throws TwitterException {
final Status status = factory.createStatus(json);
if (CONF.isJSONStoreEnabled()) {
DataObjectFactoryUtil.registerJSONObject(status, json);
}
return status;
}
protected User asUser(final JSONObject json) throws TwitterException {
final User user = factory.createUser(json);
if (CONF.isJSONStoreEnabled()) {
DataObjectFactoryUtil.registerJSONObject(user, json);
}
return user;
}
protected UserList asUserList(final JSONObject json) throws TwitterException {
final UserList userList = factory.createAUserList(json);
if (CONF.isJSONStoreEnabled()) {
DataObjectFactoryUtil.registerJSONObject(userList, json);
}
return userList;
}
protected void handleNextElement(final StreamListener[] listeners, final RawStreamListener[] rawStreamListeners)
throws TwitterException {
if (!streamAlive) throw new IllegalStateException("Stream already closed.");
try {
final 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) {
@Override
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();
}
final JSONObject json = new JSONObject(line);
final 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 (final Exception ex) {
onException(ex, listeners);
}
}
});
} catch (final IOException ioe) {
try {
is.close();
} catch (final IOException ignore) {
}
final boolean isUnexpectedException = streamAlive;
streamAlive = false;
if (isUnexpectedException) throw new TwitterException("Stream closed.", ioe);
}
}
protected void onBlock(final JSONObject source, final JSONObject target, final StreamListener[] listeners)
throws TwitterException {
logger.warn("Unhandled event: onBlock");
}
protected void onDelete(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onDelete");
}
protected void onDirectMessage(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onDirectMessage");
}
protected void onDisconnectionNotice(final String line, final StreamListener[] listeners) {
logger.warn("Unhandled event: ", line);
}
protected void onException(final Exception e, final StreamListener[] listeners) {
logger.warn("Unhandled event: ", e.getMessage());
}
protected void onFavorite(final JSONObject source, final JSONObject target, final JSONObject targetObject,
final StreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onFavorite");
}
protected void onFollow(final JSONObject source, final JSONObject target, final StreamListener[] listeners)
throws TwitterException {
logger.warn("Unhandled event: onFollow");
}
protected void onFriends(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onFriends");
}
protected void onLimit(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onLimit");
}
protected void onMessage(final String rawString, final RawStreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onMessage");
}
protected void onScrubGeo(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onScrubGeo");
}
protected void onSender(final JSONObject json, final StreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onSender");
}
protected void onStallWarning(final JSONObject json, final StreamListener[] listeners) throws TwitterException,
JSONException {
logger.warn("Unhandled event: onStallWarning");
}
protected void onStatus(final JSONObject json, final StreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onStatus");
}
protected void onUnblock(final JSONObject source, final JSONObject target, final StreamListener[] listeners)
throws TwitterException {
logger.warn("Unhandled event: onUnblock");
}
protected void onUnfavorite(final JSONObject source, final JSONObject target, final JSONObject targetObject,
final StreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onUnfavorite");
}
protected void onUnfollow(final JSONObject source, final JSONObject target, final StreamListener[] listeners)
throws TwitterException {
logger.warn("Unhandled event: onUnfollow");
}
protected void onUserListCreation(final JSONObject source, final JSONObject userList,
final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListCreation");
}
protected void onUserListDestroyed(final JSONObject source, final JSONObject userList,
final StreamListener[] listeners) throws TwitterException {
logger.warn("Unhandled event: onUserListDestroyed");
}
protected void onUserListMemberAddition(final JSONObject addedMember, final JSONObject owner,
final JSONObject userList, final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListMemberAddition");
}
protected void onUserListMemberDeletion(final JSONObject deletedMember, final JSONObject owner,
final JSONObject userList, final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListMemberDeletion");
}
protected void onUserListSubscription(final JSONObject source, final JSONObject owner, final JSONObject userList,
final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListSubscription");
}
protected void onUserListUnsubscription(final JSONObject source, final JSONObject owner, final JSONObject userList,
final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListUnsubscription");
}
protected void onUserListUpdated(final JSONObject source, final JSONObject userList,
final StreamListener[] listeners) throws TwitterException, JSONException {
logger.warn("Unhandled event: onUserListUpdated");
}
protected void onUserUpdate(final JSONObject source, final JSONObject target, final StreamListener[] listeners)
throws TwitterException {
logger.warn("Unhandled event: onUserUpdate");
}
protected String parseLine(final String line) {
return line;
}
abstract class StreamEvent implements Runnable {
String line;
StreamEvent(final String line) {
this.line = line;
}
}
}