kieker.monitoring.probe.aspectj.jersey.OperationExecutionJerseyClientInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for kieker.monitoring.probe.aspectj.jersey.OperationExecutionJerseyClientInterceptor.java

Source

/***************************************************************************
 * Copyright 2015 Kieker Project (http://kieker-monitoring.net)
 *
 * 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 kieker.monitoring.probe.aspectj.jersey;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;

import com.sun.jersey.api.client.ClientRequest;
import com.sun.jersey.api.client.ClientResponse;

import kieker.common.logging.Log;
import kieker.common.logging.LogFactory;
import kieker.common.record.controlflow.OperationExecutionRecord;
import kieker.monitoring.core.controller.IMonitoringController;
import kieker.monitoring.core.controller.MonitoringController;
import kieker.monitoring.core.registry.ControlFlowRegistry;
import kieker.monitoring.core.registry.SessionRegistry;
import kieker.monitoring.probe.aspectj.AbstractAspectJProbe;
import kieker.monitoring.timer.ITimeSource;

/**
 * @author Teerat Pitakrat
 *
 * @since 1.12
 */
@Aspect
@DeclarePrecedence("kieker.monitoring.probe.aspectj.operationExecution.*,kieker.monitoring.probe.aspectj.jersey.*")
public class OperationExecutionJerseyClientInterceptor extends AbstractAspectJProbe {
    public static final String SESSION_ID_ASYNC_TRACE = "NOSESSION-ASYNCIN";

    private static final Log LOG = LogFactory.getLog(OperationExecutionJerseyClientInterceptor.class);

    private static final IMonitoringController CTRLINST = MonitoringController.getInstance();
    private static final ITimeSource TIME = CTRLINST.getTimeSource();
    private static final String VMNAME = CTRLINST.getHostname();
    private static final ControlFlowRegistry CF_REGISTRY = ControlFlowRegistry.INSTANCE;
    private static final SessionRegistry SESSION_REGISTRY = SessionRegistry.INSTANCE;

    /**
     * Default constructor.
     */
    public OperationExecutionJerseyClientInterceptor() {
        // empty default constructor
    }

    /**
     * Method to intercept outgoing request and incoming response.
     *
     * @return value of the intercepted method
     */
    @Around("execution(public com.sun.jersey.api.client.ClientResponse com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(com.sun.jersey.api.client.ClientRequest))")
    public Object operation(final ProceedingJoinPoint thisJoinPoint) throws Throwable { // NOCS (Throwable)
        if (!CTRLINST.isMonitoringEnabled()) {
            return thisJoinPoint.proceed();
        }
        final String signature = this.signatureToLongString(thisJoinPoint.getSignature());
        if (!CTRLINST.isProbeActivated(signature)) {
            return thisJoinPoint.proceed();
        }

        boolean entrypoint = true;
        final String hostname = VMNAME;
        final String sessionId = SESSION_REGISTRY.recallThreadLocalSessionId();
        final int eoi; // this is executionOrderIndex-th execution in this trace
        final int ess; // this is the height in the dynamic call tree of this execution
        final int nextESS;
        long traceId = CF_REGISTRY.recallThreadLocalTraceId(); // traceId, -1 if entry point
        if (traceId == -1) {
            entrypoint = true;
            traceId = CF_REGISTRY.getAndStoreUniqueThreadLocalTraceId();
            CF_REGISTRY.storeThreadLocalEOI(0);
            CF_REGISTRY.storeThreadLocalESS(1); // next operation is ess + 1
            eoi = 0;
            ess = 0;
            nextESS = 1;
        } else {
            entrypoint = false;
            eoi = CF_REGISTRY.incrementAndRecallThreadLocalEOI();
            ess = CF_REGISTRY.recallAndIncrementThreadLocalESS();
            nextESS = ess + 1;
            if ((eoi == -1) || (ess == -1)) {
                LOG.error("eoi and/or ess have invalid values:" + " eoi == " + eoi + " ess == " + ess);
                CTRLINST.terminateMonitoring();
            }
        }

        // Get request header
        final Object[] args = thisJoinPoint.getArgs();
        final ClientRequest request = (ClientRequest) args[0];
        final URI uri = request.getURI();
        // LOG.info("URI = " + uri.toString());

        // This is a hack to put all values in the header
        MultivaluedMap<String, Object> requestHeader = request.getHeaders();
        if (requestHeader == null) {
            requestHeader = new MultivaluedHashMap<String, Object>();
        }

        final List<Object> requestHeaderList = new ArrayList<Object>(4);
        requestHeaderList.add(Long.toString(traceId) + "," + sessionId + "," + Integer.toString(eoi) + ","
                + Integer.toString(nextESS));
        requestHeader.put(JerseyHeaderConstants.OPERATION_EXECUTION_JERSEY_HEADER, requestHeaderList);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Sending request to " + uri.toString() + " with header = " + requestHeader.toString());
        }

