AvalonInterceptorContextImpl.java

package org.apache.fulcrum.yaafi.framework.interceptor;

/*
 * 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.
 */

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.apache.fulcrum.yaafi.framework.tls.ThreadLocalStorage;
import org.apache.fulcrum.yaafi.framework.tls.ThreadLocalStorageImpl;
import org.apache.fulcrum.yaafi.framework.util.ToStringBuilder;
import org.apache.fulcrum.yaafi.framework.util.Validate;

/**
 * Contains context information for the interceptors being invoked. The
 * class contains a request context which allows to store data from within an
 * interceptor. It also provides access to a ThreadLocalStorage to associate
 * data with the current thread.
 *
 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
 */

public class AvalonInterceptorContextImpl implements AvalonInterceptorContext
{
    /** key for looking up the transaction id */
    private static final String TRANSACTIONID_KEY =
        "$org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext#transactionId";

    /** key for looking up the service invocation depth */
    private static final String INVOCATIONDEPTH_KEY =
        "$org.apache.fulcrum.yaafi.framework.interceptor.AvalonInterceptorContext#invocationDepth";

    /** the name of the service being intercepted */
    private String serviceName;

    /** the shorthand of the service being intercepted */
    private String serviceShorthand;

    /** the real service implementation */
    private Object serviceDelegate;

    /** the method being invoked */
    private Method method;

    /** the arguments for the method invocation */
    private Object[] args;

    /** context information associated with the current invocation */
    private Map<String, Object> requestContext;

    /** context information associated with the current thread */
    private static ThreadLocalStorageImpl tls = new ThreadLocalStorageImpl();

    /** works as invocation counter */
    private static volatile long invocationCounter = 0L;

    /** the associated transaction id */
    private Long invocationId;

    /**
     * Constructor.
     *
     * @param serviceName the name of the service being intercepted
     * @param serviceShorthand the shorthand of the service being intercepted
     * @param serviceDelegate the real service implementation
     * @param method the method being invoked
     * @param args the list of arguments for the method invocation
     */
    public AvalonInterceptorContextImpl(
        String serviceName, String serviceShorthand, Object serviceDelegate, Method method, Object[] args )
    {
        Validate.notEmpty(serviceName,"serviceName");
        Validate.notEmpty(serviceShorthand,"serviceShorthand");
        Validate.notNull(serviceDelegate,"serviceDelegate");
        Validate.notNull(method,"method");

        this.invocationId = Long.valueOf(++AvalonInterceptorContextImpl.invocationCounter);
        this.serviceName = serviceName;
        this.serviceShorthand = serviceShorthand;
        this.serviceDelegate = serviceDelegate;
        this.method = method;
        this.args = args;
        this.requestContext = new HashMap<String, Object>();
    }

    /**
     * @return Returns the context for the given request.
     */
    public final Map<String, Object> getRequestContext()
    {
        return requestContext;
    }

    /**
     * @return Returns the serviceDelegate.
     */
    public final Object getServiceDelegate()
    {
        return serviceDelegate;
    }

    /**
     * @return Returns the serviceName.
     */
    public final String getServiceName()
    {
        return serviceName;
    }

    /**
     * @return Returns the serviceShorthand.
     */
    public String getServiceShorthand()
    {
        return serviceShorthand;
    }

    /**
     * @return Returns the args.
     */
    public final Object [] getArgs()
    {
        return args;
    }

    /**
     * @return Returns the method.
     */
    public final Method getMethod()
    {
        return method;
    }

    /**
     * @return Returns the ThreadLocalStorage
     */
    public final ThreadLocalStorage getThreadContext()
    {
        return AvalonInterceptorContextImpl.tls;
    }

    /**
     * @return is a transaction id defined for the current thread
     */
    public boolean hasTransactionId()
    {
        return ( this.getTransactionId() != null ? true : false );
    }

    /**
     * @return get the transaction id defined for the current thread
     */
    public Object getTransactionId()
    {
        return this.getThreadContext().get(TRANSACTIONID_KEY);
    }

    /**
     * Set the transaction id for the current thread.
     * @param transactionId the transaction id
     */
    public void setTransactionId( Object transactionId )
    {
        this.getThreadContext().put(TRANSACTIONID_KEY,transactionId);
    }

    /**
     * Clears the transaction id for the current thread.
     */
    public void clearTransactionId()
    {
        this.setTransactionId(null);
    }

    /**
     * Increment the current service invocation depth
     */
    public void incrementInvocationDepth()
    {
        Integer invocationDepth = (Integer) this.getThreadContext().get(INVOCATIONDEPTH_KEY);

        if( invocationDepth != null )
        {
            int currInvocationDepth = invocationDepth.intValue();
            this.getThreadContext().put(INVOCATIONDEPTH_KEY, Integer.valueOf(++currInvocationDepth));
        }
        else
        {
            this.getThreadContext().put(INVOCATIONDEPTH_KEY, Integer.valueOf(0));
        }
    }

    /**
     * Decrement the current service invocation depth
     */
    public void decrementInvocationDepth()
    {
        Integer invocationDepth = (Integer) this.getThreadContext().get(INVOCATIONDEPTH_KEY);

        if( invocationDepth != null )
        {
            int currInvocationDepth = invocationDepth.intValue();
            this.getThreadContext().put(INVOCATIONDEPTH_KEY, Integer.valueOf(--currInvocationDepth));
        }
    }

    /**
     * Get the current service invocation depth
     * @return the current service invocation depth
     */
    public int getInvocationDepth()
    {
        Integer invocationDepth = (Integer) this.getThreadContext().get(INVOCATIONDEPTH_KEY);

        if( invocationDepth != null )
        {
            return invocationDepth.intValue();
        }
        else
        {
            return 0;
        }
    }

    /**
     * @return Returns the invocationId.
     */
    public final Long getInvocationId()
    {
        return invocationId;
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString()
    {
        ToStringBuilder toStringBuilder = new ToStringBuilder(this);

        toStringBuilder.append("serviceShorthand",this.serviceShorthand);
        toStringBuilder.append("serviceName",this.serviceName);
        toStringBuilder.append("serviceDelegate",this.serviceDelegate);
        toStringBuilder.append("method",this.method.getName());
        toStringBuilder.append("args",this.args.length);
        toStringBuilder.append("transactionId",this.getTransactionId());
        toStringBuilder.append("invocationId",this.invocationId);
        toStringBuilder.append("invocationDepth",this.getInvocationDepth());
        toStringBuilder.append("requestContext",this.requestContext);

        return toStringBuilder.toString();
    }
}