001package io.jstach.jstachio.spi; 002 003import java.lang.System.Logger; 004import java.util.List; 005import java.util.Objects; 006import java.util.ResourceBundle; 007 008import org.eclipse.jdt.annotation.NonNull; 009import org.eclipse.jdt.annotation.Nullable; 010 011import io.jstach.jstache.JStacheConfig; 012 013/** 014 * Runtime Config Service. 015 * <p> 016 * While a majority of jstachio config is static and done at compile time some config like 017 * logging and disabling extensions is needed at runtime. Config and DI agnostic 018 * extensions should use this facility for simple key value based config. 019 * <p> 020 * The default resolved config uses System properties but can be replaced by implementing 021 * this extension. 022 * <p> 023 * Core runtime configuration properties for the {@link JStachioFactory#defaultJStachio() 024 * default JStachio} are: 025 * <ul id="_jstachio_config_properties"> 026 * <li>{@link #REFLECTION_TEMPLATE_DISABLE}</li> 027 * <li>{@link #SERVICELOADER_TEMPLATE_DISABLE}</li> 028 * <li>{@link #LOGGING_DISABLE}</li> 029 * </ul> 030 * <strong>This configuration is for runtime only and NOT {@linkplain JStacheConfig static 031 * configuration} needed for code generation.</strong> 032 * 033 * @see JStachioExtension 034 * @author agentgt 035 */ 036public non-sealed interface JStachioConfig extends JStachioExtension { 037 038 /** 039 * Config key to disable non service loader reflection based lookup of templates. If a 040 * custom JStachio is being used this configuration property maybe irrelevant. 041 * <p> 042 * Valid values are <code>true</code> or <code>false</code>. The default is 043 * <code>false</code>. 044 */ 045 public static String REFLECTION_TEMPLATE_DISABLE = "jstachio.reflection.template.disable"; 046 047 /** 048 * Config key to disable service loader based lookup of templates. If a custom 049 * JStachio is being used this configuration property maybe irrelevant. 050 * <p> 051 * Valid values are <code>true</code> or <code>false</code>. The default is 052 * <code>false</code>. 053 */ 054 public static String SERVICELOADER_TEMPLATE_DISABLE = "jstachio.serviceloader.template.disable"; 055 056 /** 057 * Config key to disable logging. By default logging is enabled and will use the 058 * {@link System.Logger}. If a custom {@link JStachioConfig} is being used this 059 * configuration property maybe irrelevant. 060 * <p> 061 * Valid values are <code>true</code> or <code>false</code>. The default is 062 * <code>false</code>. 063 */ 064 public static String LOGGING_DISABLE = "jstachio.logging.disable"; 065 066 /** 067 * Gets a property from some config implementation. 068 * @param key the key to use to lookup 069 * @return if not found <code>null</code>. 070 */ 071 public @Nullable String getProperty(String key); 072 073 /** 074 * See {@link Boolean#getBoolean(String)}. 075 * @param key the property key 076 * @return only true if string is "true" 077 */ 078 default boolean getBoolean(String key) { 079 String prop = getProperty(key); 080 return Boolean.parseBoolean(prop); 081 } 082 083 /** 084 * Gets the property as a boolean and if no property value is found the fallback is 085 * used. 086 * @param key property key 087 * @param fallback if property has no value this value is used. 088 * @return the parsed boolean or the fallback 089 */ 090 default boolean getBoolean(String key, boolean fallback) { 091 String prop = getProperty(key); 092 if (prop == null) { 093 return fallback; 094 } 095 return Boolean.parseBoolean(prop); 096 } 097 098 /** 099 * A NonNull friendly analog of {@link System#getProperty(String, String)} that will 100 * never return null unlike System.getProperty which is PolyNull. 101 * @param key checked if null and will NPE immediatly if it is 102 * @param fallback used if the retrieved property is null 103 * @return property or fallback if property is not found (<code>null</code>). 104 * @throws NullPointerException if the fallback is null or if the key is null. 105 */ 106 default String requireProperty(String key, String fallback) { 107 if (Objects.isNull(key)) { 108 throw new NullPointerException("key is null"); 109 } 110 String v = getProperty(key); 111 if (v == null) { 112 v = fallback; 113 } 114 if (Objects.isNull(v)) { 115 throw new NullPointerException("fallback is null. key: " + key); 116 } 117 return v; 118 } 119 120 /** 121 * Gets a system logger if the property {@link #LOGGING_DISABLE} is 122 * <code>false</code>. If the property is set to a <code>true</code> value a NOOP 123 * Logger <em>that will not trigger initialization of the System {@link Logger} 124 * facilities</em> will be returned. The NOOP logger is always disabled at every level 125 * and will not produce any output. 126 * @param name the name of the logger usually the class. 127 * @return the System logger. 128 * @see #noopLogger() 129 */ 130 default Logger getLogger(String name) { 131 if (!getBoolean(LOGGING_DISABLE)) { 132 return System.getLogger(name); 133 } 134 return noopLogger(); 135 } 136 137 /** 138 * NOOP Logger <em>that will not trigger initialization of the System {@link Logger} 139 * facilities</em>. The NOOP logger is always disabled at every level and will not 140 * produce any output. 141 * <p> 142 * Extensions might find this useful to set a nonnull Logger field like: 143 * <pre><code class="language-java"> 144 * private Logger logger = JStacheConfig.noopLogger(); 145 * public void init(JStacheConfig config) { 146 * logger = config.getLogger(getClass().getName()); 147 * } 148 * </code> </pre> 149 * @return singleton instance of noop logger 150 */ 151 public static Logger noopLogger() { 152 return NOOPLogger.INSTANCE; 153 } 154 155} 156 157enum NOOPLogger implements Logger { 158 159 INSTANCE; 160 161 @Override 162 public @NonNull String getName() { 163 return "NOOPLogger"; 164 } 165 166 @Override 167 public boolean isLoggable(Level level) { 168 return false; 169 } 170 171 @Override 172 public void log(Level level, @Nullable ResourceBundle bundle, @Nullable String msg, @Nullable Throwable thrown) { 173 // Do nothing 174 } 175 176 @Override 177 public void log(Level level, @Nullable ResourceBundle bundle, @Nullable String format, 178 @Nullable Object @NonNull... params) { 179 // Do nothing 180 } 181 182} 183 184enum SystemPropertyConfig implements JStachioConfig { 185 186 INSTANCE; 187 188 @Override 189 public @Nullable String getProperty(String key) { 190 return System.getProperty(key); 191 } 192 193} 194 195class CompositeConfig implements JStachioConfig { 196 197 private final List<JStachioConfig> configs; 198 199 static JStachioConfig of(List<JStachioConfig> configs) { 200 if (configs.isEmpty()) { 201 return SystemPropertyConfig.INSTANCE; 202 } 203 if (configs.size() == 1) { 204 return configs.get(0); 205 } 206 return new CompositeConfig(configs); 207 208 } 209 210 private CompositeConfig(List<JStachioConfig> configs) { 211 super(); 212 this.configs = configs; 213 } 214 215 @Override 216 public @Nullable String getProperty(String key) { 217 for (var c : configs) { 218 String v = c.getProperty(key); 219 if (v != null) { 220 return v; 221 } 222 } 223 return null; 224 } 225 226 @Override 227 public Logger getLogger(String name) { 228 return configs.get(0).getLogger(name); 229 } 230 231}