001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2015, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.joran; 015 016import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION; 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 055 public final void doConfigure(URL url) throws JoranException { 056 InputStream in = null; 057 try { 058 informContextOfURLUsedForConfiguration(getContext(), url); 059 URLConnection urlConnection = url.openConnection(); 060 // per http://jira.qos.ch/browse/LBCORE-105 061 // per http://jira.qos.ch/browse/LBCORE-127 062 urlConnection.setUseCaches(false); 063 064 in = urlConnection.getInputStream(); 065 doConfigure(in, url.toExternalForm()); 066 } catch (IOException ioe) { 067 String errMsg = "Could not open URL [" + url + "]."; 068 addError(errMsg, ioe); 069 throw new JoranException(errMsg, ioe); 070 } finally { 071 if (in != null) { 072 try { 073 in.close(); 074 } catch (IOException ioe) { 075 String errMsg = "Could not close input stream"; 076 addError(errMsg, ioe); 077 throw new JoranException(errMsg, ioe); 078 } 079 } 080 } 081 } 082 083 public final void doConfigure(String filename) throws JoranException { 084 doConfigure(new File(filename)); 085 } 086 087 public final void doConfigure(File file) throws JoranException { 088 FileInputStream fis = null; 089 try { 090 URL url = file.toURI().toURL(); 091 informContextOfURLUsedForConfiguration(getContext(), url); 092 fis = new FileInputStream(file); 093 doConfigure(fis, url.toExternalForm()); 094 } catch (IOException ioe) { 095 String errMsg = "Could not open [" + file.getPath() + "]."; 096 addError(errMsg, ioe); 097 throw new JoranException(errMsg, ioe); 098 } finally { 099 if (fis != null) { 100 try { 101 fis.close(); 102 } catch (java.io.IOException ioe) { 103 String errMsg = "Could not close [" + file.getName() + "]."; 104 addError(errMsg, ioe); 105 throw new JoranException(errMsg, ioe); 106 } 107 } 108 } 109 } 110 111 public static void informContextOfURLUsedForConfiguration(Context context, URL url) { 112 ConfigurationWatchListUtil.setMainWatchURL(context, url); 113 } 114 115 public final void doConfigure(InputStream inputStream) throws JoranException { 116 doConfigure(new InputSource(inputStream)); 117 } 118 119 public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { 120 InputSource inputSource = new InputSource(inputStream); 121 inputSource.setSystemId(systemId); 122 doConfigure(inputSource); 123 } 124 125 protected abstract void addElementSelectorAndActionAssociations(RuleStore rs); 126 127 protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter); 128 129 protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { 130 // nothing by default 131 } 132 133 protected ElementPath initialElementPath() { 134 return new ElementPath(); 135 } 136 137 protected void buildSaxEventInterpreter(List<SaxEvent> saxEvents) { 138 RuleStore rs = new SimpleRuleStore(context); 139 addElementSelectorAndActionAssociations(rs); 140 this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents); 141 SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext(); 142 interpretationContext.setContext(context); 143 setImplicitRuleSupplier(saxEventInterpreter); 144 } 145 146 147 protected void buildModelInterpretationContext() { 148 this.modelInterpretationContext = new ModelInterpretationContext(context); 149 addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry()); 150 } 151 152 153 // this is the most inner form of doConfigure whereto other doConfigure 154 // methods ultimately delegate 155 public final void doConfigure(final InputSource inputSource) throws JoranException { 156 157 long threshold = System.currentTimeMillis(); 158 159 SaxEventRecorder recorder = populateSaxEventRecorder(inputSource); 160 Model top = buildModelFromSaxEventList(recorder.getSaxEventList()); 161 if(top == null) { 162 addError(ErrorCodes.EMPTY_MODEL_STACK); 163 return; 164 } 165 processModel(top); 166 167 // no exceptions a this level 168 StatusUtil statusUtil = new StatusUtil(context); 169 if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { 170 addInfo("Registering current configuration as safe fallback point"); 171 registerSafeConfiguration(top); 172 } 173 } 174 175 public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { 176 SaxEventRecorder recorder = new SaxEventRecorder(context); 177 recorder.recordEvents(inputSource); 178 return recorder; 179 } 180 181 public Model buildModelFromSaxEventList(List<SaxEvent> saxEvents) throws JoranException { 182 buildSaxEventInterpreter(saxEvents); 183 playSaxEvents(); 184 Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); 185 return top; 186 } 187 188 private void playSaxEvents() throws JoranException { 189 saxEventInterpreter.getEventPlayer().play(); 190 } 191 192 public void processModel(Model model) { 193 buildModelInterpretationContext(); 194 DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); 195 addModelHandlerAssociations(defaultProcessor); 196 197 // disallow simultaneous configurations of the same context 198 synchronized (context.getConfigurationLock()) { 199 defaultProcessor.process(model); 200 } 201 } 202 203 protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { 204 } 205 206 /** 207 * Register the current event list in currently in the interpreter as a safe 208 * configuration point. 209 * 210 * @since 0.9.30 211 */ 212 public void registerSafeConfiguration(Model top) { 213 context.putObject(SAFE_JORAN_CONFIGURATION, top); 214 } 215 216 /** 217 * Recall the event list previously registered as a safe point. 218 */ 219 public Model recallSafeConfiguration() { 220 return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); 221 } 222}