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 public final void doConfigure(InputStream inputStream) throws JoranException { 123 doConfigure(new InputSource(inputStream)); 124 } 125 126 public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { 127 InputSource inputSource = new InputSource(inputStream); 128 inputSource.setSystemId(systemId); 129 doConfigure(inputSource); 130 } 131 132 protected abstract void addElementSelectorAndActionAssociations(RuleStore rs); 133 134 protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter); 135 136 protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { 137 // nothing by default 138 } 139 140 protected ElementPath initialElementPath() { 141 return new ElementPath(); 142 } 143 144 protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) { 145 RuleStore rs = getRuleStore(); 146 addElementSelectorAndActionAssociations(rs); 147 this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents); 148 SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext(); 149 interpretationContext.setContext(context); 150 setImplicitRuleSupplier(saxEventInterpreter); 151 } 152 153 public RuleStore getRuleStore() { 154 if(this.ruleStore == null) { 155 this.ruleStore = new SimpleRuleStore(context); 156 } 157 return this.ruleStore; 158 } 159 160 protected void buildModelInterpretationContext() { 161 this.modelInterpretationContext = new ModelInterpretationContext(context); 162 this.modelInterpretationContext.setTopURL(topURL); 163 addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry()); 164 } 165 166 // this is the most inner form of doConfigure whereto other doConfigure 167 // methods ultimately delegate 168 public final void doConfigure(final InputSource inputSource) throws JoranException { 169 170 context.fireConfigurationEvent(newConfigurationStartedEvent(this)); 171 long threshold = System.currentTimeMillis(); 172 173 SaxEventRecorder recorder = populateSaxEventRecorder(inputSource); 174 List<SaxEvent> saxEvents = recorder.getSaxEventList(); 175 if (saxEvents.isEmpty()) { 176 addWarn("Empty sax event list"); 177 return; 178 } 179 Model top = buildModelFromSaxEventList(recorder.getSaxEventList()); 180 if (top == null) { 181 addError(ErrorCodes.EMPTY_MODEL_STACK); 182 return; 183 } 184 sanityCheck(top); 185 processModel(top); 186 187 // no exceptions at this level 188 StatusUtil statusUtil = new StatusUtil(context); 189 if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { 190 addInfo("Registering current configuration as safe fallback point"); 191 registerSafeConfiguration(top); 192 context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this)); 193 } else { 194 context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this)); 195 } 196 197 198 } 199 200 public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { 201 SaxEventRecorder recorder = new SaxEventRecorder(context); 202 recorder.recordEvents(inputSource); 203 return recorder; 204 } 205 206 public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException { 207 buildSaxEventInterpreter(saxEvents); 208 playSaxEvents(); 209 Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); 210 return top; 211 } 212 213 private void playSaxEvents() throws JoranException { 214 saxEventInterpreter.getEventPlayer().play(); 215 } 216 217 public void processModel(Model model) { 218 buildModelInterpretationContext(); 219 this.modelInterpretationContext.setTopModel(model); 220 modelInterpretationContext.setConfiguratorHint(this); 221 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 222 addModelHandlerAssociations(defaultProcessor); 223 224 // disallow simultaneous configurations of the same context 225 ReentrantLock configurationLock = context.getConfigurationLock(); 226 227 try { 228 configurationLock.lock(); 229 defaultProcessor.process(model); 230 } finally { 231 configurationLock.unlock(); 232 } 233 } 234 235 /** 236 * Perform sanity check and issue warning if necessary. 237 * 238 * Default implementation does nothing. 239 * 240 * @param topModel 241 * @since 1.3.2 and 1.4.2 242 */ 243 protected void sanityCheck(Model topModel) { 244 245 } 246 247 protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 248 } 249 250 /** 251 * Register the current event list in currently in the interpreter as a safe 252 * configuration point. 253 * 254 * @since 0.9.30 255 */ 256 public void registerSafeConfiguration(Model top) { 257 context.putObject(SAFE_JORAN_CONFIGURATION, top); 258 } 259 260 /** 261 * Recall the event list previously registered as a safe point. 262 */ 263 public Model recallSafeConfiguration() { 264 return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); 265 } 266 267}