001/* 002 * (C) Copyright 2006-2012 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 * 016 * Contributors: 017 * slacoin 018 */ 019package org.nuxeo.ecm.core.test; 020 021import java.io.File; 022import java.io.IOException; 023import java.lang.annotation.ElementType; 024import java.lang.annotation.Retention; 025import java.lang.annotation.RetentionPolicy; 026import java.lang.annotation.Target; 027 028import javax.inject.Inject; 029 030import org.apache.commons.logging.LogFactory; 031import org.junit.runner.notification.Failure; 032import org.junit.runner.notification.RunListener; 033import org.junit.runner.notification.RunNotifier; 034import org.nuxeo.runtime.management.jvm.ThreadDeadlocksDetector; 035import org.nuxeo.runtime.test.runner.FeaturesRunner; 036import org.nuxeo.runtime.test.runner.SimpleFeature; 037 038public class DetectThreadDeadlocksFeature extends SimpleFeature { 039 040 @Retention(RetentionPolicy.RUNTIME) 041 @Target(ElementType.TYPE) 042 public @interface Config { 043 044 public boolean dumpAtTearDown() default false; 045 046 public boolean dumpOnFailure() default true; 047 } 048 049 @Inject 050 protected RunNotifier notifier; 051 052 protected ThreadDeadlocksDetector detector = new ThreadDeadlocksDetector(); 053 054 protected Config config; 055 056 protected final RunListener listener = new RunListener() { 057 @Override 058 public void testFailure(Failure failure) throws Exception { 059 dump(); 060 } 061 }; 062 063 @Override 064 public void initialize(FeaturesRunner runner) throws Exception { 065 config = runner.getConfig(Config.class); 066 } 067 068 @Override 069 public void beforeRun(FeaturesRunner runner) throws Exception { 070 runner.getInjector().injectMembers(this); 071 if (config.dumpOnFailure()) { 072 notifier.addListener(listener); 073 } 074 detector.schedule(30 * 1000, new ThreadDeadlocksDetector.KillListener()); 075 } 076 077 @Override 078 public void stop(FeaturesRunner runner) throws Exception { 079 if (config.dumpOnFailure()) { 080 notifier.removeListener(listener); 081 } 082 if (config.dumpAtTearDown()) { 083 dump(); 084 } 085 detector.cancel(); 086 } 087 088 protected void dump() throws IOException { 089 long[] detectThreadLock = detector.detectThreadLock(); 090 File dump = detector.dump(detectThreadLock); 091 LogFactory.getLog(DetectThreadDeadlocksFeature.class).warn("Thread dump available at " + dump); 092 } 093}