001/* 002 * (C) Copyright 2006-2013 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.nuxeo.ecm.core.work.api; 017 018import java.io.Serializable; 019 020import javax.management.MXBean; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024import org.nuxeo.runtime.api.Framework; 025 026@MXBean 027public class WorkSchedulePath implements Serializable { 028 029 private static final long serialVersionUID = 1L; 030 031 protected static final transient ThreadLocal<Work> enteredLocal = new ThreadLocal<>(); 032 033 public static final Log log = LogFactory.getLog(WorkSchedulePath.class); 034 035 protected static Boolean captureStack; 036 037 public static final WorkSchedulePath EMPTY = new WorkSchedulePath(); 038 039 protected final String parentPath; 040 041 protected final String name; 042 043 protected final transient Trace scheduleStackTrace; 044 045 public class Trace extends Throwable { 046 047 private static final long serialVersionUID = 1L; 048 049 protected Trace(Trace cause) { 050 super(getPath(), cause); 051 } 052 053 public WorkSchedulePath path() { 054 return WorkSchedulePath.this; 055 } 056 } 057 058 public static synchronized boolean toggleCaptureStack() { 059 captureStack = Boolean.valueOf(!isCaptureStackEnabled()); 060 return captureStack.booleanValue(); 061 } 062 063 public static synchronized boolean isCaptureStackEnabled() { 064 if (captureStack == null) { 065 captureStack = Boolean.valueOf(log.isTraceEnabled() 066 || Boolean.parseBoolean(Framework.getProperty("work.schedule.captureStack", "false"))); 067 // we don't do the initialization as a static field init because 068 // the Framework may not be initialized when the class is loaded 069 } 070 return captureStack.booleanValue(); 071 } 072 073 public static void newInstance(Work work) { 074 Work entered = enteredLocal.get(); 075 WorkSchedulePath path = new WorkSchedulePath(entered == null ? EMPTY : entered.getSchedulePath(), work); 076 work.setSchedulePath(path); 077 } 078 079 public static void handleEnter(Work work) { 080 if (enteredLocal.get() != null) { 081 throw new AssertionError("thread local leak, chain should not be re-rentrant"); 082 } 083 enteredLocal.set(work); 084 } 085 086 public static void handleReturn() { 087 enteredLocal.remove(); 088 } 089 090 protected static String path(WorkSchedulePath parent) { 091 if (EMPTY.equals(parent)) { 092 return ""; 093 } 094 return parent.parentPath + "/" + parent.name; 095 } 096 097 protected static String name(Work work) { 098 return work.getCategory() + ":" + work.getId(); 099 } 100 101 protected WorkSchedulePath() { 102 parentPath = ""; 103 name = ""; 104 scheduleStackTrace = null; 105 } 106 107 public boolean isRoot() { 108 return parentPath.isEmpty(); 109 } 110 111 protected WorkSchedulePath(WorkSchedulePath parent, Work work) { 112 parentPath = parent.getPath(); 113 name = name(work); 114 scheduleStackTrace = isCaptureStackEnabled() ? new Trace(parent.scheduleStackTrace) : null; 115 } 116 117 public String getPath() { 118 return path(this); 119 } 120 121 public String getParentPath() { 122 return parentPath; 123 } 124 125 public Trace getStack() { 126 return scheduleStackTrace; 127 } 128 129 @Override 130 public String toString() { 131 return "[parentPath=" + parentPath + ", name=" + name + "]"; 132 } 133 134}