com.p5solutions.trackstate.aop.TrackStateProxyAspect.java Source code

Java tutorial

Introduction

Here is the source code for com.p5solutions.trackstate.aop.TrackStateProxyAspect.java

Source

/* Pivotal 5 Solutions Inc. - Object Change Tracking and Mapping Utilities - Java Library.
 * 
 * Copyright (C) 2011  KASRA RASAEE
 * 
 * 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 3 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, see <http://www.gnu.org/licenses/>. 
 */
package com.p5solutions.trackstate.aop;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

import com.p5solutions.core.aop.ProxyFactory;
import com.p5solutions.core.utils.Comparison;
import com.p5solutions.core.utils.ReflectionUtility;

/**
 * TrackStateProxyAspect: TrackStateProxy AspectJ handling for target method call. All resulting objects with the
 * {@link WrapTrackStateProxy} annotation will result in proxy based object derived from the
 * {@link TrackStateProxyFactoryImpl}
 * 
 * @author Kasra Rasaee
 * @since 2009-02-11
 * @see TrackStateProxyStategy for strategy on the method result
 * @see TrackStateProxy interface for definition
 * @see TrackStateProxyFactoryImpl for factory implementation on proxying via {@link javassist.util.proxy.ProxyFactory}
 * @see TrackState annotation on proxable objects
 */
@Aspect
public class TrackStateProxyAspect {

    private ProxyFactory trackStateProxyFactory;

    protected final TrackState getTrackState(Object target) {
        // if the target is not null, then search
        // for the appropriate annotation.
        if (target != null) {
            Class<?> clazz = target.getClass();
            return ReflectionUtility.findAnnotation( //
                    clazz, TrackState.class);
        }

        // return null if no TrackState annotation was found.
        return null;
    }

    /**
     * Checks for track state proxy on a given target object.
     * 
     * @param target
     *          the target, must either be instance of {@link TrackStateProxy}
     * 
     * @return true, if instance is of type {@link TrackStateProxy}
     */
    protected final boolean hasTrackStateProxy(Object target) {
        if (target instanceof TrackStateProxy) {
            return true;
        }
        return false;
    }

    /**
     * Auto proxy returning object.
     * 
     * @param target
     *          the target
     * @return Instance of the returning object form the proxied.
     * @throws Throwable
     *           the throwable
     */
    protected final Object autoTrack(Object target) throws Throwable {
        TrackState trackState = null;

        if (target != null) {
            Class<?> clazz = target.getClass();
            trackState = ReflectionUtility.findAnnotation( //
                    clazz, TrackState.class);
        }

        // if the TrackState annotation exists on the class type
        // and the target object has not already been enhanced via
        // the TrackStateProxyFactory implementation, then proxy it.
        if (target instanceof List<?>) {
            target = listProxy(target);
        } else if (trackState != null && !(target instanceof TrackStateProxy)) {
            // if (target instanceof List<?>) {
            // target = listProxy(target);
            // } else {
            target = proxy(target);
            // }
        }

        return target;
    }

    /**
     * Proxy a list of objects and return a list of proxies.
     * 
     * @param target
     * @return
     */
    protected final Object listProxy(Object target) {
        List<?> items = (List<?>) target;
        // if there are objects iterate them
        if (!Comparison.isEmptyOrNull(items)) {
            List<Object> newItems = new ArrayList<Object>();
            // iterate and proxy the objects
            for (Object item : items) {
                // proxy the object
                Object proxy = proxy(item);
                // TODO: fix me... need to check for proxy state first.
                // add the proxy to the list
                newItems.add(proxy);
            }
            // return the proxied list
            return newItems;
        }

        return target;
    }

    /**
     * Proxy an object via the {@link #trackStateProxyFactory}.
     * 
     * @param target
     *          the target
     * @return an instance of {@link TrackStateProxy} enhanced.
     */
    protected final Object proxy(Object target) {
        TrackStateProxy proxy = null;

        // if the target object is already proxied
        if (target instanceof TrackStateProxy) {
            proxy = (TrackStateProxy) target;
        } else if (target != null) {
            // if the target is not ap roxy and target is not null

            // If the target object is a collection
            // then proxy each object within it.
            if (target instanceof Collection<?>) {
                return listProxy(target);
            }

            // Search for track state annotation
            TrackState trackState = getTrackState(target);

            // if TrackState annotation exists on the
            // type, then attempt to proxy the target
            if (trackState != null) {
                // proxy the target if it already hasn't been proxied
                Class<?> clazz = target.getClass();
                proxy = (TrackStateProxy) trackStateProxyFactory.createProxy(clazz, target);
            }
        }

        // set the proxy as being initialized
        if (proxy != null) {
            proxy.setInitialized(true);
            target = proxy;
        }

        // return the proxied object, or existing proxy
        return target;
    }

    /**
     * Reset proxy dirty flags.
     * 
     * @param target
     *          the target
     * @return true, if successful
     */
    protected final boolean resetProxy(Object target) {

        if (target instanceof TrackStateProxy) {
            TrackStateProxy proxy = (TrackStateProxy) target;
            proxy.reset();
            proxy.setInitialized(true);
            return true;
        }

        return false;
    }

