001/* 002 * Copyright (c) 2024 QOS.ch Sarl (Switzerland) 003 * All rights reserved. 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining 006 * a copy of this software and associated documentation files (the 007 * "Software"), to deal in the Software without restriction, including 008 * without limitation the rights to use, copy, modify, merge, publish, 009 * distribute, sublicense, and/or sell copies of the Software, and to 010 * permit persons to whom the Software is furnished to do so, subject to 011 * the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be 014 * included in all copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 017 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 018 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 021 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 023 * 024 * 025 * 026 */ 027 028package ch.qos.logback.tyler.base.handler; 029 030import ch.qos.logback.core.Context; 031import ch.qos.logback.core.CoreConstants; 032import ch.qos.logback.core.hook.DefaultShutdownHook; 033import ch.qos.logback.core.model.Model; 034import ch.qos.logback.core.model.ShutdownHookModel; 035import ch.qos.logback.core.model.processor.ModelHandlerBase; 036import ch.qos.logback.core.model.processor.ModelHandlerException; 037import ch.qos.logback.core.model.processor.ModelInterpretationContext; 038import ch.qos.logback.core.util.OptionHelper; 039import ch.qos.logback.core.util.StringUtil; 040import ch.qos.logback.tyler.base.TylerModelInterpretationContext; 041import ch.qos.logback.tyler.base.util.ClassUtil; 042import com.squareup.javapoet.ClassName; 043import com.squareup.javapoet.FieldSpec; 044import com.squareup.javapoet.MethodSpec; 045 046import static ch.qos.logback.tyler.base.TylerConstants.SETUP; 047 048public class ShutdownHookModelHandler extends ModelHandlerBase { 049 050 static final String DEFAULT_SHUTDOWN_HOOK_CLASSNAME = DefaultShutdownHook.class.getName(); 051 static final String OLD_SHUTDOWN_HOOK_CLASSNAME = "ch.qos.logback.core.hook.DelayingShutdownHook"; 052 static public final String RENAME_WARNING = OLD_SHUTDOWN_HOOK_CLASSNAME + " was renamed as "+ DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 053 054 ImplicitModelHandlerData implicitModelHandlerData; 055 boolean inError = false; 056 057 public ShutdownHookModelHandler(Context context) { 058 super(context); 059 } 060 061 static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { 062 return new ShutdownHookModelHandler(context); 063 } 064 065 @Override 066 protected Class<ShutdownHookModel> getSupportedModelClass() { 067 return ShutdownHookModel.class; 068 } 069 070 071 @Override 072 public void handle(ModelInterpretationContext mic, Model model) 073 throws ModelHandlerException { 074 075 ShutdownHookModel shutdownHookModel = (ShutdownHookModel) model; 076 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 077 078 String className = shutdownHookModel.getClassName(); 079 if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { 080 className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 081 addInfo("Assuming className [" + className + "]"); 082 } else { 083 className = mic.getImport(className); 084 if(className.equals(OLD_SHUTDOWN_HOOK_CLASSNAME)) { 085 className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 086 addWarn(RENAME_WARNING); 087 addWarn("Please use the new class name"); 088 } 089 } 090 091 addInfo("About to instantiate shutdown hook of type [" + className + "]"); 092 093 MethodSpec.Builder methodSpec = addJavaStatement(tmic, className); 094 this.implicitModelHandlerData = ImplicitModelHandlerData.makeInstance(this, methodSpec, className); 095 if(implicitModelHandlerData != null) { 096 mic.pushObject(implicitModelHandlerData); 097 } else { 098 addError("Could not make implicitModelHandlerData for ["+className+"]"); 099 model.markAsSkipped(); 100 inError = true; 101 } 102 103 } 104 105 MethodSpec.Builder addJavaStatement(TylerModelInterpretationContext tmic, String fqcnStr) { 106 String simpleName = ClassUtil.extractSimpleClassName(fqcnStr); 107 ClassName desiredCN = ClassName.get(ClassUtil.extractPackageName(fqcnStr), 108 simpleName); 109 110 final String variableName = StringUtil.lowercaseFirstLetter(simpleName); 111 final String hookTreadVariableName = "hookThread"; 112 final FieldSpec contextFieldSpec = tmic.getContextFieldSpec(); 113 114 MethodSpec.Builder setupMethodSpec = MethodSpec.methodBuilder( 115 SETUP + simpleName).returns(void.class); 116 setupMethodSpec.addStatement("$1T $2N = new $1T()", desiredCN, variableName); 117 setupMethodSpec.addStatement("$N.setContext($N)", variableName, contextFieldSpec); 118 setupMethodSpec.addCode("\n"); 119 setupMethodSpec.addStatement("Thread $N = new Thread($N, \"Logback shutdown hook [\" + $N.getName() + \"]\")", hookTreadVariableName, variableName, contextFieldSpec); 120 setupMethodSpec.addStatement("addInfo(\"Registering shutdown hook with JVM runtime.\")"); 121 122 setupMethodSpec.addStatement("$N.putObject($T.SHUTDOWN_HOOK_THREAD, $N)", contextFieldSpec, CoreConstants.class, hookTreadVariableName); 123 setupMethodSpec.addStatement("Runtime.getRuntime().addShutdownHook($N)", hookTreadVariableName); 124 125 return setupMethodSpec; 126 127 } 128 129 @Override 130 public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { 131 if (inError) { 132 return; 133 } 134 Object o = mic.peekObject(); 135 if (o != implicitModelHandlerData) { 136 addWarn("The object at the of the stack is not the ImplicitModelHandlerData pushed earlier."); 137 } else { 138 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 139 tmic.popObject(); 140 MethodSpec.Builder mMethodBuilder = implicitModelHandlerData.methodSpecBuilder; 141 MethodSpec methodSpec = mMethodBuilder.build(); 142 tmic.tylerConfiguratorTSB.addMethod(methodSpec); 143 tmic.configureMethodSpecBuilder.addStatement("$N()", methodSpec); 144 } 145 } 146}