/**
* http://auzll.iteye.com/
*/
package com.gengweibo.weibo;
import static com.gengweibo.weibo.Weibo.Method.GET;
import static com.gengweibo.weibo.Weibo.Method.POST;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.oauth.OAuth;
import net.oauth.OAuth.Parameter;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthMessage;
import net.oauth.OAuthServiceProvider;
import net.oauth.ParameterStyle;
import net.oauth.client.OAuthClient;
import net.oauth.client.URLConnectionClient;
import net.oauth.http.HttpMessage;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.inject.internal.Lists;
import com.google.inject.internal.Maps;
/**
* 微博接口的抽象实现。本实现是基于网易微博的,也就是说Weibo163这个类基本不用修改就能 使用;其余的WeiboQQ等等实现类需要做一些调整才能使用。
*
* @author auzll@msn.com
* @since 2011-3-11
*/
public abstract class Weibo implements IWeibo, Serializable {
private static final Log LOG = LogFactory.getLog(Weibo.class);
/**
* OAuth请求类型,本系统暂时只需要用到GET或POST
*/
public static enum Method {
GET, POST;
}
private static final long serialVersionUID = -5519577190711442534L;
/** 分页参数,每页20条,每次最多可以获取20条数据 */
protected static final int PAGE_MAX = 20;
protected OAuthAccessor accessor;
/** 用户在本系统中的id,如果是初次使用,用UUID生成 */
protected String accountId;
/** 微博类型 */
protected WeiboType type;
/** 各微博厂商请求资源的地址 */
protected String urlResource;
/** 用户在微博厂商系统中的昵称 */
protected String weiboAccountName;
/**
* 微博账户在本系统中的ID,注意区别微博内容的id,本系统用statusId来表示微博内容的id。<br/>
* 微博账户ID格式(type_userIdorUserName): T_163_123123, T_QQ_zzzz, T_SINA_1231235
*/
protected String weiboId;
protected boolean synUpdate;
protected int parseLegalPageCount(String value) {
if (null != value) {
try {
int i = Integer.valueOf(value.trim());
if (i > 0 && i < PAGE_MAX) {
return i;
}
} catch (NumberFormatException e) {}
}
return PAGE_MAX;
}
public Weibo(String urlRequest, String urlAuthorization, String urlAccess,
String urlResource, String urlCallback, String consumerKey,
String consumerSecret, WeiboType type) {
OAuthServiceProvider provider = new OAuthServiceProvider(urlRequest,
urlAuthorization, urlAccess);
OAuthConsumer consumer = new OAuthConsumer(urlCallback, consumerKey,
consumerSecret, provider);
this.accessor = new OAuthAccessor(consumer);
this.urlResource = urlResource;
if (!this.urlResource.endsWith("/")) {
this.urlResource += "/";
}
this.type = type;
}
public void bindWeiboAccountContext() {
if (null == weiboId && null == weiboAccountName) {
Response response = verifyCredentials();
try {
JSONObject jsonObject = new JSONObject(response.readBodyAsString());
weiboId = getType().getEnName() + "_" + jsonObject.getString("id");
weiboAccountName = jsonObject.getString("name");
} catch (JSONException e) {
throw new WeiException(e);
}
}
}
public void convertAccessToken(IParam param) throws WeiException {
String oauth_verifier = param.getParamValue("oauth_verifier");
if (null == accessor.requestToken) {
throw new NullPointerException("accessor.requestToken is null");
}
Map<String, String> map = Maps.newHashMap();
map.put("oauth_token", accessor.requestToken);
if (null != oauth_verifier) {
map.put("oauth_verifier", oauth_verifier);
}
Response response = sendRequest(map,
accessor.consumer.serviceProvider.accessTokenURL, GET);
accessor.requestToken = null;
accessor.accessToken = response.getParameter("oauth_token");
accessor.tokenSecret = response.getParameter("oauth_token_secret");
}
public Response favoritesCreate(IParam param) {
String id = param.getParamValue("statusId");
return sendRequest(null, urlResource + "favorites/create/" + id
+ ".json", POST);
}
public Response friendshipsCreate(IParam param) {
String userId = param.getParamValue("userId");
return sendRequest(toRequestParam("user_id", userId), urlResource
+ "friendships/create.json", POST);
}
public Response friendshipsDestroy(IParam param) {
String userId = param.getParamValue("userId");
return sendRequest(toRequestParam("user_id", userId), urlResource
+ "friendships/destroy.json", POST);
}
public String getAccountId() {
return accountId;
}
public WeiboType getType() {
return type;
}
public String getWeiboAccountName() {
return weiboAccountName;
}
public String getWeiboId() {
return weiboId;
}
public Response homeTimeline(IParam param) {
RequestParam reqParam = toRequestParam("count",
parseLegalPageCount(param.getParamValue("count")));
if (null != param.getParamValue("since_id")) {
reqParam.add("since_id", param.getParamValue("since_id"));
}
if (null != param.getParamValue("max_id")) {
reqParam.add("max_id", param.getParamValue("max_id"));
}
return sendRequest(reqParam, urlResource
+ "statuses/home_timeline.json", GET);
}
public String makeClickUrl() throws WeiException {
OAuthClient client = new OAuthClient(new URLConnectionClient());
try {
client.getRequestToken(accessor);
} catch (Exception e) {
throw new WeiException(e);
}
Collection<Parameter> parameters = Lists.newArrayList();
parameters.add(new Parameter("oauth_token", accessor.requestToken));
if (null != accessor.consumer.callbackURL) {
parameters.add(new Parameter("oauth_callback",
accessor.consumer.callbackURL));
}
try {
OAuthMessage request = accessor.newRequestMessage(GET.toString(),
accessor.consumer.serviceProvider.userAuthorizationURL,
parameters);
Object accepted = accessor.consumer
.getProperty(OAuthConsumer.ACCEPT_ENCODING);
if (accepted != null) {
request.getHeaders().add(
new OAuth.Parameter(HttpMessage.ACCEPT_ENCODING,
accepted.toString()));
}
Object ps = accessor.consumer
.getProperty(OAuthClient.PARAMETER_STYLE);
ParameterStyle style = (ps == null) ? ParameterStyle.BODY : Enum
.valueOf(ParameterStyle.class, ps.toString());
HttpMessage httpRequest = HttpMessage.newRequest(request, style);
return httpRequest.url.toString();
} catch (Exception e) {
throw new WeiException(e);
}
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public void setWeiboAccountName(String accountName) {
this.weiboAccountName = accountName;
}
public void setWeiboId(String weiboId) {
this.weiboId = weiboId;
}
public Response statusesComments(IParam param) {
RequestParam reqParam = toRequestParam("count", PAGE_MAX);
if (null != param.getParamValue("since_id")) {
// 163可选参数,该参数需传微博id,返回此条索引之后发的微博列表,不包含此条;
reqParam.add("since_id", param.getParamValue("since_id"));
}
return sendRequest(
reqParam,
urlResource + "statuses/comments/"
+ param.getParamValue("statusId") + ".json", GET);
}
public Response statusesDestroy(IParam param) {
String id = param.getParamValue("statusId");
return sendRequest(null, urlResource + "statuses/destroy/" + id
+ ".json", POST);
}
public Response statusesReply(IParam param) {
String id = param.getParamValue("statusId");
String status = param.getParamValue("status");
return sendRequest(toRequestParam("status", status).add("id", id),
urlResource + "statuses/reply.json", POST);
}
public Response statusesRetweet(IParam param) {
String id = param.getParamValue("statusId");
RequestParam reqParam = null;
if (null != param.getParamValue("status")) {
reqParam = toRequestParam("status", param.getParamValue("status"));
}
return sendRequest(reqParam, urlResource + "statuses/retweet/" + id
+ ".json", POST);
}
public Response statusesUpdate(IParam param) {
String status = param.getParamValue("status");
return sendRequest(toRequestParam("status", status), urlResource + "statuses/update.json", POST);
}
public Response verifyCredentials() {
return sendRequest(null, urlResource
+ "account/verify_credentials.json", GET);
}
protected static final int MAX_RETRY_TIMES = 3;
protected Response sendRequest(Map<?, ?> map, String url, Method method)
throws WeiException {
try {
List<Map.Entry<?, ?>> params = null;
if (null != map) {
params = Lists.newArrayList();
Iterator<?> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<?, ?> p = (Map.Entry<?, ?>) it.next();
params.add(new OAuth.Parameter((String) p.getKey(),
(String) p.getValue()));
}
}
OAuthClient client = new OAuthClient(new URLConnectionClient());
int tryTimes = 0;
while (tryTimes < MAX_RETRY_TIMES) {
try {
tryTimes++;
return new Response(client.invoke(accessor, method.toString(), url, params));
} catch (Exception e) {
// 最多重试MAX_RETRY_TIMES
LOG.error("method:sendRequest,url:" + url + ",tryTimes:" + tryTimes);
if (tryTimes >= MAX_RETRY_TIMES) {
throw e;
}
}
}
throw new RuntimeException("UnexpectedException");
} catch (Exception e) {
throw new WeiException(e);
}
}
protected class RequestParam extends HashMap<String, String> {
private static final long serialVersionUID = -5338614760105715587L;
public RequestParam() {}
public RequestParam(String key, Object value) {
put(key, value.toString());
}
public RequestParam add(String key, Object value) {
if (null != value) {
put(key, value.toString());
}
return this;
}
}
protected RequestParam toRequestParam(String key, Object value) {
if (null == value) {
throw new NullPointerException("value for key[" + key + "] is null");
}
return new RequestParam(key, value.toString());
}
public OAuthAccessor getAccessor() {
return accessor;
}
public Response statusesMentions(IParam param) {
RequestParam reqParam = toRequestParam("count",
parseLegalPageCount(param.getParamValue("count")));
if (null != param.getParamValue("since_id")) {
reqParam.add("since_id", param.getParamValue("since_id"));
}
if (null != param.getParamValue("max_id")) {
reqParam.add("max_id", param.getParamValue("max_id"));
}
return sendRequest(reqParam, urlResource + "statuses/mentions.json",
GET);
}
public Response statusesCommentsToMe(IParam param) {
RequestParam reqParam = toRequestParam("count",
parseLegalPageCount(param.getParamValue("count")));
if (null != param.getParamValue("since_id")) {
reqParam.add("since_id", param.getParamValue("since_id"));
}
reqParam.add("trim_user", "false");
return sendRequest(reqParam, urlResource
+ "statuses/comments_to_me.json", GET);
}
public boolean isSynUpdate() {
return synUpdate;
}
public void setSynUpdate(boolean synUpdate) {
this.synUpdate = synUpdate;
}
}