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.hook.ShutdownHook; 034import ch.qos.logback.core.model.Model; 035import ch.qos.logback.core.model.ShutdownHookModel; 036import ch.qos.logback.core.model.processor.ModelHandlerBase; 037import ch.qos.logback.core.model.processor.ModelHandlerException; 038import ch.qos.logback.core.model.processor.ModelInterpretationContext; 039import ch.qos.logback.core.util.OptionHelper; 040import ch.qos.logback.core.util.StringUtil; 041import ch.qos.logback.tyler.base.TylerModelInterpretationContext; 042import ch.qos.logback.tyler.base.util.ClassUtil; 043import ch.qos.logback.tyler.base.util.VariableNameUtil; 044import com.squareup.javapoet.ClassName; 045import com.squareup.javapoet.FieldSpec; 046import com.squareup.javapoet.MethodSpec; 047 048import static ch.qos.logback.tyler.base.TylerConstants.SETUP; 049 050public class ShutdownHookModelHandler extends ModelHandlerBase { 051 052 static final String DEFAULT_SHUTDOWN_HOOK_CLASSNAME = DefaultShutdownHook.class.getName(); 053 static final String OLD_SHUTDOWN_HOOK_CLASSNAME = "ch.qos.logback.core.hook.DelayingShutdownHook"; 054 static public final String RENAME_WARNING = OLD_SHUTDOWN_HOOK_CLASSNAME + " was renamed as "+ DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 055 056 ImplicitModelHandlerData implicitModelHandlerData; 057 boolean inError = false; 058 059 public ShutdownHookModelHandler(Context context) { 060 super(context); 061 } 062 063 static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { 064 return new ShutdownHookModelHandler(context); 065 } 066 067 @Override 068 protected Class<ShutdownHookModel> getSupportedModelClass() { 069 return ShutdownHookModel.class; 070 } 071 072 073 @Override 074 public void handle(ModelInterpretationContext mic, Model model) 075 throws ModelHandlerException { 076 077 ShutdownHookModel shutdownHookModel = (ShutdownHookModel) model; 078 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 079 080 String className = shutdownHookModel.getClassName(); 081 if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { 082 className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 083 addInfo("Assuming className [" + className + "]"); 084 } else { 085 className = mic.getImport(className); 086 if(className.equals(OLD_SHUTDOWN_HOOK_CLASSNAME)) { 087 className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; 088 addWarn(RENAME_WARNING); 089 addWarn("Please use the new class name"); 090 } 091 } 092 093 addInfo("About to instantiate shutdown hook of type [" + className + "]"); 094 095 MethodSpec.Builder methodSpec = addJavaStatement(tmic, className); 096 this.implicitModelHandlerData = ImplicitModelHandlerData.makeInstance(this, methodSpec, className); 097 if(implicitModelHandlerData != null) { 098 mic.pushObject(implicitModelHandlerData); 099 } else { 100 addError("Could not make implicitModelHandlerData for ["+className+"]"); 101 model.markAsSkipped(); 102 inError = true; 103 } 104 105 } 106 107 MethodSpec.Builder addJavaStatement(TylerModelInterpretationContext tmic, String fqcnStr) { 108 String simpleName = ClassUtil.extractSimpleClassName(fqcnStr); 109 ClassName desiredCN = ClassName.get(ClassUtil.extractPackageName(fqcnStr), 110 simpleName); 111 112 final String variableName = StringUtil.lowercaseFirstLetter(simpleName); 113 final String hookTreadVariableName = "hookThread"; 114 final FieldSpec contextFieldSpec = tmic.getContextFieldSpec(); 115 116 MethodSpec.Builder setupMethodSpec = MethodSpec.methodBuilder( 117 SETUP + simpleName).returns(void.class); 118 setupMethodSpec.addStatement("$1T $2N = new $1T()", desiredCN, variableName); 119 setupMethodSpec.addStatement("$N.setContext($N)", variableName, contextFieldSpec); 120 setupMethodSpec.addCode("\n"); 121 setupMethodSpec.addStatement("Thread $N = new Thread($N, \"Logback shutdown hook [\" + $N.getName() + \"]\")", hookTreadVariableName, variableName, contextFieldSpec); 122 setupMethodSpec.addStatement("addInfo(\"Registering shutdown hook with JVM runtime.\")"); 123 124 setupMethodSpec.addStatement("$N.putObject($T.SHUTDOWN_HOOK_THREAD, $N)", contextFieldSpec, CoreConstants.class, hookTreadVariableName); 125 setupMethodSpec.addStatement("Runtime.getRuntime().addShutdownHook($N)", hookTreadVariableName); 126 127 return setupMethodSpec; 128 129 } 130 131 @Override 132 public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { 133 if (inError) { 134 return; 135 } 136 Object o = mic.peekObject(); 137 if (o != implicitModelHandlerData) { 138 addWarn("The object at the of the stack is not the ImplicitModelHandlerData pushed earlier."); 139 } else { 140 TylerModelInterpretationContext tmic = (TylerModelInterpretationContext) mic; 141 tmic.popObject(); 142 MethodSpec.Builder mMethodBuilder = implicitModelHandlerData.methodSpecBuilder; 143 MethodSpec methodSpec = mMethodBuilder.build(); 144 tmic.tylerConfiguratorTSB.addMethod(methodSpec); 145 tmic.configureMethodSpecBuilder.addStatement("$N()", methodSpec); 146 } 147 } 148}