org.ohmage.reporting.AuditReporter.java Source code

Java tutorial

Introduction

Here is the source code for org.ohmage.reporting.AuditReporter.java

Source

/*******************************************************************************
 * Copyright 2012 The Regents of the University of California
 * 
 * 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 org.ohmage.reporting;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.json.JSONException;
import org.json.JSONObject;
import org.ohmage.cache.PreferenceCache;
import org.ohmage.domain.Audit;
import org.ohmage.exception.CacheMissException;
import org.ohmage.exception.ServiceException;
import org.ohmage.request.InputKeys;
import org.ohmage.request.RequestBuilder;
import org.ohmage.service.AuditServices;
import org.ohmage.util.DateTimeUtils;

/**
 * Begins on server startup and creates a daily snapshot of some information
 * from the audit table.
 * 
 * @author John Jenkins
 */
public final class AuditReporter {
    private static final Logger LOGGER = Logger.getLogger(AuditReporter.class);

    // Used by the timer.
    private static final int MILLIS_IN_A_DAY = 1000 * 60 * 60 * 24;

    // The number of idle threads ready to generate the report. Two reports 
    // should never be generated at the same time, so we only need one thread
    // to handle this.
    private static final int THREAD_POOL_SIZE = 1;

    // Where we will save the audit reports.
    private static String saveLocation;

    /**
     * The class that runs as its own thread to generate the report.
     * 
     * @author John Jenkins
     */
    private final class GenerateReport implements Runnable {
        /**
         * Generates a report for the previous day.
         */
        @Override
        public void run() {
            DateTime currentDate = new DateTime();

            DateTime endDate = new DateTime(currentDate.getYear(), currentDate.getMonthOfYear(),
                    currentDate.getDayOfMonth(), 0, 0);

            DateTime startDate = endDate.minusDays(1);

            // Use the service to aggregate the results.
            List<Audit> audits;
            try {
                audits = AuditServices.instance().getAuditInformation(null, null, null, null, null, null, startDate,
                        endDate);
            } catch (ServiceException e) {
                LOGGER.error("There was an error generating the audit information.", e);
                return;
            }

            long numberOfValidRequests = 0;
            long numberOfInvalidRequests = 0;
            long numberOfSuccessfulValidRequests = 0;
            long numberOfFailedValidRequests = 0;
            long timeToProcessValidRequests = 0;

            Map<String, Integer> numberUriRequests = new HashMap<String, Integer>();
            Map<String, Integer> numberCampaignReads = new HashMap<String, Integer>();
            Map<String, Integer> numberClassReads = new HashMap<String, Integer>();

            // Cycle through all of the audit entries.
            for (Audit audit : audits) {
                // First, get the URI and determine if the request is even 
                // valid.
                String uri = audit.getUri();

                // Either way, make a note of it in the list of URIs.
                Integer uriCount = numberUriRequests.get(uri);
                if (uriCount == null) {
                    numberUriRequests.put(uri, 1);
                } else {
                    numberUriRequests.put(uri, uriCount + 1);
                }

                // If the request is known, note it and continue processing.
                if (RequestBuilder.getInstance().knownUri(uri)) {
                    numberOfValidRequests++;

                    // Calculate the time it took to process the request.
                    timeToProcessValidRequests += audit.getRespondedMillis() - audit.getReceivedMillis();

                    // Get the audit's response. If there is an issue parsing
                    // the response, note it and continue to the next audit.
                    JSONObject response = audit.getResponse();
                    String result;
                    try {
                        result = response.getString("result");
                    } catch (JSONException e) {
                        LOGGER.error("Error reading an audit's response.");
                        continue;
                    }

                    // If the request was successful, continue evaluating the
                    // audit to see if any other substantial data was returned.
                    if ("success".equals(result)) {
                        numberOfSuccessfulValidRequests++;

                        // Check if it's a class read request.
                        if (RequestBuilder.getInstance().getApiClassRead().equals(uri)
                                || RequestBuilder.getInstance().getApiClassRosterRead().equals(uri)) {
                            // Get the class ID parameter if it exists.
                            Collection<String> classIdCollection = audit.getExtras(InputKeys.CLASS_URN);
                            if (classIdCollection != null) {
                                for (String classId : classIdCollection) {
                                    Integer count = numberClassReads.get(classId);
                                    if (count == null) {
                                        numberClassReads.put(classId, 1);
                                    } else {
                                        numberClassReads.put(classId, count + 1);
                                    }
                                }
                            }
                        }
                        // Check if it's a campaign read request.
                        else if (RequestBuilder.getInstance().getApiCampaignRead().equals(uri)) {
                            Collection<String> campaignIdCollection = audit.getExtras(InputKeys.CAMPAIGN_URN);
                            if (campaignIdCollection != null) {
                                for (String campaignId : campaignIdCollection) {
                                    Integer count = numberCampaignReads.get(campaignId);
                                    if (count == null) {
                                        numberCampaignReads.put(campaignId, 1);
                                    } else {
                                        numberCampaignReads.put(campaignId, count + 1);
                                    }
                                }
                            }
                        }
                    }
                    // If the request was unsuccessful, note it and move on to
                    // the next request.
                    else {
                        numberOfFailedValidRequests++;
                    }
                }
                // If the request is unknown, note it and move on to the next
                // request.
                else {
                    numberOfInvalidRequests++;
                }
            }

            try {
                // Retrieve the output file to write the results.
                FileWriter fileWriter = new FileWriter(
                        saveLocation + "/" + DateTimeUtils.getIso8601DateString(startDate, false));
                BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

                bufferedWriter.write("invalid_requests=");
                bufferedWriter.write(Long.toString(numberOfInvalidRequests));
                bufferedWriter.write('\n');

                bufferedWriter.write("valid_requests=");
                bufferedWriter.write(Long.toString(numberOfValidRequests));
                bufferedWriter.write('\n');

                bufferedWriter.write("successful_valid_requests=");
                bufferedWriter.write(Long.toString(numberOfSuccessfulValidRequests));
                bufferedWriter.write('\n');

                bufferedWriter.write("failed_valid_requests=");
                bufferedWriter.write(Long.toString(numberOfFailedValidRequests));
                bufferedWriter.write('\n');

                bufferedWriter.write("average_request_time=");
                if (numberOfValidRequests == 0) {
                    bufferedWriter.write('0');
                } else {
                    bufferedWriter.write(Long.toString(timeToProcessValidRequests / numberOfValidRequests));
                }
                bufferedWriter.write('\n');

                bufferedWriter.write("number of requests per uri\n");
                for (String uri : numberUriRequests.keySet()) {
                    bufferedWriter.write(uri);
                    bufferedWriter.write('=');
                    bufferedWriter.write(numberUriRequests.get(uri).toString());
                    bufferedWriter.write('\n');
                }

                bufferedWriter.write("number of reads per campaign ID\n");
                for (String campaignId : numberCampaignReads.keySet()) {
                    bufferedWriter.write(campaignId);
                    bufferedWriter.write('=');
                    bufferedWriter.write(numberCampaignReads.get(campaignId).toString());
                    bufferedWriter.write('\n');
                }

                bufferedWriter.write("number of reads per class ID\n");
                for (String classId : numberClassReads.keySet()) {
                    bufferedWriter.write(classId);
                    bufferedWriter.write('=');
                    bufferedWriter.write(numberClassReads.get(classId).toString());
                    bufferedWriter.write('\n');
                }

                bufferedWriter.flush();
                bufferedWriter.close();
                fileWriter.close();
            } catch (IOException e) {
                LOGGER.error("Error while writing the audit information.", e);
            }
        }
    }

