cn.teamlab.wg.framework.struts2.breadcrumb.BreadCrumbInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for cn.teamlab.wg.framework.struts2.breadcrumb.BreadCrumbInterceptor.java

Source

/*
 *  Copyright 2011 - Giovanni Tosto
 *
 *  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 cn.teamlab.wg.framework.struts2.breadcrumb;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Stack;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.mapper.ActionMapper;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import com.opensymphony.xwork2.mock.MockActionProxy;
import com.opensymphony.xwork2.util.ValueStack;

/**
 * @author Giovanni Tosto
 * @version $Id: BreadCrumbInterceptor.java 128 2011-03-21 17:54:01Z
 *          giovanni.tosto $
 */
@SuppressWarnings("rawtypes")
public class BreadCrumbInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = 1L;

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

    public static final String CRUMB_KEY = BreadCrumbInterceptor.class.getName() + ":CRUMBS";

    static final Object LOCK = new Object();

    private boolean enable = true;

    /**
     * The default breadcrumb trail
     */
    BreadCrumbTrail trail = new BreadCrumbTrail();

    protected ActionMapper actionMapper;

    protected Configuration configuration;

    protected ObjectFactory objectFactory;

    public BreadCrumbTrail getTrail() {
        return trail;
    }

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        /*
         * Register a pre-result listener
         */
        invocation.addPreResultListener(new PreResultListener() {

            public void beforeResult(ActionInvocation invocation, String resultCode) {
                try {
                    LOG.debug("processing invocation" + invocation + "(rc= " + resultCode + ")");
                    beforeInvocation(invocation);
                } catch (Exception e) {
                    String msg = String.format("Exception in BreadCrumbInterceptor : ", e.getMessage());
                    LOG.error(msg, e);
                }
            }
        });

        return invocation.invoke();

    }

    protected BreadCrumbTrail getBreadCrumbTrail(ActionInvocation invocation) {

        ActionContext context = invocation.getInvocationContext();
        // http request
        HttpServletRequest request_ = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
        HttpSession session = request_.getSession(true);

        BreadCrumbTrail bcTrail = (BreadCrumbTrail) session.getAttribute(CRUMB_KEY);

        /*
         * TODO make sure to put one bread crumb trail only
         */
        if (bcTrail == null) {
            synchronized (LOCK) {
                bcTrail = new BreadCrumbTrail();
                bcTrail.setName("$default");
                bcTrail.setMaxCrumbs(trail.maxCrumbs);
                bcTrail.setRewindMode(trail.rewindMode);
                bcTrail.setComparator(trail.comparator);
                // store trail in session
                session.setAttribute(CRUMB_KEY, bcTrail);
                LOG.debug("Stored new BreadCrumbTrail '" + bcTrail.name + "' with key: " + CRUMB_KEY);
            }
        }

        return bcTrail;
    }

    protected void beforeInvocation(ActionInvocation invocation) throws Exception {

        if (!enable) {
            invocation.getInvocationContext().getSession().remove(CRUMB_KEY);
            return;
        }

        BreadCrumb annotation = processAnnotation(invocation);

        if (annotation != null) {

            if (annotation.isEnable() == false) {
                invocation.getInvocationContext().getSession().remove(CRUMB_KEY);
                return;
            }

            BreadCrumbTrail trail = getBreadCrumbTrail(invocation);

            Stack<Crumb> crumbs = trail.getCrumbs();
            crumbs.clear();

            List<Crumb> crumbsList = new ArrayList<Crumb>();

            ActionInvocation inv = invocation;
            Crumb current = makeCrumb(inv, annotation.name(), annotation.key());
            crumbsList.add(current);

            while (!"".equals(annotation.parent())) {

                String namespace = annotation.parentNamespace();

                if (StringUtils.isBlank(namespace)) {
                    namespace = invocation.getProxy().getNamespace();
                }

                ActionConfig parentConfig = configuration.getRuntimeConfiguration().getActionConfig(namespace,
                        annotation.parent());

                String actionName = parentConfig.getName();
                String methodName = parentConfig.getMethodName();

                MockActionInvocation mockInv = new MockActionInvocation();
                MockActionProxy mockProxy = new MockActionProxy();

                Object action = objectFactory.buildAction(actionName, namespace, parentConfig,
                        invocation.getInvocationContext().getContextMap());

                mockProxy.setAction(action);
                mockProxy.setActionName(actionName);
                mockProxy.setMethod(methodName);
                mockProxy.setNamespace(namespace);
                mockProxy.setInvocation(mockInv);

                mockInv.setAction(action);
                mockInv.setProxy(mockProxy);
                mockInv.setInvocationContext(invocation.getInvocationContext());
                inv = mockInv;

                // inv = proxy.getInvocation();
                annotation = processAnnotation(inv);

                current = makeCrumb(inv, annotation.name(), annotation.key());
                crumbsList.add(current);

            }

            for (int i = crumbsList.size() - 1; i >= 0; i--) {
                crumbs.push(crumbsList.get(i));
            }
        }

    }

    //
    // protected void beforeInvocation(ActionInvocation invocation) {
    //
    // BreadCrumb annotation = processAnnotation(invocation);
    //
    // /*
    // * overrides rewind mode of this invocation if needed
    // */
    //
    // if (annotation != null) {
    //
    // Crumb current = makeCrumb(invocation, annotation.name(), annotation
    // .key());
    //
    // // get the bread crumbs trail stored in session(CRUMB_KEY)
    // BreadCrumbTrail trail = getBreadCrumbTrail(invocation);
    //
    // // set default configuration
    // RewindMode mode = trail.rewindMode;
    // int maxCrumbs = trail.maxCrumbs;
    // Comparator<Crumb> comparator = trail.comparator;
    //
    // // TODO override configuration (if needed)
    // if (annotation.rewind() != RewindMode.DEFAULT)
    // mode = annotation.rewind();
    //
    // if (annotation.comparator() != BreadCrumb.NULL.class) {
    // comparator = createComparator(annotation.comparator());
    // }
    //
    // // The comparator to use
    //
    // // then set initial condition the crumbs
    // Stack<Crumb> crumbs = trail.getCrumbs();
    //
    // /*
    // * synchronized region is needed to prevent
    // * ConcurrentModificationException(s) for concurrent request
    // * (operating on the same session) that would modify the bread
    // * crumbs trail.
    // */
    // synchronized (crumbs) {
    // LOG.debug("aquired lock on crumbs trail");
    //
    // Crumb last = (crumbs.size() == 0) ? null : crumbs.lastElement();
    //
    // /*
    // * compare current and last crumbs
    // */
    // /*
    // * if (comparator.compare(current, last) != 0) { int dupIdx =
    // * trail.indexOf(current, comparator); if (mode ==
    // * RewindMode.AUTO && dupIdx != -1) { trail.rewindAt(dupIdx -
    // * 1); } crumbs.push(current); if (crumbs.size() > maxCrumbs)
    // * crumbs.remove(0);
    // *
    // * } else {
    // *
    // * if (crumbs.size() > 0) { crumbs.remove(crumbs.size() - 1);
    // * crumbs.push(current); } }
    // */
    // //change 1 to 0 because top should be invoked in compare
    // if (crumbs.size() > 0) {
    // // group checking
    // if (last.getAction().startsWith(
    // current.getAction().split("/")[0]) == false) {
    // // not the same group
    // //trail.rewindAt(0);
    // crumbs.clear();
    // }
    //
    // // the same group, sort them
    // int dupIdx = trail.indexOf(current, comparator);
    // if (mode == RewindMode.AUTO && dupIdx != -1) {
    // trail.rewindAt(dupIdx - 1);
    // }
    // crumbs.push(current);
    // if (crumbs.size() > maxCrumbs) {
    // crumbs.remove(0);
    // }
    // /*
    // if (crumbs.size() > 2) {
    // // sort by name
    // trail.sort(comparator);
    // }
    // */
    // } else {
    // // add directly
    // crumbs.push(current);
    // }
    //
    // LOG.debug("releasing lock on crumbs trail");
    // } // synchronized
    // }
    //
    // }

    @SuppressWarnings("unchecked")
    protected Comparator<Crumb> createComparator(Class clazz) {
        try {
            Comparator instance = (Comparator) clazz.newInstance();
            return instance;
        } catch (InstantiationException e) {
            LOG.error("Cannot create comparator of class " + clazz, e);
        } catch (IllegalAccessException e) {
            LOG.error("Cannot create comparator of class " + clazz, e);
        }
        return null;

    }

    protected static BreadCrumb processAnnotation(ActionInvocation invocation) {

        Class aclass = invocation.getAction().getClass();

        String methodName = invocation.getProxy().getMethod();
        if (methodName == null)
            methodName = "execute";

        Method method = Utils.findMethod(aclass, methodName);

        return findAnnotation(aclass, method);
    }

    @SuppressWarnings("unchecked")
    protected static BreadCrumb findAnnotation(Class aclass, Method method) {
        BreadCrumb crumb = null;

        /*
         * Check if it is an annotated method
         */
        if (method != null) {
            crumb = method.getAnnotation(BreadCrumb.class);
        }

        /*
         * Check if we have an annotated class
         */
        if (crumb == null) {
            crumb = (BreadCrumb) aclass.getAnnotation(BreadCrumb.class);
        }

        return crumb;
    }

    protected static Crumb makeCrumb(ActionInvocation invocation, String name, String key) {

        ActionProxy proxy = invocation.getProxy();

        Crumb c = new Crumb();
        c.timestamp = new Date();
        c.namespace = proxy.getNamespace();
        c.action = proxy.getActionName();
        c.method = proxy.getMethod();

        // evaluate name
        if (name.startsWith("%{") && name.endsWith("}")) {
            ValueStack vstack = invocation.getStack();
            Object value = vstack.findValue(name.substring(2, name.length() - 1));
            name = "" + value; // avoid NPE
        }
        c.name = name;

        // store request parameters
        c.params = invocation.getInvocationContext().getParameters();

        if (key == null) {
            c.text = name;
        } else {
            Object action = proxy.getAction();
            if (action instanceof TextProvider) {
                TextProvider i18n = (TextProvider) action;
                c.text = i18n.getText(key, name);
            }
        }

        return c;
    }

    @Inject
    public void setActionMapper(ActionMapper mapper) {
        this.actionMapper = mapper;
    }

    @Inject
    public void setConfiguration(Configuration config) {
        this.configuration = config;
    }

    @Inject
    public void setObjectFactory(ObjectFactory factory) {
        this.objectFactory = factory;
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }
}