        // measure before
        final long tin = TIME.getTime();
        // execution of the called method
        Object retval = null;
        try {
            retval = thisJoinPoint.proceed(args);
        } finally {
            // measure after
            final long tout = TIME.getTime();

            // Process response
            if (retval instanceof ClientResponse) {
                final ClientResponse response = (ClientResponse) retval;
                final MultivaluedMap<String, String> responseHeader = response.getHeaders();
                if (responseHeader != null) {
                    final List<String> responseHeaderList = responseHeader
                            .get(JerseyHeaderConstants.OPERATION_EXECUTION_JERSEY_HEADER);
                    if (responseHeaderList != null) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Received response from " + uri.toString() + " with header = "
                                    + responseHeader.toString());
                        }
                        final String[] responseHeaderArray = responseHeaderList.get(0).split(",");

                        // Extract trace id
                        final String retTraceIdStr = responseHeaderArray[0];
                        Long retTraceId = -1L;
                        if (!"null".equals(retTraceIdStr)) {
                            try {
                                retTraceId = Long.parseLong(retTraceIdStr);
                            } catch (final NumberFormatException exc) {
                                LOG.warn("Invalid tradeId");
                            }
                        }
                        if (traceId != retTraceId) {
                            LOG.error("TraceId in response header (" + retTraceId
                                    + ") is different from that in request header (" + traceId + ")");
                        }

                        // Extract session id
                        String retSessionId = responseHeaderArray[1];
                        if ("null".equals(retSessionId)) {
                            retSessionId = OperationExecutionRecord.NO_SESSION_ID;
                        }

                        // Extract eoi
                        int retEOI = -1;
                        final String retEOIStr = responseHeaderArray[2];
                        if (!"null".equals(retEOIStr)) {
                            try {
                                retEOI = Integer.parseInt(retEOIStr);
                                CF_REGISTRY.storeThreadLocalEOI(retEOI);
                            } catch (final NumberFormatException exc) {
                                LOG.warn("Invalid eoi", exc);
                            }
                        }

                    } else {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("No monitoring data found in the response header from " + uri.toString()
                                    + ". Is it instrumented?");
                        }
                    }
                } else {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Response header from " + uri.toString() + " is null. Is it instrumented?");
                    }
                }
            }

            CTRLINST.newMonitoringRecord(
                    new OperationExecutionRecord(signature, sessionId, traceId, tin, tout, hostname, eoi, ess));
            // cleanup
            if (entrypoint) {
                CF_REGISTRY.unsetThreadLocalTraceId();
                CF_REGISTRY.unsetThreadLocalEOI();
                CF_REGISTRY.unsetThreadLocalESS();
                SESSION_REGISTRY.unsetThreadLocalSessionId();
            } else {
                CF_REGISTRY.storeThreadLocalESS(ess); // next operation is ess
            }
        }
        return retval;
    }
}