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 ch.qos.logback.core.Context;
017import ch.qos.logback.core.joran.event.SaxEvent;
018import ch.qos.logback.core.joran.event.SaxEventRecorder;
019import ch.qos.logback.core.joran.spi.*;
020import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
021import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache;
022import ch.qos.logback.core.spi.ContextAwareBase;
023import ch.qos.logback.core.status.StatusUtil;
024
025import org.xml.sax.InputSource;
026
027import java.io.File;
028import java.io.FileInputStream;
029import java.io.IOException;
030import java.io.InputStream;
031import java.net.URL;
032import java.net.URLConnection;
033import java.util.List;
034
035import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION;
036
037public abstract class GenericConfigurator extends ContextAwareBase {
038
039    private BeanDescriptionCache beanDescriptionCache;
040
041    protected Interpreter interpreter;
042
043    public final void doConfigure(URL url) throws JoranException {
044        InputStream in = null;
045        try {
046            informContextOfURLUsedForConfiguration(getContext(), url);
047            URLConnection urlConnection = url.openConnection();
048            // per http://jira.qos.ch/browse/LBCORE-105
049            // per http://jira.qos.ch/browse/LBCORE-127
050            urlConnection.setUseCaches(false);
051
052            in = urlConnection.getInputStream();
053            doConfigure(in, url.toExternalForm());
054        } catch (IOException ioe) {
055            String errMsg = "Could not open URL [" + url + "].";
056            addError(errMsg, ioe);
057            throw new JoranException(errMsg, ioe);
058        } finally {
059            if (in != null) {
060                try {
061                    in.close();
062                } catch (IOException ioe) {
063                    String errMsg = "Could not close input stream";
064                    addError(errMsg, ioe);
065                    throw new JoranException(errMsg, ioe);
066                }
067            }
068        }
069    }
070
071    public final void doConfigure(String filename) throws JoranException {
072        doConfigure(new File(filename));
073    }
074
075    public final void doConfigure(File file) throws JoranException {
076        FileInputStream fis = null;
077        try {
078            URL url = file.toURI().toURL();
079            informContextOfURLUsedForConfiguration(getContext(), url);
080            fis = new FileInputStream(file);
081            doConfigure(fis, url.toExternalForm());
082        } catch (IOException ioe) {
083            String errMsg = "Could not open [" + file.getPath() + "].";
084            addError(errMsg, ioe);
085            throw new JoranException(errMsg, ioe);
086        } finally {
087            if (fis != null) {
088                try {
089                    fis.close();
090                } catch (java.io.IOException ioe) {
091                    String errMsg = "Could not close [" + file.getName() + "].";
092                    addError(errMsg, ioe);
093                    throw new JoranException(errMsg, ioe);
094                }
095            }
096        }
097    }
098
099    public static void informContextOfURLUsedForConfiguration(Context context, URL url) {
100        ConfigurationWatchListUtil.setMainWatchURL(context, url);
101    }
102
103    public final void doConfigure(InputStream inputStream) throws JoranException {
104        doConfigure(new InputSource(inputStream));
105    }
106
107    public final void doConfigure(InputStream inputStream, String systemId) throws JoranException {
108        InputSource inputSource = new InputSource(inputStream);
109        inputSource.setSystemId(systemId);
110        doConfigure(inputSource);
111    }
112
113    protected BeanDescriptionCache getBeanDescriptionCache() {
114        if (beanDescriptionCache == null) {
115            beanDescriptionCache = new BeanDescriptionCache(getContext());
116        }
117        return beanDescriptionCache;
118    }
119
120    protected abstract void addInstanceRules(RuleStore rs);
121
122    protected abstract void addImplicitRules(Interpreter interpreter);
123
124    protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) {
125
126    }
127
128    protected ElementPath initialElementPath() {
129        return new ElementPath();
130    }
131
132    protected void buildInterpreter() {
133        RuleStore rs = new SimpleRuleStore(context);
134        addInstanceRules(rs);
135        this.interpreter = new Interpreter(context, rs, initialElementPath());
136        InterpretationContext interpretationContext = interpreter.getInterpretationContext();
137        interpretationContext.setContext(context);
138        addImplicitRules(interpreter);
139        addDefaultNestedComponentRegistryRules(interpretationContext.getDefaultNestedComponentRegistry());
140    }
141
142    // this is the most inner form of doConfigure whereto other doConfigure
143    // methods ultimately delegate
144    public final void doConfigure(final InputSource inputSource) throws JoranException {
145
146        long threshold = System.currentTimeMillis();
147        // if (!ConfigurationWatchListUtil.wasConfigurationWatchListReset(context)) {
148        // informContextOfURLUsedForConfiguration(getContext(), null);
149        // }
150        SaxEventRecorder recorder = new SaxEventRecorder(context);
151        recorder.recordEvents(inputSource);
152        doConfigure(recorder.saxEventList);
153        // no exceptions a this level
154        StatusUtil statusUtil = new StatusUtil(context);
155        if (statusUtil.noXMLParsingErrorsOccurred(threshold)) {
156            addInfo("Registering current configuration as safe fallback point");
157            registerSafeConfiguration(recorder.saxEventList);
158        }
159    }
160
161    public void doConfigure(final List<SaxEvent> eventList) throws JoranException {
162        buildInterpreter();
163        // disallow simultaneous configurations of the same context
164        synchronized (context.getConfigurationLock()) {
165            interpreter.getEventPlayer().play(eventList);
166        }
167    }
168
169    /**
170     * Register the current event list in currently in the interpreter as a safe
171     * configuration point.
172     *
173     * @since 0.9.30
174     */
175    public void registerSafeConfiguration(List<SaxEvent> eventList) {
176        context.putObject(SAFE_JORAN_CONFIGURATION, eventList);
177    }
178
179    /**
180     * Recall the event list previously registered as a safe point.
181     */
182    @SuppressWarnings("unchecked")
183    public List<SaxEvent> recallSafeConfiguration() {
184        return (List<SaxEvent>) context.getObject(SAFE_JORAN_CONFIGURATION);
185    }
186}