    /**
     * Auto reset proxy state after saving.
     * 
     * @param target
     *          the target
     * @param args
     *          the args
     * @return the object
     * @throws Throwable
     *           the throwable
     */
    protected final Object autoReset(Object target, Object[] args) throws Throwable {
        try {
            reset(target);
            // return the return value from the invoked method
            return target;
        } catch (Exception e) {
            // catch any exceptions and throw it back down the callstack
            throw e;
        }
    }

    /**
     * Reset join point method arguments and returning target.
     * 
     * @param target
     *          the target
     * @return true, if successful
     */
    private boolean reset(Object target) {

        // iterate each method argument and reset if its a proxy
        if (hasTrackStateProxy(target)) {
            TrackStateProxy proxy = (TrackStateProxy) target;
            proxy.reset();
            proxy.setInitialized(true);
            return true;
        }

        return false;
    }

    /**
     * Auto proxy or reset.
     * 
     * @param target
     *          the target
     * @param args
     *          the args
     * 
     * @return the object
     * 
     * @throws Throwable
     *           the throwable
     */
    protected Object autoProxyOrReset(Object target, Object[] args) throws Throwable {
        try {
            if (!reset(target)) {
                target = proxy(target);
            }

            // reset all the arguments
            if (!Comparison.isEmptyOrNull(args)) {
                for (Object arg : args) {
                    if (arg != target) {
                        reset(arg);
                    }
                }
            }

            // return the return value from the invoked method
            return target;
        } catch (Exception e) {
            // catch any exceptions and throw it back down the callstack
            throw e;
        }
    }

    /**
     * Wrap around a method with the annotation of {@link WrapTrackStateProxy}. If {@link TrackStateProxyStategy#PROXY},
     * the returning value of this method will be proxied via the {@link #trackStateProxyFactory}.
     * 
     * @param pjp
     *          The proceeding join point, representing the method in question.
     * @param wrap
     *          The {@link WrapTrackStateProxy} instance on annotated on the method.
     * @return Instance of the returning object form the <code>pjp.proceed()</code> proxied via the
     *         {@link #autoTrack(ProceedingJoinPoint)} if {@link TrackStateProxyStategy#PROXY}
     * @throws Throwable
     *           the throwable
     */
    @Around("@annotation(wrap)")
    public Object trackState(ProceedingJoinPoint pjp, WrapTrackStateProxy wrap) throws Throwable {

        Object target = pjp.proceed();
        Object[] args = pjp.getArgs();

        target = wrap(target, args, wrap);

        return target;
    }

    protected Object wrap(Object target, Object[] args, WrapTrackStateProxy wrap) throws Throwable {

        TrackStateProxyStategy strategy = wrap.value();

        switch (strategy) {
        case PROXY:
            // call the method and proxy the results
            return autoTrack(target);

        case RESET_WHEN_NO_EXCEPTION:
            // call the method and reset the proxy laundry list
            return autoReset(target, args);

        case PROXY_OR_RESET:
            // call the method and proxy the results or reset the proxy if
            // present
            return autoProxyOrReset(target, args);
        case CONTAINER:
            doContainer(target);
        }

        return target;
    }

    /**
     * Do container. Runs through an object looking for {@link WrapTrackStateProxy} annotions on methods.
     * 
     * @param target
     *          the target
     * 
     * @throws Throwable
     *           the throwable
     */
    private void doContainer(Object target) throws Throwable {
        if (target == null) {
            return;
        }

        // In case the target is a collection of objects that need to be tracked
        if (target instanceof Collection<?>) {
            for (Object collectionObject : (Collection<?>) target) {
                doContainer(collectionObject);
            }
        }

        /* Find all methods with WrapTrackStateProxy and call #wrap on the object */

        Class<?> clazz = target.getClass();
        List<Method> methods = ReflectionUtility.findMethodsWithAnnotation(clazz, WrapTrackStateProxy.class);

        // iterate each method and recursively walk the containers
        for (Method method : methods) {
            Object innerTarget = ReflectionUtility.getValue(method, target);

            // skip if target object within container is null
            if (innerTarget == null) {
                continue;
            }

            // find the wrap annotation
            WrapTrackStateProxy innerWrap = ReflectionUtility.findAnnotation(method, WrapTrackStateProxy.class);

            // invoke wrap method and set the value back into the cointainer object
            innerTarget = wrap(innerTarget, null, innerWrap);

            // find the setter method based on the getter method
            Method set = ReflectionUtility.findSetterMethod(clazz, method);

            // set the proxied object back into the container object
            ReflectionUtility.setValue(set, target, innerTarget);
        }
    }

    /**
     * Sets the track state proxy factory.
     * 
     * @param trackStateProxyFactory
     *          the new track state proxy factory
     */
    public void setTrackStateProxyFactory(ProxyFactory trackStateProxyFactory) {
        this.trackStateProxyFactory = trackStateProxyFactory;
    }
}