org.envirocar.app.application.UploadManager.java Source code

Java tutorial

Introduction

Here is the source code for org.envirocar.app.application.UploadManager.java

Source

/* 
 * enviroCar 2013
 * Copyright (C) 2013  
 * Martin Dueren, Jakob Moellers, Gerald Pape, Christopher Stephan
 *
 * 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 3 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
 * 
 */

package org.envirocar.app.application;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.FileEntity;
import org.envirocar.app.R;
import org.envirocar.app.activity.SettingsActivity;
import org.envirocar.app.dao.DAOProvider;
import org.envirocar.app.dao.NotConnectedException;
import org.envirocar.app.json.TrackEncoder;
import org.envirocar.app.logging.Logger;
import org.envirocar.app.model.Car;
import org.envirocar.app.network.HTTPClient;
import org.envirocar.app.storage.DbAdapter;
import org.envirocar.app.storage.DbAdapterImpl;
import org.envirocar.app.storage.Track;
import org.envirocar.app.storage.TrackMetadata;
import org.envirocar.app.storage.TrackWithoutMeasurementsException;
import org.json.JSONException;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;

/**
 * Manager that can upload tracks and cars to the server. 
 * Use the uploadAllTracks function to upload all local tracks. 
 * Make sure that you specify the dbAdapter when instantiating.
 * The default constructor should only be used when there is no
 * other way.
 * 
 */
public class UploadManager {

    public static final String NET_ERROR = "net_error";
    public static final String GENERAL_ERROR = "-1";

    private String url = ECApplication.BASE_URL + "/users/%1$s/tracks";
    private static Logger logger = Logger.getLogger(UploadManager.class);
    private DbAdapter dbAdapter;
    private Context context;
    private static Map<String, String> temporaryAlreadyRegisteredCars = new HashMap<String, String>();

    /**
     * Normal constructor for this manager. Specify the context and the dbadapter.
     * @param dbAdapter The dbadapter (most likely the local one)
     * @param ctx The context.
     */
    public UploadManager(Context ctx) {
        this.context = ctx;
        this.dbAdapter = DbAdapterImpl.instance();
    }

    /**
     * This methods uploads all local tracks to the server
     */
    public void uploadAllTracks(TrackUploadFinishedHandler callback) {
        for (Track track : dbAdapter.getAllLocalTracks()) {
            uploadSingleTrack(track, callback);
        }
    }

    public void uploadSingleTrack(Track track, TrackUploadFinishedHandler callback) {
        if (track == null)
            return;

        new UploadAsyncTask(callback).execute(track);
    }

    private void registerCarBeforeUpload(Track track) throws NotConnectedException {
        Car car = track.getCar();
        String tempId = car.getId();
        String sensorIdFromServer = DAOProvider.instance().getSensorDAO().saveSensor(car);

        car.setId(sensorIdFromServer);

        logger.info("Car id tmpTrack: " + track.getCar().getId());

        DbAdapterImpl.instance().updateTrack(track);
        DbAdapterImpl.instance().updateCarIdOfTracks(tempId, car.getId());

        /*
         * we need this hack... Track objects
         * in memory are not informed through the DB update
         */
        temporaryAlreadyRegisteredCars.put(tempId, car.getId());
        if (CarManager.instance().getCar().getId().equals(tempId)) {
            CarManager.instance().setCar(car);
        }
    }

    private boolean hasTemporaryCar(Track track) {
        return track.getCar().getId().startsWith(Car.TEMPORARY_SENSOR_ID);
    }

    private class UploadAsyncTask extends AsyncTask<Track, Track, Track> {

        private TrackUploadFinishedHandler callback;

        public UploadAsyncTask(TrackUploadFinishedHandler callback) {
            this.callback = callback;
        }

