org.ameba.aop.ServiceLayerAspect.java Source code

Java tutorial

Introduction

Here is the source code for org.ameba.aop.ServiceLayerAspect.java

Source

/*
 * Copyright 2014-2015 the original author or 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
 * 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.ameba.aop;

import java.util.Optional;
import java.util.stream.Stream;

import org.ameba.LoggingCategories;
import org.ameba.exception.BusinessRuntimeException;
import org.ameba.exception.ServiceLayerException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;

/**
 * A ServiceLayerAspect is spawned around all public API methods and is responsible to log method execution time and log occurring
 * exceptions around the service layer.
 *
 * @author <a href="mailto:scherrer@openwms.org">Heiko Scherrer</a>
 * @version 0.1
 * @since 1.2
 */
@Aspect
@Order(15)
public class ServiceLayerAspect {

    /** Springs component name. */
    public static final String COMPONENT_NAME = "ServiceLayerAspect";
    private static final Logger SRV_LOGGER = LoggerFactory.getLogger(LoggingCategories.SERVICE_LAYER_ACCESS);
    private static final Logger EXC_LOGGER = LoggerFactory.getLogger(LoggingCategories.SERVICE_LAYER_EXCEPTION);
    private static final Logger BOOT_LOGGER = LoggerFactory.getLogger(LoggingCategories.BOOT);
    private boolean withRootCause = false;

    /** Default constructor with some loginfo. */
    public ServiceLayerAspect() {
        BOOT_LOGGER.info("-- w/ " + COMPONENT_NAME);
    }

    /** Constructor with some loginfo and considering the root cause. */
    public ServiceLayerAspect(boolean withRootCause) {
        BOOT_LOGGER.info("-- w/ " + COMPONENT_NAME);
        this.withRootCause = withRootCause;
    }

    /**
     * Around intercepted methods do some logging and exception translation. <p> <ul> <li> Set log level of {@link
     * LoggingCategories#SERVICE_LAYER_ACCESS} to INFO to enable method tracing. <li>Set log level of {@link
     * LoggingCategories#SERVICE_LAYER_EXCEPTION} to ERROR to enable exception logging. </ul> </p>
     *
     * @param pjp The joinpoint
     * @return Method return value
     * @throws Throwable in case of errors
     */
    @Around("org.ameba.aop.Pointcuts.servicePointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long startMillis = 0L;
        if (SRV_LOGGER.isDebugEnabled()) {
            SRV_LOGGER.debug("[S]>> Method call: {}", pjp.toShortString());
            startMillis = System.currentTimeMillis();
        }

        Object obj = null;
        try {
            obj = pjp.proceed();
        } catch (Exception ex) {
            Exception e = translateException(ex);
            if (EXC_LOGGER.isErrorEnabled()) {
                EXC_LOGGER.error(e.getLocalizedMessage(), e);
            }
            throw e;
        } finally {
            if (SRV_LOGGER.isDebugEnabled()) {
                SRV_LOGGER.debug("[S]<< {} took {} [ms]", pjp.toShortString(),
                        (System.currentTimeMillis() - startMillis));
            }
        }
        return obj;
    }

    /**
     * Called after an exception is thrown by classes of the service layer. <p> Set log level to ERROR to log the root cause. </p>
     *
     * @param ex The root exception that is thrown
     * @return Returns the exception to be thrown
     */
    public Exception translateException(Exception ex) {
        if (ex instanceof BusinessRuntimeException) {
            BusinessRuntimeException bre = (BusinessRuntimeException) ex;
            MDC.put(LoggingCategories.MSGKEY, bre.getMsgKey());
            if (bre.getData() != null) {
                MDC.put(LoggingCategories.MSGDATA,
                        String.join(",", Stream.of(bre.getData()).map(Object::toString).toArray(String[]::new)));
            }
            // cleanup of context is done in SLF4JMappedDiagnosticContextFilter
            return bre;
        }

        Optional<Exception> handledException = doTranslateException(ex);
        if (handledException.isPresent()) {
            return handledException.get();
        }
        if (ex instanceof ServiceLayerException) {
            return ex;
        }
        return withRootCause ? new ServiceLayerException(ex.getMessage(), ex)
                : new ServiceLayerException(ex.getMessage());
    }

    /**
     * Override method to handle the transaction yourself and skip to the default exception handling .
     *
     * @param ex Exception to handle
     * @return An empty Optional to use the default exception handling or an Exception to skip default handling
     */
    protected Optional<Exception> doTranslateException(Exception ex) {
        return Optional.empty();
    }
}