de.mojadev.ohmmarks.virtuohm.VirtuOhmHTTPHandler.java Source code

Java tutorial

Introduction

Here is the source code for de.mojadev.ohmmarks.virtuohm.VirtuOhmHTTPHandler.java

Source

/**
 *  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;
    }
}