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