GWTでTwitter

GWTを使って、発言、検索、TLの表示をするアプリを作成する

■アプリ概要

  • ページを開くとTimeLineの取得をする
  • Sayボタンを押すとTextBoxの内容をつぶやく
  • Searchボタンを押すとTextBoxの内容を検索する
  • 更新ボタンを押すとTimeLineを更新する

■レイアウト

  • RootPane
    • SendPanel
      • TextBox
      • SendButton
    • SearchPanek
      • TextBox
      • SendButton
    • TLPanel
      • 更新Button

■プロジェクト概要

  • プロジェクト名:GwtTwitter


■作成手順

  • twitter4jの追加
  • Beansの作成
    • twitterのラッパークラスを用意してRPC通信をする
      • ユーザ名、つぶやき、Icon、などをフィールドにもつ
  • Service・ServiceAsync作成
    • つぶやきメソッド定義
    • 検索メソッド定義
    • TL取得定義
  • Cliantクラス作成
    • コールバックを作成
  • ServiceImpleの作成

上記メソッドの実装

■twitter4jの追加

  • twitter4jを用意する
  • プロジェクト右クリック → Buildpath → Configure Build Path → Liblaryタブ → AddExternal JARs → twitter4jを選択
  • war\WEB-INF\libフォルダにjarファイル配置する

■TwitterBeans
つぶやきデータのラッパークラスを作成する。

  • client以下にentityパッケージを作成する
  • IsSerializableインタフェースを実装
  • Beansクラスを作成
  • 以下のプロパティを定義
    • 名前
    • スクリーン名前
    • ImageIcon
    • ホームURL
    • つぶやき
    • つぶやき時間

をもたせる

	private String userName;
	private String userScreenName;
	private String profileImageURL;
	private String home;
	private String tubuyaki;
	private String tubuyakiTime;


■Service
以下のメソッドを定義する

  • タイムラインの取得の定義
  • つぶやくメソッドの定義
  • つぶやき検索の定義
	/**
	 * タイムラインの取得
	 * @return
	 */
	ArrayList<TwitterBeans> getFriendTimeline();
	
	/**
	 * つぶやく
	 * @param input
	 * @return
	 */
	String say(String input);
	
	/**
	 * つぶやき検索
	 * @param input
	 * @return
	 */
	ArrayList<TwitterBeans> search(String input);

	/**
	 * ユーザ情報を取得する
	 * @param id
	 * @param pass
	 * @return
	 */
	TwitterBeans getUser(String id, String pass);


■GreetingServiceAsync
Serviceに対応したメソッドを定義する

	void say(String input, AsyncCallback<String> callback);
	void search(String input, AsyncCallback<ArrayList<TwitterBeans>> callback);
	void getFriendTimeline(AsyncCallback<ArrayList<TwitterBeans>> callback);
	void getUser(String id, String pass, AsyncCallback<TwitterBeans> callback);


■GreetingServiceImpl
Serviceインタフェースを実装する
各メソッドにはTwitterBeansをセットするsetBeansDataメソッドを呼び出す。
※setBeansDataの内容については後述するソースを参照

sayメソッド
引数inputの内容をつぶやく
つぶやくにはTwitterオブジェクトのupdateStatusメソッドを呼び出す

			Twitter twitter = new Twitter(ID, PASS);
			twitter.updateStatus(input);

