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<Work>(); 032 033 public static final Log log = LogFactory.getLog(WorkSchedulePath.class); 034 035 public static boolean captureStack = Boolean.parseBoolean(Framework.getProperty("work.schedule.captureStack", 036 "false")) || log.isTraceEnabled(); 037 038 public static WorkSchedulePath EMPTY = new WorkSchedulePath(); 039 040 protected final String parentPath; 041 042 protected final String name; 043 044 protected final transient Trace scheduleStackTrace; 045 046 public class Trace extends Throwable { 047 048 private static final long serialVersionUID = 1L; 049 050 protected Trace(Trace cause) { 051 super(getPath(), cause); 052 } 053 054 public WorkSchedulePath path() { 055 return WorkSchedulePath.this; 056 } 057 } 058 059 public static boolean toggleCaptureStack() { 060 return captureStack = !captureStack; 061 } 062 063 public static boolean isCaptureStackEnabled() { 064 return captureStack; 065 } 066 067 public static void newInstance(Work work) { 068 Work entered = enteredLocal.get(); 069 WorkSchedulePath path = new WorkSchedulePath(entered == null ? EMPTY : entered.getSchedulePath(), work); 070 work.setSchedulePath(path); 071 } 072 073 public static void handleEnter(Work work) { 074 if (enteredLocal.get() != null) { 075 throw new AssertionError("thread local leak, chain should not be re-rentrant"); 076 } 077 enteredLocal.set(work); 078 } 079 080 public static void handleReturn() { 081 enteredLocal.remove(); 082 } 083 084 protected static String path(WorkSchedulePath parent) { 085 if (EMPTY.equals(parent)) { 086 return ""; 087 } 088 return parent.parentPath + "/" + parent.name; 089 } 090 091 protected static String name(Work work) { 092 return work.getCategory() + ":" + work.getId(); 093 } 094 095 protected WorkSchedulePath() { 096 parentPath = ""; 097 name = ""; 098 scheduleStackTrace = null; 099 } 100 101 public boolean isRoot() { 102 return parentPath.isEmpty(); 103 } 104 105 protected WorkSchedulePath(WorkSchedulePath parent, Work work) { 106 parentPath = parent.getPath(); 107 name = name(work); 108 scheduleStackTrace = captureStack ? new Trace(parent.scheduleStackTrace) : null; 109 } 110 111 public String getPath() { 112 return path(this); 113 } 114 115 public String getParentPath() { 116 return parentPath; 117 } 118 119 public Trace getStack() { 120 return scheduleStackTrace; 121 } 122 123 @Override 124 public String toString() { 125 return "[parentPath=" + parentPath + ", name=" + name + "]"; 126 } 127 128}