/* ==================================================================== * * Copyright (C) 2007 - 2015 GeoSolutions S.A.S. * http://www.geo-solutions.it * * GPLv3 + Classpath exception * * 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 2 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. * * ==================================================================== * * This software consists of voluntary contributions made by developers * of GeoSolutions. For more information on GeoSolutions, please see * <http://www.geo-solutions.it/>. * */ package it.geosolutions.geostore.services.rest.auditing; import org.apache.log4j.Logger; import java.io.FileWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; final class AuditingOutput { private static final Logger LOGGER = Logger.getLogger(AuditingOutput.class); private final BlockingQueue<Map<String, String>> messagesQueue = new ArrayBlockingQueue<Map<String, String>>(10000); private AuditingConfiguration configuration; private AuditingTemplates templates; private AuditingFilesManager auditingFilesManager; private boolean auditEnable = false; private FileWriter writer; private int requestsProcessed; AuditingOutput() { if (AuditingConfiguration.configurationExists()) { configuration = new AuditingConfiguration(); if (configuration.isAuditEnable()) { LOGGER.info("Auditing enable."); auditEnable = true; templates = new AuditingTemplates(configuration.getTemplatesDirectory()); auditingFilesManager = new AuditingFilesManager( configuration.getOutputDirectory(), configuration.getOutputFilesExtension()); openWriter(); final Consumer consumer = new Consumer(); final Thread consumerThread = new Thread(consumer); consumerThread.start(); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { consumer.running = false; try { consumerThread.interrupt(); consumerThread.join(500); } catch (InterruptedException exception) { LOGGER.error("Interrupted when waiting for consumer thread.", exception); } closeWriter(); } }); } else { LOGGER.info("Auditing not enable."); } } else { LOGGER.info("Auditing configuration not found, audit disabled."); } } void offerMessage(Map<String, String> message) { if (auditEnable) { try { messagesQueue.offer(message); } catch (Exception exception) { LOGGER.error("Error offering message.", exception); } } } boolean isAuditEnable() { return auditEnable; } AuditingFilesManager getAuditingFilesManager() { return auditingFilesManager; } private void openWriter() { try { writer = new FileWriter(auditingFilesManager.getOutputFile()); } catch (Exception exception) { throw new AuditingException(exception, "Error open writer for file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } try { templates.getHeaderTemplate().process(Collections.EMPTY_MAP, writer); } catch (Exception exception) { throw new AuditingException(exception, "Error writing header to file '%s'.", auditingFilesManager.getOutputFile().getPath()); } } private void closeWriter() { try { templates.getFooterTemplate().process(Collections.EMPTY_MAP, writer); } catch (Exception exception) { throw new AuditingException("Error writing footer to file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } try { writer.close(); } catch (Exception exception) { throw new AuditingException("Error closing writer for file output '%s'.", auditingFilesManager.getOutputFile().getPath()); } } private void processMessage(Map<String, String> message) { auditingFilesManager.makeOutputFileExists(); try { message.put("id", String.valueOf(requestsProcessed)); templates.getBodyTemplate().process(message, writer); } catch (Exception exception) { LOGGER.error("Error writing to body template.", exception); } requestsProcessed++; if (requestsProcessed >= configuration.getMaxRequestPerFile()) { closeWriter(); auditingFilesManager.rollOutputFile(); openWriter(); requestsProcessed = 0; } } private class Consumer implements Runnable { volatile boolean running = true; @Override public void run() { while (running) { List<Map<String, String>> messages = new ArrayList<Map<String, String>>(); try { if (messagesQueue.isEmpty()) { messages.add(messagesQueue.take()); } else { messagesQueue.drainTo(messages); } } catch (InterruptedException exception) { Thread.currentThread().interrupt(); } for (Map<String, String> message : messages) { try { processMessage(message); } catch (Exception exception) { LOGGER.error("Error processing message.", exception); } } } } } }