x-traveler
DESCRIPTION
X-Traveler. Sixiang Chen, Shuli Shi, Xiaoping Li, Yinan Zhao, Shaojie Chen Group 14. Contents. Overview Architecture : Client Side Architecture: Server Side Pains & Gains Future Work Demos. Overview. X-Traveler is a mobile application on the android platform - PowerPoint PPT PresentationTRANSCRIPT
X-TRAVELE
RSixiang Chen, Shuli Shi, Xiaoping
Li, Yinan Zhao, Shaojie ChenGroup 14
Overview Architecture: Client Side Architecture: Server Side Pains & Gains Future Work Demos
Contents
X-Traveler is a mobile application on the android platform
Allows people to search and share information about different tourist sites like national parks
~100 classes and ~10000 lines of codes The codes are well refactored for further development Major use cases have been implemented: Login,
Register, Search for Attraction Place, View Attraction Place Details, Open Personal Page, Add To Wishlist, Create Travel Plan
Overview
Partially Implemented Extension Features: Add Friends
Extension Features To Be Implemented: Invite Friends to Comment on Plan, Recommend User with Similar Interests
Overview
Use Cases Review: Basic:
Login, Signup, Search for Place, Open Personal Page, Add to Wish list, Add to Visited List, Create Travel
Plan Extension:
Recommend Friend, Recommend Place, Add friend, Comment on Travel Plan
Overview
The Architecture:
Overview
Client Side
Database(MySQL)
ORM
JVM
Hibernate
SocketJSONObjectApach
eMysql
Php
Architecture: Client Side
UI Viewres
xml
ModelUserinterface.packageActivity
Utility ClassUtil.package
SocketHelper
HttpHelper
CommandCommand.package
Interface:
Request
Response
UI Pages:
Client Side: UI Views
Login
Signup
Main Page
Personal Page
Find Place City
Placewishlistvisitedlist
Each Activity corresponds to a page in the app
public class LogInActivity extends Activity {TextView test;
Handler mHandler;……signupButton = (Button)findViewById(R.id.signup);
signupButton.setOnClickListener(new OnClickListener(){ public void onClick(View arg0) { Request req = new SignUpRequest(username.getText().toString(),password.getText().toString() ); jsonobj = req.getJSON(); new ConnectServerThread().start();
// Option: new Task().excute();}
});
Client Side: Model
Open a new thread to connect with Server-Sideclass ConnectServerThread extends Thread {
public void run() { try {
helper.sendJSONToServer(jsonobj);Message msg = mHandler.obtainMessage(); msg.obj = helper.getUTFFromServer(); JSONObject resJSON = helper.toJSON(msg.objString response = (String)
resJSON.get(keys.RES_LOGIN_ACT); if (response.equals(values.LOGIN_SUC) || response.equals(values.SIGN_UP_SUC)) {
/* Switch to next page */ Response res = new LoginSucResponse(); res.run(LogInActivity.this, MainPage.class); } else {
mHandler.sendMessage(msg); }
Client Side: Model
Handling different actions according to different response from Server
class MessageHandler extends Handler {@Overridepublic void handleMessage(Message msg) {try {
String response = (helper.toJSON(msg.obj)).toString(); if (response.equals(values.SIGN_UP_INVALID_INPUT)){ test.setText("Invalid username or password to sign up."); } else if (response.equals(values.SIGN_UP_USERNAME_DUP)){test.setText("Username exists. Please select another one."); } else if
… }
} catch (JSONException e) {// TODO Auto-generated catch block e.printStackTrace();
…
Client Side: Model
Each Activity corresponds to a page in the app
public class LogInActivity extends Activity {TextView test;
Handler mHandler;……signupButton = (Button)findViewById(R.id.signup);
signupButton.setOnClickListener(new OnClickListener(){ public void onClick(View arg0) { Request req = new SignUpRequest(username.getText().toString(),password.getText().toString() ); jsonobj = req.getJSON(); new ConnectServerThread().start();
// Option: new DownloadFileTask().excute();}
});
Client Side: Model
class DownloadFileTask extends AsyncTask<Void, Void, Void> {@Overridepublic void onPostExecute(Void result) { wishlistImageViews[1].setImageBitmap(downloadImages[1]);…}@Overrideprotected Void doInBackground(Void... params) {// Connect with Server and get feedbackHttpUtil httpUtil = new HttpUtil……InputStream inputStream = httpUtil.getInputStream();downloadImages[2] = BitmapFactory.decodeStream(inputStream);……}
Client Side: Model
Two ways to connect with outside resources Server: Socket
// 10.0.2.2 for the emulator // IPv4 connect in same wifi network socket = new Socket("10.164.238.14", 4415); dos = new DataOutputStream( socket.getOutputStream()); dos.writeUTF(req.toString());…dis = new DataInputStream( socket.getInputStream()); return dis.readUTF()
Images: HTTP httpURLConnection = (HttpURLConnection) url.openConnection(); // Set time out httpURLConnection.setConnectTimeout(3000);httpURLConnection.setRequestMethod("GET");……
Client Side: Utility Classes
Storage Options Shared Preferences Internal Storage SQLite Databases
Client Side: Utility Classes
Store user information locally:
/*Store session key/ username into USER_INFOS.XML*/SharedPreferences infos = getSharedPreferences(USER_INFOS, 0);infos.edit() .putString(NAME, username.getText().toString()) .commit(); /*Get session key from USER_INFOS.XML*/Sessionkey = getSharedPreferences(JsonKeys.USER_INFOS, 0).getString(JsonKeys.SESSION_KEY,””)
Client Side: Utility Classes
Overview:
Architecture: Server Side
MySQL
Hibernate
DAO Layer
Session Comma
nd
Open Comma
nd
PersonalPage
Command Factory
Login Command
Factory
Client Handler
Socket Manage
r
Server
GeneralComman
dFactory
Interface:Comman
d
Interface:Command
Factory
SessionKey Communication:
Architecture: Server Side
SessionKey is a randomly generated
character string of length 32(62^32)
Client 1
Client k
Server
Database
1 Signup/Login (id + pw)2 SessionKey (or fail)3 SessionKey + new request4 Respond (or sessionout)
Socket Manager:public class SocketManager {
public SocketManager(SessionFactory sessionFactory) {/** constructor code **/}
public void createSocketManager(Scanner inputPortNumber) throws IOException, JSONException {int cur_port = inputPortNumber.nextInt();serverSocket = new ServerSocket(cur_port);while (true) {try {System.out.println("S: Receiving...");socket = serverSocket.accept();ClientHandler clientHandler = new ClientHandler(socket, sessionFactory);// This thread will do the talkingThread t = new Thread(clientHandler);t.start(); } catch (IOException ioe) { System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace(); }}
}
Architecture: Server Side
ClientHandler Connects Network
(socket) with database (sF)
Client Handler:
public class ClientHandler implements Runnable {private Socket socket;private SessionFactory sessionFactory;public ClientHandler(Socket s, SessionFactory sessionFactory) {
this.socket = s;this.sessionFactory = sessionFactory; }
public void run() {GeneralCommandFactory generalCommandFactory = new GeneralCommandFactory();ExecutionContext context = new ExecutionContext(sessionFactory, new UserDAO(sessionFactory), new PlaceDAO(sessionFactory), new PlanDAO(sessionFactory));try {DataInputStream dis = new DataInputStream(socket.getInputStream()); JSONObject jsonObject = new JSONObject(dis.readUTF());// call exception to deal with illegal purpose commandCommand command = generalCommandFactory.create(jsonObject);DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); JSONObject response = command.execute(context);dos.writeUTF(response.toString()); } catch (IOException | JSONException | CommandException| NullPointerException e) { e.printStackTrace();}}
}
Architecture: Server Side
OCP / DRY Principles
Context: Necessary Execution
Environment
ExecutionContextpublic class ExecutionContext {
private SessionFactory sessionFactory;private UserDAO userDAO;private PlaceDAO placeDAO;private PlanDAO planDAO;…../** Contains all execution requirement **/
}
Architecture: Server Side
OCP Principle
Command Factory: (Factory Pattern & Singleton Pattern)
public class GeneralCommandFactory {JSONObject jsonObject;/** * FACTORISE contains all concrete command factories */private static final CommandFactory[] FACTORIES = { new LoginCommandFactory(),
new SignUpCommandFactory(),new LogOutCommandFactory(), new AddVisitedPlaceCommandFactory(),new AddWishListPlaceCommandFactory(), newSearchPlaceCommandFactory(), new
GetVisitedListCommandFactory(), new GetWishListCommandFactory(), new PersonalPageCommandFactory(), new ListPlaceDetailCommandFactory(), new
ListFriendFactory(), new AddFriendFactory(), new DeleteFriendFactory(), new ShowCommonInterestUserFactory(), new UpdateUserInfoFactory(), new WritePlanFactory(), new PlanCommentFactory() };
private Map<String, CommandFactory> factoryMap;public GeneralCommandFactory() {this.factoryMap = new HashMap<>();for (CommandFactory factory : FACTORIES) {
this.factoryMap.put(factory.getCommandName(), factory); } }
Architecture: Server Side
Singleton Pattern
Command Factory: (Factory Pattern & Singleto Pattern)
/** * pass the json object to each concrete command factory * @param jsonObject * @return Command * @throws JSONException * @throws CommandException */public Command create(JSONObject jsonObject) throws JSONException, CommandException {
Command command = null; String commandName = jsonObject.getString(JsonKeys.PURPOSE);System.out.println(commandName);CommandFactory factory = this.factoryMap.get(commandName);if (factory == null) {// TODO: replace with an exception type that the caller can handle // (probably ProtocolException) System.out.println("void");throw new CommandException("no such command exist");} else {command = factory.makeCommand(jsonObject);return command; } }}
Architecture: Server Side
Session Command & Open Command:(Command Pattern)public abstract class SessionCommand implements Command {
JSONObject j;public SessionCommand(JSONObject j) {
this.j = j; }public boolean executeAuthenticated() throws CommandException, JSONException {/* Code to Anthenticate With SessionKey */}
}
public interface OpenCommand extends Command {}
Architecture: Server Side
Hibernate: DAO Layerpublic class PlaceDAO {
public PlaceDAO(SessionFactory sessionFactory) {}public void insertPlace(/* args */) {}public Place getPlace(/* args */) {}public String getPlaceImage(/* args */) {}public int getPlaceRate(/* args */) {}public void addToWishList(/* args */){}…
}
Architecture: Server Side
Hibernate DAO Layer: Template Methodpublic abstract class TransactionRoutine<P, R, X extends Exception> {…..public abstract R executeWithinTransaction(Session session, Transaction transaction, P param) throws X;public R execute(P param) throws X {
Session session = sessionFactory.getCurrentSession();boolean startedTransaction = false;Transaction tx = THREAD_TX.get();if (tx == null) {tx = session.beginTransaction();THREAD_TX.set(tx);startedTransaction = true;} try {R r = executeWithinTransaction(session, tx, param); if (startedTransaction) {tx.commit();THREAD_TX.set(null);}return r; } catch (Throwable t) {if (startedTransaction) {tx.rollback();THREAD_TX.set(null); }throw t;}
}}}
Architecture: Server Side
Hibernate DAO Layer: Template Methodpublic class PlaceDAO {…..public Place getPlace(String placeName) {TransactionRoutine<String, Place, RuntimeException> routine = new TransactionRoutine<String, Place, RuntimeException>(this.sessionFactory) {
@Overridepublic Place executeWithinTransaction(Session session, Transaction tx, String
placeName) {Query query = session.createQuery("from Place where placename=:placename");query.setParameter("placename", placeName); List<Place> list = query.list();for (Place u : list) {return u;}return null; }};return routine.execute(placeName);
}}
Architecture: Server Side
the Hollywood Principle!
Hibernate: ThreadLocalpublic class HibernateUtil { public static final ThreadLocal local = new ThreadLocal ();
public static Session currentSession() throws HibernateException { Session session = (Session) local.get(); //open a new session if this thread has no session if(session == null) { session = sessionFactory.openSession(); local.set(session);
}
return session;}
}
Architecture: Server Side
ThreadLocal(): create a thread local variable
get(): return the session / value of
threadlocal variable
Purpose: avoid multiple sessions
in a single thread
Hibernate: In hibernate configuration file, <property name = “hbm2ddl.auto”> create </property> can clear the whole (previous) database But when there are changes in the foreign keys of one
table (or related constraints), Hibernate may not delete them automatically.
We then have to delete the database by hand and recreate
it
Architecture: Server Side
A lot of time is spent struggling with Git But finally figured out how it works Writing our own sockets manager takes more
time and codes We have more flexibility with our own socket
manager Server side code is structurally refactored on
Iteration 4 Finally got a clearly (hierarchically) structured
server side and classes with efficient inheritance
Pains & Gains
Pains & GainsOld Structure:
MySQL
Hibernate
DAO Layer
PersonalPage
Command
Login Command
Client Handler
Socket Manage
r
Server
Command
Factory
Interface:Comman
d Potential Conflict Among
multiple Users
Implement two extension features and use Facebook API
Add more detailed controls on steps such as password strength and etc.
Refine the java doc Further machine tests with tools such as
Monkey Test Test app online and get User Experience
feedbacks
Future Work
Extension Feature: Friend Recommendation Currently we are using a simple comparison
method: for two arbitrary users, we use the number of shared visited places and wish-to-go places as a similarity score
When the number of users get large, we could use some more efficient clustering algorithms such as k-means
For place recommendation, we will use a similar logic with social network data: compared a person’s friends with the group of people who have been to a place as a recommendation score
Future Work
Thanks to Professor Smith and all TAs for this excellent class!
Thanks to Jed and Zach for your patience and help in solving the different problems we ran into!
Thanks to Our team members, Group 14 is an awesome team!
Thanks!