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}