        @Override
        protected Track doInBackground(Track... params) {
            ((ECApplication) context).createNotification("start");
            User user = UserManager.instance().getUser();
            String username = user.getUsername();
            String token = user.getToken();
            String urlL = String.format(url, username);

            Track track = params[0];
            Thread.currentThread().setName("TrackUploaderTast-" + track.getId());

            if (hasTemporaryCar(track)) {
                /*
                 * perhaps we already did a registration for this temp car.
                 * the Map is application uptime scope (static).
                 */
                if (!temporaryCarAlreadyRegistered(track)) {
                    try {
                        registerCarBeforeUpload(track);
                    } catch (NotConnectedException e) {
                        logger.warn(e.getMessage(), e);
                        this.cancel(true);
                        ((ECApplication) context).createNotification(
                                context.getResources().getString(R.string.general_error_please_report));
                    }
                }

            }

            File file = null;
            try {
                file = saveTrackAndReturnFile(track);
            } catch (JSONException e) {
                logger.warn(e.getMessage(), e);
                //the track wasn't JSON serializable. shouldn't occur.
                this.cancel(true);
                ((ECApplication) context)
                        .createNotification(context.getResources().getString(R.string.general_error_please_report));
            } catch (RuntimeException e) {
                logger.warn(e.getMessage(), e);
                this.cancel(true);
                ((ECApplication) context)
                        .createNotification(context.getResources().getString(R.string.general_error_please_report));
            } catch (TrackWithoutMeasurementsException e) {
                if (track.getNumberOfMeasurements() != 0) {
                    /*
                     * obfuscation removed all measurements
                     */
                    final Activity ac = ((ECApplication) context).getCurrentActivity();
                    if (ac != null) {
                        ac.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Crouton.makeText(ac,
                                        R.string.uploading_track_no_measurements_after_obfuscation_long,
                                        Style.ALERT).show();
                            }
                        });
                    }
                    ((ECApplication) context).createNotification(context.getResources()
                            .getString(R.string.uploading_track_no_measurements_after_obfuscation));
                } else {
                    logger.warn(e.getMessage(), e);
                }
                this.cancel(true);
            }

            if (file == null) {
                this.cancel(true);
                ((ECApplication) context)
                        .createNotification(context.getResources().getString(R.string.general_error_please_report));
            }

            //upload
            String httpResult = sendHttpPost(urlL, file, token, username);
            if (httpResult.equals(NET_ERROR)) {
                ((ECApplication) context)
                        .createNotification(context.getResources().getString(R.string.error_host_not_found));
            } else if (httpResult.equals(GENERAL_ERROR)) {
                ((ECApplication) context)
                        .createNotification(context.getResources().getString(R.string.general_error_please_report));
            } else {
                /*
                 * success, we got an ID
                 */
                ((ECApplication) context).createNotification("success");
                track.setRemoteID(httpResult);
                dbAdapter.updateTrack(track);

                if (callback != null) {
                    callback.onSuccessfulUpload(track);
                }
            }

            return track;
        }

    }

    public String getTrackJSON(Track track) throws JSONException, TrackWithoutMeasurementsException {
        /*
         * inject track metadata
         */
        track.updateMetadata(new TrackMetadata(context));

        return new TrackEncoder().createTrackJson(track, isObfuscationEnabled()).toString();
    }

    public boolean temporaryCarAlreadyRegistered(Track track) {
        if (temporaryAlreadyRegisteredCars.containsKey(track.getCar().getId())) {
            track.getCar().setId(temporaryAlreadyRegisteredCars.get(track.getCar().getId()));
            return true;
        }
        return false;
    }

    public boolean isObfuscationEnabled() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        return prefs.getBoolean(SettingsActivity.OBFUSCATE_POSITION, false);
    }

    /**
     * Uploads the json object to the server
     * 
     * @param url
     *            Url
     * @param contents
     *            The Json Object
     * @param xToken
     *            Token
     * @param xUser
     *            Username
     * @return Server response status code
     */
    private String sendHttpPost(String url, File contents, String xToken, String xUser) {

        try {
            HttpPost httpPostRequest = new HttpPost(url);

            FileEntity se = new FileEntity(contents, "application/json");
            se.setContentType("application/json");

            // Set HTTP parameters
            httpPostRequest.setEntity(se);
            httpPostRequest.setHeader("Content-type", "application/json");
            httpPostRequest.setHeader("X-Token", xToken);
            httpPostRequest.setHeader("X-User", xUser);

            HttpResponse response = (HttpResponse) HTTPClient.execute(httpPostRequest);

            String location = "";
            Header[] h = response.getAllHeaders();
            for (int i = 0; i < h.length; i++) {
                if (h[i].getName().equals("Location")) {
                    location += h[i].getValue();
                    break;
                }
            }

            String trackid = location.substring(location.lastIndexOf("/") + 1, location.length());

            int statusCode = response.getStatusLine().getStatusCode();

            logger.debug("Status Code: " + statusCode);

            if (statusCode < HttpStatus.SC_MULTIPLE_CHOICES) {
                HTTPClient.consumeEntity(response.getEntity());
                return trackid;
            } else {
                String errorResponse = HTTPClient.readResponse(response.getEntity());
                logger.warn("Server response: " + errorResponse);
                return GENERAL_ERROR;
            }

        } catch (UnsupportedEncodingException e) {
            logger.warn(e.getMessage(), e);
            return GENERAL_ERROR;
        } catch (IOException e) {
            logger.warn(e.getMessage(), e);
            return NET_ERROR;
        }
    }

    /**
     * Saves a json object to the sd card
     * 
     * @param obj
     *            the object to save
     * @param id 
     */
    private File saveToSdCard(String obj, String id) {
        File log = new File(context.getExternalFilesDir(null), "enviroCar-track-" + id + ".json");
        try {
            BufferedWriter out = new BufferedWriter(new FileWriter(log.getAbsolutePath(), false));
            out.write(obj);
            out.flush();
            out.close();
            return log;
        } catch (IOException e) {
            logger.warn(e.getMessage(), e);
        }
        return null;
    }

    public File saveTrackAndReturnFile(Track t) throws JSONException, TrackWithoutMeasurementsException {
        return saveToSdCard(getTrackJSON(t), (t.isRemoteTrack() ? t.getRemoteID() : Long.toString(t.getId())));
    }

}