org.projectbuendia.client.diagnostics.BuendiaApiHealthCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.projectbuendia.client.diagnostics.BuendiaApiHealthCheck.java

Source

// Copyright 2015 The Project Buendia 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 distrib-
// uted 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
// specific language governing permissions and limitations under the License.

package org.projectbuendia.client.diagnostics;

import android.app.Application;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

import org.apache.http.HttpResponse;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.projectbuendia.client.model.Concepts;
import org.projectbuendia.client.net.OpenMrsConnectionDetails;
import org.projectbuendia.client.utils.Logger;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.UnknownHostException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * A {@link HealthCheck} that checks whether the Buendia API server is up and responding to HTTP
 * requests at the URL in the "OpenMRS root URL" preference setting.
 */
public class BuendiaApiHealthCheck extends HealthCheck {

    private static final Logger LOG = Logger.create();

    // Under normal conditions, make requests to the server with this frequency
    // to check if it's reachable and responding.
    private static final int CHECK_PERIOD_MS = 20000;

    // During certain problem conditions, check more often so that when the
    // problem is resolved, we can hide the snackbar more promptly.
    private static final int FAST_CHECK_PERIOD_MS = 10000;

    // These are the issues for which we use the faster checking period.
    private static final Set<HealthIssue> FAST_CHECK_ISSUES = ImmutableSet.of(HealthIssue.SERVER_HOST_UNREACHABLE,
            HealthIssue.SERVER_NOT_RESPONDING);

    // Retrieving a concept should be quick and ensures that the module is both
    // running and has database access.
    private static final String HEALTH_CHECK_ENDPOINT = "/concept/" + Concepts.GENERAL_CONDITION_UUID;

    private final Object mLock = new Object();

    private final OpenMrsConnectionDetails mConnectionDetails;

    private HandlerThread mHandlerThread;
    private Handler mHandler;
    private BuendiaModuleHealthCheckRunnable mRunnable;

    BuendiaApiHealthCheck(Application application, OpenMrsConnectionDetails connectionDetails) {
        super(application);

        mConnectionDetails = connectionDetails;
    }

    @Override
    protected void startImpl() {
        synchronized (mLock) {
            if (mHandlerThread == null) {
                mHandlerThread = new HandlerThread("Buendia API Health Check");
                mHandlerThread.start();
                mHandler = new Handler(mHandlerThread.getLooper());
            }

            if (mRunnable == null) {
                mRunnable = new BuendiaModuleHealthCheckRunnable(mHandler);
            }

            if (!mRunnable.isRunning.getAndSet(true)) {
                mHandler.post(mRunnable);
            }
        }
    }

    @Override
    protected void stopImpl() {
        synchronized (mLock) {
            if (mRunnable != null) {
                mRunnable.isRunning.set(false);
                mRunnable = null;
            }

            if (mHandlerThread != null) {
                mHandlerThread.quit();
                mHandlerThread = null;
            }

            mHandler = null;
        }
    }

    protected int getCheckPeriodMillis() {
        return Sets.intersection(FAST_CHECK_ISSUES, mActiveIssues).isEmpty() ? CHECK_PERIOD_MS
                : FAST_CHECK_PERIOD_MS;
    }

    private class BuendiaModuleHealthCheckRunnable implements Runnable {
        public final AtomicBoolean isRunning;

        private final Handler mHandler;

        public BuendiaModuleHealthCheckRunnable(Handler handler) {
            isRunning = new AtomicBoolean(false);
            mHandler = handler;
        }

        @Override
        public void run() {
            if (!isRunning.get()) {
                return;
            }

            try {
                String uriString = mConnectionDetails.getBuendiaApiUrl() + HEALTH_CHECK_ENDPOINT;
                Uri uri = Uri.parse(uriString);
                if (uri.getHost() == null) {
                    LOG.w("The configured OpenMRS API URL '%1$s' is invalid.", uriString);
                    reportIssue(HealthIssue.SERVER_CONFIGURATION_INVALID);
                    return;
                }

                try {
                    HttpClient httpClient = new DefaultHttpClient();
                    HttpGet httpGet = new HttpGet(uri.toString());
                    httpGet.addHeader(BasicScheme.authenticate(new UsernamePasswordCredentials(
                            mConnectionDetails.getUser(), mConnectionDetails.getPassword()), "UTF-8", false));

                    HttpResponse httpResponse = httpClient.execute(httpGet);
                    if (httpResponse.getStatusLine().getStatusCode() != HttpURLConnection.HTTP_OK) {
                        LOG.w("The OpenMRS URL '%1$s' returned unexpected error code: %2$s", uriString,
                                httpResponse.getStatusLine().getStatusCode());
                        switch (httpResponse.getStatusLine().getStatusCode()) {
                        case HttpURLConnection.HTTP_INTERNAL_ERROR:
                            reportIssue(HealthIssue.SERVER_INTERNAL_ISSUE);
                            break;
                        case HttpURLConnection.HTTP_FORBIDDEN:
                        case HttpURLConnection.HTTP_UNAUTHORIZED:
                            reportIssue(HealthIssue.SERVER_AUTHENTICATION_ISSUE);
                            break;
                        case HttpURLConnection.HTTP_NOT_FOUND:
                        default:
                            reportIssue(HealthIssue.SERVER_NOT_RESPONDING);
                            break;
                        }
                        return;
                    }
                } catch (UnknownHostException e) {
                    reportIssue(HealthIssue.SERVER_HOST_UNREACHABLE);
                    return;
                } catch (IOException e) {
                    LOG.w("Could not perform OpenMRS health check using URL '%1$s'.", uriString);
                    return;
                }

                resolveAllIssues();
            } finally {
                mHandler.postDelayed(this, getCheckPeriodMillis());
            }
        }
    }
}