io.morea.handy.android.SendLogTask.java Source code

Java tutorial

Introduction

Here is the source code for io.morea.handy.android.SendLogTask.java

Source

/*
 * Copyright 2015 the original author or authors.
 *
 * 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 io.morea.handy.android;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.squareup.okhttp.Authenticator;
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.net.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import static io.morea.handy.android.EventDatabaseOpenHelper.Metadata.Events;

/**
 * Sends all logged events to the configured server. Successfully transferred events will be removed from the local log
 * afterwards.
 *
 * @author Andreas Ahlenstorf
 */
class SendLogTask implements Runnable {

    private static final Logger log = Logger.getLogger(SendLogTask.class.getName());

    private static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8; version=1");

    private final Context context;

    private final EventReporterConfiguration configuration;

    private final OkHttpClient client;

    private final Gson gson;

    public SendLogTask(final Context context, final EventReporterConfiguration configuration) {
        // Don't make Context a WeakReference, otherwise the reference could get collected before the task is actually
        // run and the task should be executed in any case. There's also no risk to create a memory leak here because
        // the task will be thrown away afterwards anyway.
        this.context = context;
        this.configuration = configuration;

        client = new OkHttpClient();
        client.setConnectTimeout(configuration.getHttpTimeout(), TimeUnit.MILLISECONDS);
        client.setWriteTimeout(configuration.getHttpTimeout(), TimeUnit.MILLISECONDS);
        client.setReadTimeout(configuration.getHttpTimeout(), TimeUnit.MILLISECONDS);
        client.setAuthenticator(new Authenticator() {
            @Override
            public Request authenticate(Proxy proxy, Response response) {
                String credential = Credentials.basic(configuration.getProbeId(), configuration.getAccessKey());
                return response.request().newBuilder().header("Authorization", credential).build();
            }

            @Override
            public Request authenticateProxy(Proxy proxy, Response response) {
                return null;
            }
        });

        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Date.class, new DateSerializer());
        this.gson = gsonBuilder.create();
    }

    @Override
    public void run() {
        SQLiteDatabase db = null;
        try {
            EventDatabaseOpenHelper helper = new EventDatabaseOpenHelper(this.context);
            db = helper.getWritableDatabase();

            db.beginTransaction();
            try {
                final Set<UUID> sentEvents = new HashSet<>();
                final Cursor cursor = db.query(Events.TABLE_NAME, Events.orderedColumns(), null, null, null, null,
                        Events.DATE_RECORDED);

                try {
                    final List<Event> events = new ArrayList<>();
                    while (cursor.moveToNext()) {
                        events.add(Event.fromCursor(cursor));

                        if (events.size() == configuration.getSyncBatchSize()) {
                            log.log(Level.FINE,
                                    String.format("Batch size %d reached.", configuration.getSyncBatchSize()));
                            sentEvents.addAll(sendEvents(events));
                            events.clear();
                        }
                    }

                    sentEvents.addAll(sendEvents(events));
                } finally {
                    Util.closeQuietly(cursor);
                }

                removeSentEvents(db, sentEvents);

                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        } catch (Exception e) {
            log.log(Level.SEVERE, "Failed to send events to server.", e);
        } finally {
            Util.closeQuietly(db);
        }
    }

    private Set<UUID> sendEvents(final List<Event> events) throws Exception {
        final Set<UUID> sentEvents = new HashSet<>();

        if (events.isEmpty()) {
            return sentEvents;
        }

        log.log(Level.INFO,
                String.format("Sending %d events to server %s.", events.size(), configuration.getRemoteEndpoint()));

        final Request request = new Request.Builder().url(configuration.getRemoteEndpoint())
                .post(RequestBody.create(MEDIA_TYPE_JSON, gson.toJson(events)))
                .addHeader("User-Agent", Constant.USER_AGENT).build();

        final Response response = client.newCall(request).execute();

        if (!response.isSuccessful()) {
            log.log(Level.SEVERE,
                    String.format("Failed to post events to server, response status is %d.", response.code()));
            return sentEvents;
        }

        for (Event e : events) {
            log.log(Level.FINE, String.format("Event %s successfully sent to server.", e.getIdentifier()));
            sentEvents.add(e.getIdentifier());
        }

        return sentEvents;
    }

    private void removeSentEvents(SQLiteDatabase db, Collection<UUID> sentEvents) {
        for (UUID uuid : sentEvents) {
            log.log(Level.FINE, String.format("Removing event %s.", uuid));
            db.delete(Events.TABLE_NAME, Events.IDENTIFIER + " = ?", new String[] { uuid.toString() });
        }
    }
}