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.newConfigurationEndedEvent; 016import static ch.qos.logback.core.spi.ConfigurationEvent.newConfigurationStartedEvent; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.IOException; 021import java.io.InputStream; 022import java.net.URL; 023import java.net.URLConnection; 024import java.util.List; 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/LBCORE-105 062 // per http://jira.qos.ch/browse/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 } 186 context.fireConfigurationEvent(newConfigurationEndedEvent(this)); 187 188 } 189 190 public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { 191 SaxEventRecorder recorder = new SaxEventRecorder(context); 192 recorder.recordEvents(inputSource); 193 return recorder; 194 } 195 196 public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException { 197 buildSaxEventInterpreter(saxEvents); 198 playSaxEvents(); 199 Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); 200 return top; 201 } 202 203 private void playSaxEvents() throws JoranException { 204 saxEventInterpreter.getEventPlayer().play(); 205 } 206 207 public void processModel(Model model) { 208 buildModelInterpretationContext(); 209 this.modelInterpretationContext.setTopModel(model); 210 modelInterpretationContext.setConfiguratorHint(this); 211 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 212 addModelHandlerAssociations(defaultProcessor); 213 214 // disallow simultaneous configurations of the same context 215 synchronized (context.getConfigurationLock()) { 216 defaultProcessor.process(model); 217 } 218 } 219 220 /** 221 * Perform sanity check and issue warning if necessary. 222 * 223 * Default implementation does nothing. 224 * 225 * @param topModel 226 * @since 1.3.2 and 1.4.2 227 */ 228 protected void sanityCheck(Model topModel) { 229 230 } 231 232 protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 233 } 234 235 /** 236 * Register the current event list in currently in the interpreter as a safe 237 * configuration point. 238 * 239 * @since 0.9.30 240 */ 241 public void registerSafeConfiguration(Model top) { 242 context.putObject(SAFE_JORAN_CONFIGURATION, top); 243 } 244 245 /** 246 * Recall the event list previously registered as a safe point. 247 */ 248 public Model recallSafeConfiguration() { 249 return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); 250 } 251}