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