searchメソッド
PublicTimelineを取得し、引数の内容と一致したものを取得する
取得したデータをTwitterBeansリストに格納する

	@Override
	public ArrayList<TwitterBeans> search(String input) {

		Twitter twitter = new Twitter();
		Query query = new Query(input);
		QueryResult result;
		ArrayList<TwitterBeans> listTwitter = new ArrayList<TwitterBeans>();
		try {
			result = twitter.search(query);
			for (Tweet tweet : result.getTweets()) {
				TwitterBeans bean = new TwitterBeans();
				setBeansData(bean, tweet);
				listTwitter.add(bean);
			}
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		return listTwitter;
	}

getFriendTimelineメソッド
FriendTimelineを取得する

	@Override
	public ArrayList<TwitterBeans> getFriendTimeline() {
		// get timeline
		Twitter twitter = new Twitter(ID, PASS);
		List<Status> statuses = null;
		ArrayList<TwitterBeans> listTwitter = new ArrayList<TwitterBeans>();
		try {
			statuses = twitter.getFriendsTimeline();
		} catch (TwitterException e) {
			e.printStackTrace();
		}

		for (Status status : statuses) {
			
			TwitterBeans bean = new TwitterBeans();
			setBeansData(bean, status);
			listTwitter.add(bean);
		}
		
		return listTwitter;

	}

getUserメソッド
指定したIDのユーザを取得し、TwitterBeansに格納する

	@Override
	public TwitterBeans getUser(String id, String pass) {
		Twitter twitter = new Twitter(ID, PASS);
		TwitterBeans bean = new TwitterBeans();
		try {
			User user = twitter.showUser(id);
			setBeansUserData(bean, user);
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		
		return bean;
	}

■GwtTwitterSample
今回はUI関係が多いので各UIの作成メソッドを用意する
画面の初期化が完了したら、FriendTimeLineを表示する
クライアント側のコードを作成する

  • UIを初期化しコールバック関数を作成する
    • Profile
    • つぶやき
    • 検索
    • FlendTimeLine

今回はUI関係が多いので各UIの作成メソッドを用意する

■Constant
定数定義クラス
TwitterID/Passを定義

■実行結果
■ソース
■TwitterBeans

package gwt.twitter.sample.client.beans;

import com.google.gwt.user.client.rpc.IsSerializable;

public class TwitterBeans implements IsSerializable {
	
	private String userName;
	private String userScreenName;
	private String profileImageURL;
	private String home;
	private String tubuyaki;
	private String tubuyakiTime;
	private String description;
	
	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	/**
	 * コンストラクタ
	 */
	public TwitterBeans() {
		super();
	}

	//UserName accesser
	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	//UserScreenName
	public String getUserScreenName() {
		return userScreenName;
	}

	public void setUserScreenName(String userScreenName) {
		this.userScreenName = userScreenName;
	}

	//ProfileImageURL
	public String getProfileImageURL() {
		return profileImageURL;
	}

	public void setProfileImageURL(String profileImageURL) {
		this.profileImageURL = profileImageURL;
	}

	//Home
	public String getHome() {
		return home;
	}

	public void setHome(String home) {
		this.home = home;
	}

	//Tubuyaki
	public String getTubuyaki() {
		return tubuyaki;
	}

	public void setTubuyaki(String tubuyaki) {
		this.tubuyaki = tubuyaki;
	}

	//TubuyakiTime
	public String getTubuyakiTime() {
		return tubuyakiTime;
	}

	public void setTubuyakiTime(String tubuyakiTime) {
		this.tubuyakiTime = tubuyakiTime;
	}

	
}

■GreetingService

package gwt.twitter.sample.client;

import gwt.twitter.sample.client.beans.TwitterBeans;

import java.util.ArrayList;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
	/**
	 * タイムラインの取得
	 * @return
	 */
	ArrayList<TwitterBeans> getFriendTimeline();
	
	/**
	 * つぶやく
	 * @param input
	 * @return
	 */
	String say(String input);
	
	/**
	 * つぶやき検索
	 * @param input
	 * @return
	 */
	ArrayList<TwitterBeans> search(String input);
	
	/**
	 * ユーザ情報を取得する
	 * @param id
	 * @param pass
	 * @return
	 */
	TwitterBeans getUser(String id, String pass);

}

■GreetingServiceAsync

package gwt.twitter.sample.client;

import gwt.twitter.sample.client.beans.TwitterBeans;

import java.util.ArrayList;

import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * The async counterpart of <code>GreetingService</code>.
 */
public interface GreetingServiceAsync {
	void say(String input, AsyncCallback<String> callback);
	void search(String input, AsyncCallback<ArrayList<TwitterBeans>> callback);
	void getFriendTimeline(AsyncCallback<ArrayList<TwitterBeans>> callback);
	void getUser(String id, String pass, AsyncCallback<TwitterBeans> callback);
}

■Constant

public final class Constants {
	public static final String ID = "XXXXXXXX";
	public static final String PASS = "XXXXXXX";

}

■GreetingServiceImpl

package gwt.twitter.sample.server;

import gwt.twitter.sample.client.GreetingService;
import gwt.twitter.sample.client.beans.TwitterBeans;
import gwt.twitter.sample.client.constnats.Constants;

import java.util.ArrayList;
import java.util.List;

import twitter4j.Query;
import twitter4j.QueryResult;
import twitter4j.Status;
import twitter4j.Tweet;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.User;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
		GreetingService {

	/**
	 * TweetオブジェクトからTwitterBeansを作成する
	 * @param bean
	 * @param tweet
	 */
	private void setBeansData(TwitterBeans bean, Tweet tweet){
		bean.setUserName(tweet.getFromUser());
		bean.setProfileImageURL(tweet.getProfileImageUrl());
		bean.setTubuyaki(tweet.getText());
		bean.setTubuyakiTime(tweet.getCreatedAt().toString());
	}
	
	/**
	 * StatusオブジェクトからTwitterBeansオブジェクトを作成する
	 * @param bean
	 * @param status
	 */
	private void setBeansData(TwitterBeans bean, Status status){
		User user = status.getUser();
		
		//Beansを設定する
		//Userオブジェクトからデータを追加
		setBeansUserData(bean, user);
		//Tweetデータの追加
		bean.setTubuyaki(status.getText());
	}
	
	/**
	 * TwitterBeansにUserオブジェクトのデータを追加
	 * @param bean
	 * @param user
	 */
	private void setBeansUserData(TwitterBeans bean, User user){
		bean.setUserName(user.getName());
		bean.setUserScreenName(user.getScreenName());
		bean.setProfileImageURL(user.getProfileImageURL().toString());
		bean.setHome("http://www.twitter.com/" + user.getName());
		bean.setDescription(user.getDescription());

	}

	@Override
	public TwitterBeans getUser(String id, String pass) {
		Twitter twitter = new Twitter(Constants.ID, Constants.PASS);
		TwitterBeans bean = new TwitterBeans();
		try {
			User user = twitter.showUser(id);
			setBeansUserData(bean, user);
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		
		return bean;
	}

	@Override
	public ArrayList<TwitterBeans> getFriendTimeline() {
		// get timeline
		Twitter twitter = new Twitter(Constants.ID, Constants.PASS);
		List<Status> statuses = null;
		ArrayList<TwitterBeans> listTwitter = new ArrayList<TwitterBeans>();
		try {
			statuses = twitter.getFriendsTimeline();
		} catch (TwitterException e) {
			e.printStackTrace();
		}

		for (Status status : statuses) {
			
			TwitterBeans bean = new TwitterBeans();
			setBeansData(bean, status);
			listTwitter.add(bean);
		}
		
		return listTwitter;
	}


	@Override
	public String say(String input) {
		try {
			Twitter twitter = new Twitter(Constants.ID, Constants.PASS);
			twitter.updateStatus(input);
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public ArrayList<TwitterBeans> search(String input) {

		Twitter twitter = new Twitter();
		Query query = new Query(input);
		QueryResult result;
		ArrayList<TwitterBeans> listTwitter = new ArrayList<TwitterBeans>();
		try {
			result = twitter.search(query);
			for (Tweet tweet : result.getTweets()) {
				TwitterBeans bean = new TwitterBeans();
				setBeansData(bean, tweet);
				listTwitter.add(bean);
			}
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		return listTwitter;
	}
}

■GwtTwitterSample

package gwt.twitter.sample.client;

import gwt.twitter.sample.client.beans.TwitterBeans;
import gwt.twitter.sample.client.constnats.Constants;

import java.util.ArrayList;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class GwtTwitterSample implements EntryPoint {
	/**
	 * Create a remote service proxy to talk to the server-side Greeting service.
	 */
	private final GreetingServiceAsync greetingService = GWT
			.create(GreetingService.class);

	//Debug
	private Panel panelServerRes;
	private Label serverResponseLabel;
	private HTML htmlError;
	
	//profile
	private Panel panelProfile;
	private HTML htmlProfileImage;
	private HTML htmlProfileDetaile;
	private AsyncCallback<TwitterBeans> callbackProfile;

	//つぶやき
	private Panel panelSendForm;
	private TextBox txtSay;
	private Button btSend;
	private AsyncCallback<String> callbackSay;

	//タイムライン
	static final int FRIEND_TIME_LINE_START_ROW = 1;
	private Panel panelFriendTimeLine;
	private FlexTable flexTtminline;
	private HTML htmlTwittwerRes;
	private Button btGet;
	private AsyncCallback<ArrayList<TwitterBeans>> callbackGetFriendTimeline;

	//検索
	private Panel panelSerch;
	private Button btSearch;
	private TextBox txtSearch;
	private AsyncCallback<ArrayList<TwitterBeans>> callbackSearch;
	//検索結果格納リスト
	private ArrayList<TwitterBeans> resultTwitterBeans = new ArrayList<TwitterBeans>();

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		initWidget();
		RootPanel.get("error").add(this.panelServerRes);
		RootPanel.get("panelProfile").add(this.panelProfile);
		RootPanel.get("panelSend").add(this.panelSendForm);
		RootPanel.get("panelSearch").add(this.panelSerch);
		RootPanel.get("FriendTimeLine").add(this.panelFriendTimeLine);
	}

	private void initWidget(){
		//Debug的
		this.htmlError = new HTML();
		this.serverResponseLabel = new Label();
		this.panelServerRes = new VerticalPanel();
		this.panelServerRes.add(this.htmlError);
		this.panelServerRes.add(this.serverResponseLabel);

		this.createProfile();
		this.createTweetForm();
		this.createSearchForm();
		this.createFriendTimelineForm();
		
		//Profileを表示
		greetingService.getUser(Constants.ID, Constants.PASS, callbackProfile);
		
		//フレンドラインを表示
		greetingService.getFriendTimeline(callbackGetFriendTimeline);
	}

	/**
	 * プロフィールフォームの作成
	 */
	private void createProfile(){
		this.panelProfile = new HorizontalPanel();
		this.htmlProfileImage = new HTML();
		this.htmlProfileDetaile = new HTML();
		this.panelProfile.add(this.htmlProfileImage);
		this.panelProfile.add(this.htmlProfileDetaile);
		
		//コールバックの作成
		this.callbackProfile = new AsyncCallback<TwitterBeans>(){

			@Override
			public void onFailure(Throwable caught) {
				serverResponseLabel.setText("Server Response Error");
				htmlError.setHTML(caught.getMessage());
			}

			@Override
			public void onSuccess(TwitterBeans result) {
				String username = "<b>" + result.getUserScreenName() + "/" + result.getUserName() + "</b>";
				htmlProfileImage.setHTML("<img src=\"" + result.getProfileImageURL() + "\" />");
				htmlProfileDetaile.setHTML(username + "<br>" + result.getDescription());
			}
		};
	}

	/**
	 * つぶやきフォームを作成する
	 */
	private void createTweetForm(){
		//つぶやき領域
		this.btSend = new Button("say");
		this.txtSay = new TextBox();
		this.panelSendForm = new VerticalPanel();
		this.panelSendForm.add(this.txtSay);
		this.panelSendForm.add(this.btSend);
		//つぶやきコールバック
		this.callbackSay = new AsyncCallback<String>(){
			@Override
			public void onFailure(Throwable caught) {
				serverResponseLabel.setText("Server Response Error");
				htmlError.setHTML(caught.getMessage());
			}

			@Override
			public void onSuccess(String result) {
				serverResponseLabel.setText("Success");
			}
		};

		//つぶやくRPC
		this.btSend.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				greetingService.say(txtSay.getText(), callbackSay);
				greetingService.getFriendTimeline(callbackGetFriendTimeline);
			}
		});
	}
	
	/**
	 * 検索フォームを作成する
	 */
	private void createSearchForm(){
		//検索
		this.panelSerch = new VerticalPanel();
		this.btSearch = new Button("Seach");
		this.txtSearch = new TextBox();
		this.panelSerch.add(this.txtSearch);
		this.panelSerch.add(this.btSearch);
		
		//検索コールバック
		this.callbackSearch = new AsyncCallback<ArrayList<TwitterBeans>>(){
			@Override
			public void onFailure(Throwable caught) {
				htmlError.setHTML("<b>Error</b><br>" + caught.getMessage());
			}

			@Override
			public void onSuccess(ArrayList<TwitterBeans> result) {
				display(result);
			}
		};

		//探すRPC
		this.btSearch.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				flexTtminline.clear();
				greetingService.search(txtSearch.getText(), callbackSearch);
			}
		});
	}


	/**
	 * フレンドラインを取得する
	 */
	private void createFriendTimelineForm(){
		//FriendTimeline領域
		this.panelFriendTimeLine = new VerticalPanel();
		
		this.btGet = new Button("更新");
		this.flexTtminline = new FlexTable();
		this.flexTtminline.setBorderWidth(1);
		this.flexTtminline.setHTML(FRIEND_TIME_LINE_START_ROW, 0, "<b>UserID</b>");
		this.flexTtminline.setHTML(FRIEND_TIME_LINE_START_ROW, 1, "<b>Comment</b>");
		this.flexTtminline.setHTML(FRIEND_TIME_LINE_START_ROW, 2, "<b>time</b>");
		this.htmlTwittwerRes = new HTML();
		this.panelFriendTimeLine.add(this.btGet);
		this.panelFriendTimeLine.add(this.flexTtminline);
		this.panelFriendTimeLine.add(this.htmlTwittwerRes);
		
		//コールバック
		//フレンドラインコールバックBeans
		this.callbackGetFriendTimeline = new AsyncCallback<ArrayList<TwitterBeans>>(){
			@Override
			public void onFailure(Throwable caught) {
				htmlError.setHTML("<b>Error</b><br>" + caught.getMessage());
			}

			@Override
			public void onSuccess(ArrayList<TwitterBeans> result) {
				display(result);
			}
		};

		//FriendTimeLineデータ取得RPC
		this.btGet.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				//フレンドタイムラインを取得
				greetingService.getFriendTimeline(callbackGetFriendTimeline);
			}
		});
	}

	private void display(ArrayList<TwitterBeans> result){
		int cnt = FRIEND_TIME_LINE_START_ROW;
		for( TwitterBeans bean : result){
			String col1 = "<img src=\"" + bean.getProfileImageURL() + "\" />";
			flexTtminline.setWidget(cnt, 0, new HTML(col1));
			flexTtminline.setWidget(cnt, 1, new HTML("<b>" + bean.getUserName() + "</b><br>" + bean.getTubuyaki()));
			flexTtminline.setWidget(cnt, 2, new HTML(bean.getTubuyakiTime()));
			cnt++;
		}
	}

	
}