Java tutorial
/** * OhmMarks - Simple app for displaying and notifying about new marks from * the GSO-VirtuOhm system * * Copyright (C) 2012 mojadev (www.mojadev.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * @author Moja <mojadev@gmail.com> * **/ package de.mojadev.ohmmarks.virtuohm; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.protocol.ClientContext; import org.apache.http.cookie.Cookie; import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import android.content.Context; import android.net.http.AndroidHttpClient; import android.util.Log; import android.util.Pair; import de.mojadev.ohmmarks.config.VirtuOhmConfig; import de.mojadev.ohmmarks.data.Mark; import de.mojadev.ohmmarks.data.resolver.AbstractResolver; import de.mojadev.ohmmarks.data.resolver.StaticTextProcessingMarkResolver; import de.mojadev.ohmmarks.virtuohm.exceptions.*; import de.mojadev.ohmmarks.virtuohm.http.*; import de.mojadev.ohmmarks.virtuohm.state.ResponseStateParser; import de.mojadev.ohmmarks.virtuohm.state.VirtuOhmState; /** * HTTP Handler class that handles communication with VirtuOhm, i.e. login * and fetching marks. * * At this time the size of this class is acceptable, but when there are more * features this class could be composed by different request strategy classes * @author Moja <mojadev@gmail.com> * */ public class VirtuOhmHTTPHandler implements ResponseHandler<Pair<VirtuOhmState, String>> { private VirtuOhmConfig config = null; private AndroidHttpClient httpIO = null; private VirtuOhmState state = VirtuOhmState.LOGIN_PAGE; private BasicCookieStore cookies; private Context context; private HttpContext httpContext; private Map<String, String> persistentGETParams = new HashMap<String, String>(); private Boolean loggedIn = false; private AbstractResolver<ArrayList<Mark>> resolver = new StaticTextProcessingMarkResolver(); private final static String USER_AGENT = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.1 (KHTML, like Gecko) Ubuntu/11.04 Chromium/14.0.825.0 Chrome/14.0.825.0 Safari/535.1"; class ResultPair extends Pair<VirtuOhmState, String> { public ResultPair(VirtuOhmState first, String second) { super(first, second); } } public VirtuOhmHTTPHandler(Context ctx) { this(ctx, new VirtuOhmConfig()); } public VirtuOhmHTTPHandler(Context ctx, VirtuOhmConfig cfg) { Log.d("Communicator", "Setting up communicator"); config = cfg; context = ctx; } private void setupHttpIO() { Log.d("Communicator", "Instanciating HTTP Client"); httpIO = AndroidHttpClient.newInstance(USER_AGENT); if (this.cookies == null) { // Setup a cookie store for the httpContext, so we don't have to care about them this.cookies = new BasicCookieStore(); this.httpContext = new BasicHttpContext(); this.httpContext.setAttribute(ClientContext.COOKIE_STORE, this.cookies); } } public void resetState() { Log.d("Communicator", "Resetting state"); this.loggedIn = false; this.persistentGETParams.clear(); this.setState(VirtuOhmState.LOGIN_PAGE); if (this.cookies != null) { this.cookies.clear(); this.cookies = null; this.stop(); } } public void login() throws Exception { Log.i("Communicator", "Starting to log in as user " + this.getConfig().getUser()); this.resetState(); HttpPost request = new HttpPost(getURI(VirtuOhmUris.LOGIN)); request.setEntity(VirtuOhmParams.getLogin(config.getUser(), config.getPassword())); ResultPair result = executeHttpRequest(request); // Login succeeded, now register session at server if (result.first == VirtuOhmState.LOGIN_SUCCESS) { Log.i("Communicator", "Login succeeded, creating session"); this.registerLoginSession(result); return; } // Something went wrong, determine what Log.e("Communicator", "Login failed!"); if (result.first == VirtuOhmState.CONFIG_ERROR) if (result.second == "INVALID URL") throw new HTTP404Exception(); if (result.first == VirtuOhmState.LOGIN_ERROR) throw new LoginFailedException(); throw new UnknownResultException(result.second); } private void logout() throws Exception { Log.i("Communicator", "Logging out as " + this.getConfig().getUser()); String crimmo = this.persistentGETParams.get(VirtuOhmParams.IN_CRIMMO); String url = getURI(VirtuOhmUris.LOGOUT) + VirtuOhmParams.getLogout(crimmo); HttpGet request = new HttpGet(url); executeHttpRequest(request); // if(this.state != VirtuOhmState.LOGOUT_PAGE) { // Log.e("Communicator", "Logout failed"); // throw new Exception(); // } // // Log.i("Communicator", "Logout successful"); } private void registerLoginSession(ResultPair result) throws Exception { Pair<String, String> credentialParams = ResponseStateParser.getCredentialParameters(result.second); String url = getURI(VirtuOhmUris.SET_SESSION) + VirtuOhmParams.getSessionRegisterGETParams(credentialParams.first, credentialParams.second); HttpGet request = new HttpGet(url); // TODO: Doesn't feel right to store persisted GET values here this.persistentGETParams.put(VirtuOhmParams.IN_CRIMMO, credentialParams.first); executeHttpRequest(request); if (this.state != VirtuOhmState.MAIN_PAGE) { throw new Exception(); } this.loggedIn = true; } private String getURI(String uri) { for (String p : persistentGETParams.keySet()) { boolean first = true; if (persistentGETParams.get(p) != null && persistentGETParams.get(p).length() > 0) { uri += (first ? "?" : "&") + p + "=" + persistentGETParams.get(p); first = false; } } Log.d("Request uri ", uri); return config.getAddress() + uri; } private ResultPair executeHttpRequest(HttpRequestBase request) throws ClientProtocolException, IOException { if (httpIO == null) this.setupHttpIO(); return (ResultPair) httpIO.execute(request, this, httpContext); } /** * Fetch marks and login if not already done * @return * @throws SessionTimeoutException * @throws HTTP404Exception * @throws Exception */ public ArrayList<Mark> fetchMarks() throws SessionTimeoutException, HTTP404Exception, Exception { if (!this.loggedIn) this.login(); String url = getURI(VirtuOhmUris.REQUEST_NEW_MARKS); HttpGet request = new HttpGet(url); ResultPair result = executeHttpRequest(request); this.logout(); if (result.first == VirtuOhmState.PENDING_MARKS) { ArrayList<Mark> marks = resolver.resolve(result.second); return marks; } if (result.first == VirtuOhmState.CONFIG_ERROR) if (result.second == "INVALID URL") throw new HTTP404Exception(); if (result.first == VirtuOhmState.LOGIN_ERROR || result.first == VirtuOhmState.SESSION_TIMEOUT) throw new LoginFailedException(); throw new UnknownResultException(result.second); } @Override public ResultPair handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine status = response.getStatusLine(); if (status.getStatusCode() == 404) { return new ResultPair(VirtuOhmState.CONFIG_ERROR, "INVALID URL"); } for (Cookie c : cookies.getCookies()) { Log.d("Cookies", c.getName() + " : " + c.getValue()); } String responseText = getResponseText(response); VirtuOhmState state = ResponseStateParser.getCurrentState(responseText); this.setState(state); return new ResultPair(state, responseText); } private String getResponseText(HttpResponse response) throws IllegalStateException, IOException { BufferedHttpEntity responseEntity = new BufferedHttpEntity(response.getEntity()); int contentLength = (int) responseEntity.getContentLength(); InputStream stream = responseEntity.getContent(); byte buffer[] = new byte[contentLength]; stream.read(buffer, 0, contentLength); return new String(buffer); } public void stop() { if (this.httpIO != null) this.httpIO.close(); this.httpIO = null; } public void finalize() { this.stop(); } public VirtuOhmConfig getConfig() { return config; } public void setConfig(VirtuOhmConfig config) { if (config.toString() != this.config.toString()) resetState(); this.config = config; } public Context getContext() { return context; } public void setContext(Context context) { this.context = context; } public VirtuOhmState getState() { return state; } private void setState(VirtuOhmState state) { this.state = state; } public static String getUSER_AGENT() { return USER_AGENT; } }