Java tutorial
/* * 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; } }