Package org.apache.sirona.aop

Source Code of org.apache.sirona.aop.AbstractPerformanceInterceptor$ActivationContext

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.sirona.aop;

import org.apache.sirona.Role;
import org.apache.sirona.SironaException;
import org.apache.sirona.configuration.Configuration;
import org.apache.sirona.counters.Counter;
import org.apache.sirona.repositories.Repository;
import org.apache.sirona.stopwatches.StopWatch;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A method interceptor that compute method invocation performances.
* <p/>
* Concrete implementation will adapt the method interception API to
* this class requirement.
*
*/
public abstract class AbstractPerformanceInterceptor<T> implements Serializable {
    // static for performances reasons, all these values are read through getXXX so it is overridable
    private static final boolean ADAPTIVE = Configuration.is(Configuration.CONFIG_PROPERTY_PREFIX + "performance.adaptive", false);
    private static final long FORCED_ITERATION = Configuration.getInteger(Configuration.CONFIG_PROPERTY_PREFIX + "performance.forced-iteration", 0);
    private static final long THRESHOLD = duration(Configuration.getProperty(Configuration.CONFIG_PROPERTY_PREFIX + "performance.threshold", null));
    private static final ActivationContext ALWAYS_ACTIVE_CONTEXT = new ActivationContext(true, 0, 0);

    protected static final ConcurrentMap<Object, ActivationContext> CONTEXTS = new ConcurrentHashMap<Object, ActivationContext>();

    private static long duration(final String duration) {
        if (duration == null) {
            return 0;
        }
        final String[] parts = duration.split(" ");
        if (parts.length == 1) {
            return Long.parseLong(duration.trim());
        } else if (parts.length == 2) {
            return TimeUnit.valueOf(parts[2].trim().toUpperCase(Locale.ENGLISH)).toNanos(Long.parseLong(parts[0].trim()));
        }
        return 0;
    }

    protected MonitorNameExtractor monitorNameExtractor;

    public AbstractPerformanceInterceptor() {
        setMonitorNameExtractor(DefaultMonitorNameExtractor.INSTANCE);
    }

    /**
     * API neutral method invocation
     */
    protected Object doInvoke(final T invocation) throws Throwable {
        final String name = getCounterName(invocation);
        if (name == null) {
            return proceed(invocation);
        }

        final Context ctx = before(invocation, name);
        Throwable error = null;
        try {
            return proceed(invocation);
        } catch (final Throwable t) {
            error = t;
            throw t;
        } finally {
            if (error == null) {
                ctx.stop();
            } else {
                ctx.stopWithException(error);
            }
        }
    }

    protected Context before(final T invocation, final String name) {
        final ActivationContext context = doFindContext(invocation);

        try
        {
            final StopWatch stopwatch;
            if (context.shouldExecute()) {
                Repository repository =  Repository.INSTANCE;
                if (repository==null){
                    System.out.println("repository is null");
                }
                final Counter monitor = repository.getCounter(getKey(invocation, name));
                if ( monitor == null){
                    System.out.println("monitor is null");
                }
                stopwatch = Repository.INSTANCE.start(monitor);
            } else {
                stopwatch = null;
            }

            return newContext(invocation, context, stopwatch);
        }
        catch ( Exception e )
        {
            //e.printStackTrace();
            // ignore and return a fake context can happen on start when intercepting some classes
            // and all agent classes not really loaded
            return newContext( invocation, context, new StopWatch()
            {
                @Override
                public long getElapsedTime()
                {
                    return 0;
                }

                @Override
                public StopWatch stop()
                {
                    return this;
                }
            }  );
        }
    }

    protected Context newContext(final T invocation, final ActivationContext context, final StopWatch stopwatch) {
        return new Context(context, stopwatch);
    }

    protected Counter.Key getKey(final T invocation, final String name) {
        return new Counter.Key(getRole(), name);
    }

    protected boolean isAdaptive() {
        return ADAPTIVE;
    }

    protected Object extractContextKey(final T invocation) {
        return null;
    }

    protected ActivationContext getOrCreateContext(final Object m) {
        final ActivationContext c = CONTEXTS.get(m);
        if (c == null) {
            final String counterName;
            if (SerializableMethod.class.isInstance(m)) {
                counterName = getCounterName(null, SerializableMethod.class.cast(m).method());
            } else {
                counterName = m.toString();
            }
            return putAndGetActivationContext(m, new ActivationContext(true, counterName));
        }
        return c;
    }

    protected ActivationContext putAndGetActivationContext(Object m, ActivationContext newCtx) {
        final ActivationContext old = CONTEXTS.putIfAbsent(m, newCtx);
        if (old != null) {
            newCtx = old;
        }
        return newCtx;
    }

    protected ActivationContext doFindContext(final T invocation) {
        if (!isAdaptive()) {
            return ALWAYS_ACTIVE_CONTEXT;
        }

        final Object m = extractContextKey(invocation);
        if (m != null) {
            return getOrCreateContext(m);
        }

        return ALWAYS_ACTIVE_CONTEXT;
    }

    protected Role getRole() {
        return Role.PERFORMANCES;
    }