    /**
     * Starts a timer task to generate a report at the beginning of every day.
     */
    private AuditReporter() {
        try {
            // Get the location to save the audit logs.
            saveLocation = PreferenceCache.instance().lookup(PreferenceCache.KEY_AUDIT_LOG_LOCATION);
        } catch (CacheMissException e) {
            throw new IllegalStateException(
                    "The audit log location is missing: " + PreferenceCache.KEY_AUDIT_LOG_LOCATION, e);
        }

        try {
            // If it doesn't exist, create it. If it does exist, make sure it's a
            // directory.
            File saveFolder = new File(saveLocation);
            if (!saveFolder.exists()) {
                saveFolder.mkdir();
            } else if (!saveFolder.isDirectory()) {
                throw new IllegalStateException(
                        "The directory that is to be used for saving the audit reports exists but isn't a directory: "
                                + saveLocation);
            }
        } catch (SecurityException e) {
            throw new IllegalStateException("We are not allowed to read or write in the specified directory.", e);
        }

        // Generate the number of milliseconds until the first run.
        Calendar firstRun = Calendar.getInstance();
        // Fast-forward to the beginning of the next day.
        firstRun.add(Calendar.DAY_OF_YEAR, 1);
        // Reset the hours, minutes, seconds, and milliseconds.
        firstRun.set(Calendar.HOUR_OF_DAY, 0);
        firstRun.set(Calendar.MINUTE, 0);
        firstRun.set(Calendar.SECOND, 0);
        firstRun.set(Calendar.MILLISECOND, 0);

        // Calculate the time between now and when the task should first run.
        long initialDelay = firstRun.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();

        // Begin the task.
        Executors.newScheduledThreadPool(THREAD_POOL_SIZE).scheduleAtFixedRate(new GenerateReport(), initialDelay,
                MILLIS_IN_A_DAY, TimeUnit.MILLISECONDS);
    }
}