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}