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}