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 final void doConfigure(URL url) throws JoranException { 057 InputStream in = null; 058 try { 059 informContextOfURLUsedForConfiguration(getContext(), url); 060 URLConnection urlConnection = url.openConnection(); 061 // per http://jira.qos.ch/browse/LOGBACK-117 LBCORE-105 062 // per http://jira.qos.ch/browse/LOGBACK-163 LBCORE-127 063 urlConnection.setUseCaches(false); 064 065 in = urlConnection.getInputStream(); 066 doConfigure(in, url.toExternalForm()); 067 } catch (IOException ioe) { 068 String errMsg = "Could not open URL [" + url + "]."; 069 addError(errMsg, ioe); 070 throw new JoranException(errMsg, ioe); 071 } finally { 072 if (in != null) { 073 try { 074 in.close(); 075 } catch (IOException ioe) { 076 String errMsg = "Could not close input stream"; 077 addError(errMsg, ioe); 078 throw new JoranException(errMsg, ioe); 079 } 080 } 081 } 082 } 083 084 public final void doConfigure(String filename) throws JoranException { 085 doConfigure(new File(filename)); 086 } 087 088 public final void doConfigure(File file) throws JoranException { 089 FileInputStream fis = null; 090 try { 091 URL url = file.toURI().toURL(); 092 informContextOfURLUsedForConfiguration(getContext(), url); 093 fis = new FileInputStream(file); 094 doConfigure(fis, url.toExternalForm()); 095 } catch (IOException ioe) { 096 String errMsg = "Could not open [" + file.getPath() + "]."; 097 addError(errMsg, ioe); 098 throw new JoranException(errMsg, ioe); 099 } finally { 100 if (fis != null) { 101 try { 102 fis.close(); 103 } catch (java.io.IOException ioe) { 104 String errMsg = "Could not close [" + file.getName() + "]."; 105 addError(errMsg, ioe); 106 throw new JoranException(errMsg, ioe); 107 } 108 } 109 } 110 } 111 112 public static void informContextOfURLUsedForConfiguration(Context context, URL url) { 113 ConfigurationWatchListUtil.setMainWatchURL(context, url); 114 } 115 116 public final void doConfigure(InputStream inputStream) throws JoranException { 117 doConfigure(new InputSource(inputStream)); 118 } 119 120 public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { 121 InputSource inputSource = new InputSource(inputStream); 122 inputSource.setSystemId(systemId); 123 doConfigure(inputSource); 124 } 125 126 protected abstract void addElementSelectorAndActionAssociations(RuleStore rs); 127 128 protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter); 129 130 protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { 131 // nothing by default 132 } 133 134 protected ElementPath initialElementPath() { 135 return new ElementPath(); 136 } 137 138 protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) { 139 RuleStore rs = getRuleStore(); 140 addElementSelectorAndActionAssociations(rs); 141 this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents); 142 SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext(); 143 interpretationContext.setContext(context); 144 setImplicitRuleSupplier(saxEventInterpreter); 145 } 146 147 public RuleStore getRuleStore() { 148 if(this.ruleStore == null) { 149 this.ruleStore = new SimpleRuleStore(context); 150 } 151 return this.ruleStore; 152 } 153 154 protected void buildModelInterpretationContext() { 155 this.modelInterpretationContext = new ModelInterpretationContext(context); 156 addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry()); 157 } 158 159 // this is the most inner form of doConfigure whereto other doConfigure 160 // methods ultimately delegate 161 public final void doConfigure(final InputSource inputSource) throws JoranException { 162 163 context.fireConfigurationEvent(newConfigurationStartedEvent(this)); 164 long threshold = System.currentTimeMillis(); 165 166 SaxEventRecorder recorder = populateSaxEventRecorder(inputSource); 167 List<SaxEvent> saxEvents = recorder.getSaxEventList(); 168 if (saxEvents.isEmpty()) { 169 addWarn("Empty sax event list"); 170 return; 171 } 172 Model top = buildModelFromSaxEventList(recorder.getSaxEventList()); 173 if (top == null) { 174 addError(ErrorCodes.EMPTY_MODEL_STACK); 175 return; 176 } 177 sanityCheck(top); 178 processModel(top); 179 180 // no exceptions at this level 181 StatusUtil statusUtil = new StatusUtil(context); 182 if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { 183 addInfo("Registering current configuration as safe fallback point"); 184 registerSafeConfiguration(top); 185 context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this)); 186 } else { 187 context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this)); 188 } 189 190 191 } 192 193 public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { 194 SaxEventRecorder recorder = new SaxEventRecorder(context); 195 recorder.recordEvents(inputSource); 196 return recorder; 197 } 198 199 public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException { 200 buildSaxEventInterpreter(saxEvents); 201 playSaxEvents(); 202 Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); 203 return top; 204 } 205 206 private void playSaxEvents() throws JoranException { 207 saxEventInterpreter.getEventPlayer().play(); 208 } 209 210 public void processModel(Model model) { 211 buildModelInterpretationContext(); 212 this.modelInterpretationContext.setTopModel(model); 213 modelInterpretationContext.setConfiguratorHint(this); 214 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 215 addModelHandlerAssociations(defaultProcessor); 216 217 // disallow simultaneous configurations of the same context 218 ReentrantLock configurationLock = context.getConfigurationLock(); 219 220 try { 221 configurationLock.lock(); 222 defaultProcessor.process(model); 223 } finally { 224 configurationLock.unlock(); 225 } 226 } 227 228 /** 229 * Perform sanity check and issue warning if necessary. 230 * 231 * Default implementation does nothing. 232 * 233 * @param topModel 234 * @since 1.3.2 and 1.4.2 235 */ 236 protected void sanityCheck(Model topModel) { 237 238 } 239 240 protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 241 } 242 243 /** 244 * Register the current event list in currently in the interpreter as a safe 245 * configuration point. 246 * 247 * @since 0.9.30 248 */ 249 public void registerSafeConfiguration(Model top) { 250 context.putObject(SAFE_JORAN_CONFIGURATION, top); 251 } 252 253 /** 254 * Recall the event list previously registered as a safe point. 255 */ 256 public Model recallSafeConfiguration() { 257 return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); 258 } 259}