001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights 003 * reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License 006 * v1.0 as published by the Eclipse Foundation 007 * 008 * or (per the licensee's choosing) 009 * 010 * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 011 */ 012package ch.qos.logback.core.joran; 013 014import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION; 015import static ch.qos.logback.core.spi.ConfigurationEvent.*; 016 017import java.io.File; 018import java.io.FileInputStream; 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.URL; 022import java.net.URLConnection; 023import java.util.List; 024import java.util.concurrent.locks.ReentrantLock; 025 026import org.xml.sax.InputSource; 027 028import ch.qos.logback.core.Context; 029import ch.qos.logback.core.joran.event.SaxEvent; 030import ch.qos.logback.core.joran.event.SaxEventRecorder; 031import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; 032import ch.qos.logback.core.joran.spi.ElementPath; 033import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; 034import ch.qos.logback.core.joran.spi.JoranException; 035import ch.qos.logback.core.joran.spi.RuleStore; 036import ch.qos.logback.core.joran.spi.SaxEventInterpreter; 037import ch.qos.logback.core.joran.spi.SimpleRuleStore; 038import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; 039import ch.qos.logback.core.model.Model; 040import ch.qos.logback.core.model.processor.DefaultProcessor; 041import ch.qos.logback.core.model.processor.ModelInterpretationContext; 042import ch.qos.logback.core.spi.ContextAwareBase; 043import ch.qos.logback.core.spi.ErrorCodes; 044import ch.qos.logback.core.status.StatusUtil; 045 046public abstract class GenericXMLConfigurator extends ContextAwareBase { 047 048 protected SaxEventInterpreter saxEventInterpreter; 049 protected ModelInterpretationContext modelInterpretationContext; 050 051 public ModelInterpretationContext getModelInterpretationContext() { 052 return this.modelInterpretationContext; 053 } 054 private RuleStore ruleStore; 055 056 public URL getTopURL() { 057 return topURL; 058 } 059 060 public void setTopURL(URL topURL) { 061 this.topURL = topURL; 062 } 063 064 URL topURL; 065 066 public final void doConfigure(URL url) throws JoranException { 067 InputStream in = null; 068 try { 069 topURL = url; 070 URLConnection urlConnection = url.openConnection(); 071 // per http://jira.qos.ch/browse/LOGBACK-117 LBCORE-105 072 // per http://jira.qos.ch/browse/LOGBACK-163 LBCORE-127 073 urlConnection.setUseCaches(false); 074 075 in = urlConnection.getInputStream(); 076 doConfigure(in, url.toExternalForm()); 077 } catch (IOException ioe) { 078 String errMsg = "Could not open URL [" + url + "]."; 079 addError(errMsg, ioe); 080 throw new JoranException(errMsg, ioe); 081 } finally { 082 if (in != null) { 083 try { 084 in.close(); 085 } catch (IOException ioe) { 086 String errMsg = "Could not close input stream"; 087 addError(errMsg, ioe); 088 throw new JoranException(errMsg, ioe); 089 } 090 } 091 } 092 } 093 094 public final void doConfigure(String filename) throws JoranException { 095 doConfigure(new File(filename)); 096 } 097 098 public final void doConfigure(File file) throws JoranException { 099 FileInputStream fis = null; 100 try { 101 URL url = file.toURI().toURL(); 102 topURL = url; 103 fis = new FileInputStream(file); 104 doConfigure(fis, url.toExternalForm()); 105 } catch (IOException ioe) { 106 String errMsg = "Could not open [" + file.getPath() + "]."; 107 addError(errMsg, ioe); 108 throw new JoranException(errMsg, ioe); 109 } finally { 110 if (fis != null) { 111 try { 112 fis.close(); 113 } catch (java.io.IOException ioe) { 114 String errMsg = "Could not close [" + file.getName() + "]."; 115 addError(errMsg, ioe); 116 throw new JoranException(errMsg, ioe); 117 } 118 } 119 } 120 } 121 122 /** 123 * Removed in 1.5.27 with no replacement. 124 * 125 * @deprecated 126 */ 127 @Deprecated 128 public static void informContextOfURLUsedForConfiguration(Context context, URL url) { 129 // 130 } 131 132 public final void doConfigure(InputStream inputStream) throws JoranException { 133 doConfigure(new InputSource(inputStream)); 134 } 135 136 public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { 137 InputSource inputSource = new InputSource(inputStream); 138 inputSource.setSystemId(systemId); 139 doConfigure(inputSource); 140 } 141 142 protected abstract void addElementSelectorAndActionAssociations(RuleStore rs); 143 144 protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter); 145 146 protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { 147 // nothing by default 148 } 149 150 protected ElementPath initialElementPath() { 151 return new ElementPath(); 152 } 153 154 protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) { 155 RuleStore rs = getRuleStore(); 156 addElementSelectorAndActionAssociations(rs); 157 this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents); 158 SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext(); 159 interpretationContext.setContext(context); 160 setImplicitRuleSupplier(saxEventInterpreter); 161 } 162 163 public RuleStore getRuleStore() { 164 if(this.ruleStore == null) { 165 this.ruleStore = new SimpleRuleStore(context); 166 } 167 return this.ruleStore; 168 } 169 170 protected void buildModelInterpretationContext() { 171 this.modelInterpretationContext = new ModelInterpretationContext(context); 172 this.modelInterpretationContext.setTopURL(topURL); 173 addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry()); 174 } 175 176 // this is the most inner form of doConfigure whereto other doConfigure 177 // methods ultimately delegate 178 public final void doConfigure(final InputSource inputSource) throws JoranException { 179 180 context.fireConfigurationEvent(newConfigurationStartedEvent(this)); 181 long threshold = System.currentTimeMillis(); 182 183 SaxEventRecorder recorder = populateSaxEventRecorder(inputSource); 184 List<SaxEvent> saxEvents = recorder.getSaxEventList(); 185 if (saxEvents.isEmpty()) { 186 addWarn("Empty sax event list"); 187 return; 188 } 189 Model top = buildModelFromSaxEventList(recorder.getSaxEventList()); 190 if (top == null) { 191 addError(ErrorCodes.EMPTY_MODEL_STACK); 192 return; 193 } 194 sanityCheck(top); 195 processModel(top); 196 197 // no exceptions at this level 198 StatusUtil statusUtil = new StatusUtil(context); 199 if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { 200 addInfo("Registering current configuration as safe fallback point"); 201 registerSafeConfiguration(top); 202 context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this)); 203 } else { 204 context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this)); 205 } 206 207 208 } 209 210 public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { 211 SaxEventRecorder recorder = new SaxEventRecorder(context); 212 recorder.recordEvents(inputSource); 213 return recorder; 214 } 215 216 public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException { 217 buildSaxEventInterpreter(saxEvents); 218 playSaxEvents(); 219 Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); 220 return top; 221 } 222 223 private void playSaxEvents() throws JoranException { 224 saxEventInterpreter.getEventPlayer().play(); 225 } 226 227 public void processModel(Model model) { 228 buildModelInterpretationContext(); 229 this.modelInterpretationContext.setTopModel(model); 230 modelInterpretationContext.setConfiguratorHint(this); 231 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 232 addModelHandlerAssociations(defaultProcessor); 233 234 // disallow simultaneous configurations of the same context 235 ReentrantLock configurationLock = context.getConfigurationLock(); 236 237 try { 238 configurationLock.lock(); 239 defaultProcessor.process(model); 240 } finally { 241 configurationLock.unlock(); 242 } 243 } 244 245 /** 246 * Perform sanity check and issue warning if necessary. 247 * 248 * Default implementation does nothing. 249 * 250 * @param topModel 251 * @since 1.3.2 and 1.4.2 252 */ 253 protected void sanityCheck(Model topModel) { 254 255 } 256 257 protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 258 } 259 260 /** 261 * Register the current event list in currently in the interpreter as a safe 262 * configuration point. 263 * 264 * @since 0.9.30 265 */ 266 public void registerSafeConfiguration(Model top) { 267 context.putObject(SAFE_JORAN_CONFIGURATION, top); 268 } 269 270 /** 271 * Recall the event list previously registered as a safe point. 272 */ 273 public Model recallSafeConfiguration() { 274 return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); 275 } 276 277}