    protected abstract Object proceed(T invocation) throws Throwable;

    protected abstract String getCounterName(T invocation);

    /**
     * Compute the counter name associated to this method invocation
     *
     * @param method method being invoked
     * @return counter name. If <code>null</code>, nothing will be monitored
     */
    protected String getCounterName(final Object instance, final Method method) {
        return monitorNameExtractor.getMonitorName(instance, method);
    }

    public void setMonitorNameExtractor(final MonitorNameExtractor monitorNameExtractor) {
        this.monitorNameExtractor = monitorNameExtractor;
    }

    /**
     * The handler for cases where interception is not possible and you need to pass the "before"object to be able to monitor.
     */
    public static class Context {
        private static final int MAX_LENGTH = Configuration.getInteger(Configuration.CONFIG_PROPERTY_PREFIX + "performance.exception.max-length", 100);

        protected final ActivationContext activationContext;
        protected final StopWatch stopWatch;

        public Context(final ActivationContext activationContext, final StopWatch stopWatch) {
            this.activationContext = activationContext;
            this.stopWatch = stopWatch;
        }

        public void stop() {
            if (stopWatch != null) {
                final long elapsedTime = stopWatch.stop().getElapsedTime();
                activationContext.elapsedTime(elapsedTime);
            }
        }

        public void stopWithException(final Throwable error) {
            if (stopWatch != null) {
                stopWatch.stop();

                final long elapsedTime = stopWatch.getElapsedTime();

                if (error != null) {
                    /*
                    final ByteArrayOutputStream writer = new ByteArrayOutputStream();
                    error.printStackTrace(new PrintStream(writer));
                    final String toString = writer.toString();
                    */
                    Repository.INSTANCE.getCounter(
                            new Counter.Key(Role.FAILURES, error.getClass().getName() + ":" + (error.getMessage() != null ? error.getMessage() : ""))).add(elapsedTime);
                }

                activationContext.elapsedTime(elapsedTime);
            }
        }
    }

    protected static class SerializableMethod implements Serializable {
        protected final String clazz;
        protected final String method;
        protected transient Method realMethod;
        protected final int hashCode;

        public SerializableMethod(final String clazz, final String method, final Method reflectMethod) {
            this.clazz = clazz;
            this.method = method;
            this.realMethod = reflectMethod;
            this.hashCode = reflectMethod.hashCode();
        }

        public SerializableMethod(final Method m) {
            this(m.getDeclaringClass().getName(), m.getName(), m);
        }

        public Method method() {
            if (realMethod == null) { // try to find it
                try {
                    Class<?> declaring = Thread.currentThread().getContextClassLoader().loadClass(clazz);
                    while (declaring != null) {
                        for (final Method m : declaring.getDeclaredMethods()) {
                            if (m.getName().equals(method)) {
                                realMethod = m;
                                return realMethod;
                            }
                        }
                        declaring = declaring.getSuperclass();
                    }
                } catch (final ClassNotFoundException e) {
                    throw new SironaException(e.getMessage(),e);
                }
            }
            return realMethod;
        }

        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            final SerializableMethod that = SerializableMethod.class.cast(o);
            if (method != null && that.method != null) {
                return method.equals(that.method);
            }
            return hashCode == that.hashCode;
        }

        @Override
        public int hashCode() {
            return hashCode;
        }
    }

    /**
     * This class contains the activation/deactivation logic.
     */
    protected static class ActivationContext implements Serializable {
        protected final long forceIteration;
        protected final long threshold;
        protected final boolean thresholdActive;

        protected volatile boolean active = true;
        protected volatile AtomicInteger iteration = new AtomicInteger(0);

        public ActivationContext(final boolean active, final long th, final long it) {
            this.active = active;

            if (it >= 0) {
                forceIteration = it;
            } else {
                forceIteration = FORCED_ITERATION;
            }

            if (th >= 0) {
                threshold = th;
            } else {
                threshold = THRESHOLD;
            }

            this.thresholdActive = this.threshold > 0;
        }

        public ActivationContext(final boolean active, final String name) {
            this(active,
                duration(Configuration.getProperty(Configuration.CONFIG_PROPERTY_PREFIX + "performance." + name + ".threshold", null)),
                Configuration.getInteger(Configuration.CONFIG_PROPERTY_PREFIX + "performance." + name + ".forced-iteration", -1));
        }

        public boolean isForcedIteration() {
            return iteration.incrementAndGet() > forceIteration;
        }

        protected long getThreshold() {
            return threshold;
        }

        protected boolean isThresholdActive() {
            return thresholdActive;
        }

        public boolean isActive() {
            return active;
        }

        public void reset() {
            active = false;
            iteration.set(0);
        }

        public boolean shouldExecute() {
            return isActive() || isForcedIteration();
        }

        public void elapsedTime(final long elapsedTime) {
            if (isThresholdActive() && elapsedTime < getThreshold()) {
                reset();
            }
        }
    }
}
TOP

Related Classes of org.apache.sirona.aop.AbstractPerformanceInterceptor$ActivationContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.