001/*******************************************************************************
002 * Copyright (c) 2006-2013 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *******************************************************************************/
009package org.nuxeo.ecm.core.work.api;
010
011import java.io.Serializable;
012
013import javax.management.MXBean;
014
015import org.apache.commons.logging.Log;
016import org.apache.commons.logging.LogFactory;
017import org.nuxeo.runtime.api.Framework;
018
019@MXBean
020public class WorkSchedulePath implements Serializable {
021
022    private static final long serialVersionUID = 1L;
023
024    protected static final transient ThreadLocal<Work> enteredLocal = new ThreadLocal<Work>();
025
026    public static final Log log = LogFactory.getLog(WorkSchedulePath.class);
027
028    public static boolean captureStack = Boolean.parseBoolean(Framework.getProperty("work.schedule.captureStack",
029            "false")) || log.isTraceEnabled();
030
031    public static WorkSchedulePath EMPTY = new WorkSchedulePath();
032
033    protected final String parentPath;
034
035    protected final String name;
036
037    protected final transient Trace scheduleStackTrace;
038
039    public class Trace extends Throwable {
040
041        private static final long serialVersionUID = 1L;
042
043        protected Trace(Trace cause) {
044            super(getPath(), cause);
045        }
046
047        public WorkSchedulePath path() {
048            return WorkSchedulePath.this;
049        }
050    }
051
052    public static void toggleCaptureStack() {
053        captureStack = !captureStack;
054    }
055
056    public static boolean isCaptureStackEnabled() {
057        return captureStack;
058    }
059
060    public static void newInstance(Work work) {
061        Work entered = enteredLocal.get();
062        WorkSchedulePath path = new WorkSchedulePath(entered == null ? EMPTY : entered.getSchedulePath(), work);
063        work.setSchedulePath(path);
064    }
065
066    public static void handleEnter(Work work) {
067        if (enteredLocal.get() != null) {
068            throw new AssertionError("thread local leak, chain should not be re-rentrant");
069        }
070        enteredLocal.set(work);
071    }
072
073    public static void handleReturn() {
074        enteredLocal.remove();
075    }
076
077    protected static String path(WorkSchedulePath parent) {
078        if (EMPTY.equals(parent)) {
079            return "";
080        }
081        return parent.parentPath + "/" + parent.name;
082    }
083
084    protected static String name(Work work) {
085        return work.getCategory() + ":" + work.getId();
086    }
087
088    protected WorkSchedulePath() {
089        parentPath = "";
090        name = "";
091        scheduleStackTrace = null;
092    }
093
094    public boolean isRoot() {
095        return parentPath.isEmpty();
096    }
097
098    protected WorkSchedulePath(WorkSchedulePath parent, Work work) {
099        parentPath = parent.getPath();
100        name = name(work);
101        scheduleStackTrace = captureStack ? new Trace(parent.scheduleStackTrace) : null;
102    }
103
104    public String getPath() {
105        return path(this);
106    }
107
108    public String getParentPath() {
109        return parentPath;
110    }
111
112    public Trace getStack() {
113        return scheduleStackTrace;
114    }
115
116    @Override
117    public String toString() {
118        return "[parentPath=" + parentPath + ", name=" + name + "]";
119    }
120
121}