/******************************************************************************* * Copyright 2010 Universidade do Minho, Ricardo Vila�a and Francisco Cruz * * 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.ublog.benchmark.social.voldemort; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.ublog.benchmark.BenchOperation; import org.ublog.benchmark.ComplexBenchMarkClient; import org.ublog.benchmark.operations.CollectionOperation; import org.ublog.benchmark.operations.DataStoreOperation; import org.ublog.benchmark.operations.GetOperation; import org.ublog.benchmark.operations.GetRangeOperation; import org.ublog.benchmark.operations.OperationListener; import org.ublog.benchmark.operations.PutOperation; import org.ublog.benchmark.social.Message; import org.ublog.benchmark.social.SocialBenchmark; import org.ublog.benchmark.social.UserService; import org.ublog.benchmark.social.Utils; public class StartFollowingOperation implements BenchOperation,OperationListener{ private enum Phase {UPDATE_FOLLOWERS,UPDATE_FOLLOWING,UPDATE_TIMELINE} private ComplexBenchMarkClient client; private String userId,toStartUser; private Phase currentPhase; private Map<String, String> user; private Map<String, String> toStart; private boolean get; private boolean finish; private List<String> timeLine; private List<Message> recentTweets; public StartFollowingOperation(ComplexBenchMarkClient client, String userId, String toStartUser) { this.client=client; this.userId=userId; this.toStartUser=toStartUser; this.currentPhase=Phase.UPDATE_FOLLOWING; this.get=false; this.finish=false; this.user=null; this.toStart=null; this.recentTweets=null; } private void addToTimeline(List<String> timeline, int timelineIdx, String timelineId) { timeline.add(timelineIdx, timelineId); } private String getTimelineId(Message tweet) { return tweet.getId() + ":" + tweet.getDate().toString(); } @Override public void handleFinishedOperation(DataStoreOperation op) { switch(this.currentPhase) { case UPDATE_FOLLOWING: if (this.user==null) { this.user=(Map<String,String>) ((GetOperation) op).getResult(); } else this.currentPhase=Phase.UPDATE_FOLLOWERS; break; case UPDATE_FOLLOWERS: if (this.toStart==null){ this.toStart=(Map<String,String>) ((GetOperation) op).getResult(); } else this.currentPhase=Phase.UPDATE_TIMELINE; break; case UPDATE_TIMELINE: if (this.timeLine==null) { this.timeLine=(List<String>) ((GetOperation) op).getResult(); if (this.timeLine==null) this.timeLine=new ArrayList<String>(); } else if (this.recentTweets==null) { this.recentTweets=new ArrayList<Message>(); for(Map<String,String> tweetMap:((Set<Map<String, String>>) ((GetRangeOperation) op).getResult())){ this.recentTweets.add(Utils.toTweet(tweetMap)); } } else this.client.handleFinishedComplexOperation(this); break; } } @Override public String getName() { return "twitter:startFollowing"; } @Override public CollectionOperation getNextDBOperation() { CollectionOperation res=null; switch(this.currentPhase) { case UPDATE_FOLLOWING: if (!this.get) { res=new GetOperation(client,SocialBenchmark.userTable,this.userId,250); this.get=true; } else { Set<String> following = Utils.asSet(user.get(UserService.FOLLOWING)); following.add(toStartUser); this.user.put(UserService.FOLLOWING, Utils.toString(following)); res=new PutOperation(client,SocialBenchmark.userTable,this.userId,this.user,null); this.get=false; } break; case UPDATE_FOLLOWERS: if (!this.get) { res=new GetOperation(client,SocialBenchmark.userTable,this.toStartUser,250); this.get=true; } else { Set<String> followers = Utils.asSet(user.get(UserService.FOLLOWERS)); followers.add(userId); this.toStart.put(UserService.FOLLOWERS, Utils.toString(followers)); res=new PutOperation(client,SocialBenchmark.userTable,this.toStartUser,this.toStart,null); this.get=false; } break; case UPDATE_TIMELINE: if (!this.get) { this.timeLine=null; res=new GetOperation(client,SocialBenchmark.friendsTimeLineTable,this.userId,250); this.get=true; } else { if (this.recentTweets==null) { int lastTweet = Integer.valueOf(this.toStart.get(UserService.LAST_TWEET)); String min=this.toStartUser + "-" + Utils.getTweetPadding(Math.max(1,(lastTweet - 20+1))); String max=this.toStartUser + "-" + Utils.getTweetPadding(lastTweet) ; res=new GetRangeOperation(client,SocialBenchmark.tweetsTable,min,max); } else { int timelineIdx = 0; long timelineDate = 0; for (Message tweet : recentTweets) { if(tweet!=null){ long date = tweet.getDate().timestamp(); String timelineId = getTimelineId(tweet); if (this.timeLine.contains(timelineId)) { continue; } while (timelineIdx < this.timeLine.size() && timelineIdx < Utils.MAX_MESSAGES_IN_TIMELINE) { String id = this.timeLine.get(timelineIdx); String[] split = id.split(":"); //timelineDate = new Long(split[1]); timelineDate = UUID.fromString(split[1]).timestamp(); if (timelineDate > date) { addToTimeline(this.timeLine, timelineIdx, timelineId); timelineIdx++; break; } timelineIdx++; } if (timelineIdx == this.timeLine.size()) { addToTimeline(this.timeLine, timelineIdx, timelineId); } if (timelineIdx == Utils.MAX_MESSAGES_IN_TIMELINE) { break; } } } res=new PutOperation(client,SocialBenchmark.friendsTimeLineTable,this.userId,timeLine,null); this.finish=true; } } break; } res.addListener(this); return res; } @Override public boolean hasMoreDBOperations() { return !this.finish; } @Override public String toString() { return "StartFollowingOperation [toStartUser=" + toStartUser + ", userId=" + userId + "]"; } @Override public boolean isInit() { // TODO Auto-generated method stub return false; } @Override public boolean isReadOnly() { // TODO Auto-generated method stub return false; } @Override public void setInit(boolean init) { // TODO Auto-generated method stub } }