uk.gov.ofwat.fountain.aspect.audit.AuditAdvice.java Source code

Java tutorial

Introduction

Here is the source code for uk.gov.ofwat.fountain.aspect.audit.AuditAdvice.java

Source

/*
 *  Copyright (C) 2009 Water Services Regulation Authority (Ofwat)
 *
 *  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; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package uk.gov.ofwat.fountain.aspect.audit;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import org.apache.commons.lang.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.bson.types.ObjectId;
import org.jboss.resteasy.annotations.providers.jaxb.json.BadgerFish;
import org.mongodb.morphia.Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;

import uk.gov.ofwat.fountain.api.MongoService;
import uk.gov.ofwat.fountain.audit.AuditExclusionStrategy;
import uk.gov.ofwat.fountain.dao.RestAuditDao;
import uk.gov.ofwat.fountain.domain.RestAudit;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mongodb.DBObject;
import com.mongodb.MongoException;
import com.mongodb.util.JSON;

//import org.elasticsearch.groovy.client.GClient;

@Path("/abc")
@Aspect
@Order(11)
public class AuditAdvice {

    private static int MAX_SIZE_BYTES_RESPONSE = 14000000;
    private static int MAX_SIZE_BYTES_POST = 1000000;
    //   private static org.apache.log4j.Logger log = Logger.getLogger(AuditAdvice.class);
    private final static Logger log = LoggerFactory.getLogger(AuditAdvice.class);
    //   private static Logger logger = LoggerFactory.getLogger(AuditAdvice.class);

    private RestAuditDao restAuditDao;
    private MongoService mongoService;
    private Gson gson;

    public MongoService getMongoService() {
        return mongoService;
    }

    public void setMongoService(MongoService mongoService) {
        this.mongoService = mongoService;
    }

    public void setRestAuditDao(RestAuditDao restAuditDao) {
        this.restAuditDao = restAuditDao;
    }

    private HttpServletRequest servletRequest;
    private HttpServletResponse servletResponse;

    @Context
    public void setHttpServeletRequest(HttpServletRequest request) {
        servletRequest = request;
    }

    @Context
    public void setHttpServeletResponse(HttpServletResponse response) {
        servletResponse = response;
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.RunResource.*(..))")
    private void run() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.ReportResource.*(..))")
    private void report() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.DataResource.*(..))")
    private void data() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.BannerResource.*(..))")
    private void banner() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.TagResource.*(..))")
    private void tag() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.FileResource.*(..))")
    private void file() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.ItemResource.*(..))")
    private void item() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.CheckoutResource.*(..))")
    private void checkout() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.CacheResource.*(..))")
    private void cache() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.TableResource.*(..))")
    private void table() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.LockResource.*(..))")
    private void lock() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.BasketResource.*(..))")
    private void basket() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.IntervalResource.*(..))")
    private void interval() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.AuditResource.*(..))")
    private void audit() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.ConfidenceGradeResource.*(..))")
    private void confidenceGrade() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.CompanyResource.*(..))")
    private void company() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.GroupResource.*(..))")
    private void group() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.ModelResource.*(..))")
    private void model() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.TeamResource.*(..))")
    private void team() {
    }

    @Pointcut("execution(public * uk.gov.ofwat.fountain.rest.WikiAddressResource.*(..))")
    private void wikiAddress() {
    }

    @Pointcut("wikiAddress() || " + "group() || " + "company() || " + "confidenceGrade() || " + "cache() || "
            + "lock() || " + "interval() || " +
            // "banner() || " +
            "file() || " + "run() || " + "report() || " + "data() || " + "tag() || " + "item() || "
            + "checkout() || " + "table() || " + "basket() || " + "audit() || " + "model() || " + "team()")
    private void all() {
    }

    /**
     * List of GET methods that will be audited as well as all POST, PUT above.
     */
    private ArrayList<String> auditedGetMethods = new ArrayList<String>(
            Arrays.asList("getReport", "getXmlReport", "getExcelReport", "getReportTable"));

    /**
     * Interceptor to audit the request and response of a call to the Fountain methods
     * marked above and the GEt methods in the list in 'auditedGetMethods'.
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("uk.gov.ofwat.fountain.aspect.audit.AuditAdvice.all()")
    public Object auditThis(ProceedingJoinPoint pjp) throws Throwable {
        log.debug("AuditAdvice ... Class: " + pjp.getTarget().getClass().getName() + " Method: "
                + pjp.getSignature().getName());

        // Check that we have a PUT POST or DELETE or GET to selected methods,
        // If not then we will ignore the audit.
        if (!servletRequest.getMethod().equals("PUT") && !servletRequest.getMethod().equals("POST")
                && !servletRequest.getMethod().equals("DELETE")
                && !auditedGetMethods.contains(pjp.getSignature().getName())) {
            return (Object) pjp.proceed();
        }

        Response response = null;
        Date startTime = new Date();
        try {
            response = (Response) pjp.proceed();
            RestAudit audit = doAudit(pjp, startTime, response);
            return response;
        } catch (WebApplicationException webEx) {
            log.error("Caught a WebApplicationException in auditAdvice: " + webEx.toString());
            RestAudit audit = doAudit(pjp, startTime, response, webEx);
            throw webEx;
        } catch (Exception e) {
            log.error("Caught an Exception in auditAdvice: " + e.toString());
            //return response;
            throw e;
        }
    }

    private RestAudit doAudit(ProceedingJoinPoint pjp, Date startTime, Response response) {
        return doAudit(pjp, startTime, response, null);
    }

    private RestAudit doAudit(ProceedingJoinPoint pjp, Date startTime, Response response, Exception e) {
        // Build a RestAudit object with a view to persisting.
        RestAudit restAudit = new RestAudit();
        restAudit.setHttpMethod(servletRequest.getMethod());
        restAudit.setUri(servletRequest.getRequestURI());
        restAudit.setUser(servletRequest.getUserPrincipal().getName());
        restAudit.setStartDate(startTime);
        restAudit.setFountainUsers(servletRequest.isUserInRole("OFWAT\\Fountain.Users"));
        restAudit.setFountainEditors(servletRequest.isUserInRole("OFWAT\\Fountain.Editors"));
        restAudit.setFountainAdmins(servletRequest.isUserInRole("OFWAT\\Fountain.Admins"));
        restAudit.setFountainRunAdmin(servletRequest.isUserInRole("OFWAT\\G Fountain Run Admin"));
        restAudit.setResourceClass("" + pjp.getTarget().getClass().getName());
        restAudit.setResourceMethod("" + pjp.getSignature().getName());

        // Get the content!
        HashMap<String, Object> postedContent = getContent(pjp);
        // Vars to keep track if we are being sent or receiving a TableDTO
        Boolean postedTableDto = false;
        Boolean responseTableDto = false;
        // Grab the passed parameters and audit.
        try {
            HashMap<String, String> params = getParams(pjp);
            //Persist the RestAudit Item to the MongoDB and get the Key.     
            Key<RestAudit> key = restAuditDao.createUpdate(restAudit);
            //Initialise GSON
            gson = getGson();
            //Turn posted content to JSON. 
            String jsonPostedContent = gson.toJson(postedContent);
            // We have to make special dispensation for tableDtos as they may be massive.
            if (params.get("tableDto") != null) {
                params.remove("tableDto"); // this is still audited as a JSON doc as the 'Content' and will only be in the params if it's a POST/PUT
                postedTableDto = true;
            }
            //If there is a tableDto, persist the posted content to GridFS then strip the tableDto.
            if (postedTableDto) {
                ObjectId contentMongoId = mongoService.persistToGridFS(jsonPostedContent,
                        servletRequest.getMethod() + ":" + pjp.getSignature().getName() + ".json",
                        "application/json", "RestAudit");
                restAudit.setAttachedGridFsContent(contentMongoId);
                restAudit.setContent((DBObject) JSON.parse("{'GridFSData':true}"));
            } else {
                DBObject contentDbo = (DBObject) JSON.parse(jsonPostedContent);
                restAudit.setContent(contentDbo);
            }
            //finish setting properties.
            restAudit.setAudits(getAudits(response));
            restAudit.setParams(params);
            restAuditDao.createUpdate(restAudit);

            //if e is not null we already have an exception, lets add it to the log.
            if ((e != null) && (e.getClass() == WebApplicationException.class)) {
                WebApplicationException webException = (WebApplicationException) e;
                response = webException.getResponse();
                restAudit.setResponseText(ExceptionUtils.getFullStackTrace(webException));
                restAudit.setResponseCode(Integer.toString(response.getStatus()));
            } else {
                if (response instanceof Response) {
                    restAudit.setResponseCode(Integer.toString(((Response) response).getStatus()));

                    // This entity can be a number of different DTOs, Lists or Domain Objects - we will build a JSON representation to save it.
                    Object responseEntity = ((Response) response).getEntity();
                    if ((responseEntity != null) && (responseEntity.getClass() != null)) {
                        Class responseEntityClass = responseEntity.getClass();
                        String responseEntityClassString = responseEntityClass.toString();
                        if (responseEntityClassString.equals("class uk.gov.ofwat.fountain.rest.dto.Report")
                                || responseEntityClassString
                                        .equals("class uk.gov.ofwat.fountain.rest.dto.TableDto")) {
                            responseTableDto = true;
                        }
                    }

                    String jsonResponseContent = gson.toJson(responseEntity);
                    //If we are sending a large DTO back persist this too!
                    if (responseTableDto) {
                        ObjectId reponseMongoId = mongoService.persistToGridFS(jsonResponseContent,
                                String.valueOf(response.getStatus()) + ":" + pjp.getSignature().getName() + ".json",
                                "application/json", "RestAudit");
                        restAudit.setAttachedGridFsResponse(reponseMongoId);
                        restAudit.setResponseEntity((DBObject) JSON.parse("{'GridFSData':true}"));
                    } else {
                        HashMap<String, Object> responseEntityMap = new HashMap<String, Object>();
                        responseEntityMap.put("entity", responseEntity);
                        String jsonResponse = gson.toJson(responseEntityMap);
                        restAudit.setResponseEntity((DBObject) JSON.parse(jsonResponse));
                    }
                }
            }
            restAudit.setEndDate(new Date());
            restAuditDao.createUpdate(restAudit);
        } catch (Exception e1) {
            // log the exception
            if (MongoException.class.isAssignableFrom(e1.getClass())) {
                log.error("\n\n\n*** Potentially not logged anything - caught a Mongo Exception:" + e1.toString()
                        + "\n\n\n***");
            } else {
                try {
                    log.error("\n\n\n*** Caught an exception in audit advice, trying to persist a record: "
                            + e1.toString() + " ***\n\n\n");
                    restAudit.setResponseText(ExceptionUtils.getFullStackTrace(e1));
                    restAudit.setResponseCode("ERR");
                    restAudit.setEndDate(new Date());
                    restAuditDao.createUpdate(restAudit);
                } catch (Exception e2) {
                    log.error(
                            "\n\n\n*** Unable to persist audit record correctly: " + e2.toString() + " ***\n\n\n");
                }
            }
        }
        return restAudit;
    }

    private Gson getGson() {
        if (gson == null) {
            gson = new GsonBuilder().setExclusionStrategies(new AuditExclusionStrategy()).create();
        }
        return gson;
    }

    private DBObject getAudits(Response response) {
        //Get a list of the Fountain audits that have been placed on the response if any! 
        if (response != null) {
            MultivaluedMap<String, Object> metaData = response.getMetadata();
            List<Object> audits = metaData.get("audits");
            metaData.remove("audits");
            //Change audits to DBObject
            if ((audits != null) && (audits.get(0) != null)) {
                DBObject auditsDBObject = (DBObject) JSON.parse(getGson().toJson(audits.get(0)));
                //restAudit.setAudits(auditsDBObject);
                return auditsDBObject;
            }
        }
        return null;
    }

    private HashMap<String, String> getParams(ProceedingJoinPoint pjp) {
        HashMap<String, String> params = new HashMap<String, String>();
        Object[] args = pjp.getArgs();
        Signature signature = pjp.getStaticPart().getSignature();
        if (signature instanceof MethodSignature) {
            MethodSignature ms = (MethodSignature) signature;
            Method method = ms.getMethod();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();

            String[] parameterNames = ms.getParameterNames();
            for (int i = 0; i < args.length; i++) {
                String argsString = "";
                if (args[i] != null) {
                    argsString = args[i].toString();
                }
                params.put(parameterNames[i], argsString);
            }
        }
        return params;
    }

    private HashMap<String, Object> getContent(ProceedingJoinPoint pjp) {
        HashMap<String, Object> content = new HashMap<String, Object>();
        Object[] args = pjp.getArgs();
        Signature signature = pjp.getStaticPart().getSignature();
        if (signature instanceof MethodSignature) {
            MethodSignature ms = (MethodSignature) signature;
            Method method = ms.getMethod();
            Annotation[][] parameterAnnotations = method.getParameterAnnotations();
            String[] parameterNames = ms.getParameterNames();
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int i = 0; i < parameterAnnotations.length; i++) {
                if (org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput.class == parameterTypes[i]
                        || javax.ws.rs.core.SecurityContext.class == parameterTypes[i]
                        || javax.ws.rs.core.UriInfo.class == parameterTypes[i]
                        || javax.servlet.http.HttpServletRequest.class == parameterTypes[i]) {
                    continue;
                }
                content.put(parameterNames[i], args[i]);
            }
        }
        return content;
    }

    private String getBadgerFishParameterValue(String paramName, Object object, Annotation[] annotations) {
        String value = "";
        for (Annotation annotation : annotations) {
            if (annotation instanceof BadgerFish) {
                // exclusions:
                // @BadgerFish List<Lock> locks
                BadgerFish myAnnotation = (BadgerFish) annotation;
                value = value + object.toString();
                System.out.println("BadgerFish type: " + object.getClass().getSimpleName() + " name: " + paramName
                        + " value: " + object.toString());
            }
        }
        return value;
    }

}