001/*
002 * Logback: the reliable, generic, fast and flexible logging framework.
003 * Copyright (C) 1999-2024, 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 */
014
015package ch.qos.logback.core.model.processor;
016
017import ch.qos.logback.core.Context;
018import ch.qos.logback.core.joran.GenericXMLConfigurator;
019import ch.qos.logback.core.joran.event.SaxEvent;
020import ch.qos.logback.core.joran.event.SaxEventRecorder;
021import ch.qos.logback.core.joran.spi.JoranException;
022import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil;
023import ch.qos.logback.core.model.IncludeModel;
024import ch.qos.logback.core.model.Model;
025import ch.qos.logback.core.spi.ErrorCodes;
026import ch.qos.logback.core.util.Loader;
027import ch.qos.logback.core.util.OptionHelper;
028
029import java.io.File;
030import java.io.IOException;
031import java.io.InputStream;
032import java.net.MalformedURLException;
033import java.net.URI;
034import java.net.URL;
035import java.util.List;
036import java.util.function.Supplier;
037
038import static ch.qos.logback.core.joran.JoranConstants.CONFIGURATION_TAG;
039import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG;
040
041/**
042 * @since 1.5.5
043 */
044public class IncludeModelHandler extends ResourceHandlerBase {
045    boolean inError = false;
046
047    public IncludeModelHandler(Context context) {
048        super(context);
049    }
050
051    static public IncludeModelHandler makeInstance(Context context, ModelInterpretationContext mic) {
052        return new IncludeModelHandler(context);
053    }
054
055    @Override
056    protected Class<IncludeModel> getSupportedModelClass() {
057        return IncludeModel.class;
058    }
059
060    @Override
061    public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
062        IncludeModel includeModel = (IncludeModel) model;
063
064        this.optional = OptionHelper.toBoolean(includeModel.getOptional(), false);
065
066        if (!checkAttributes(includeModel)) {
067            inError = true;
068            return;
069        }
070
071        InputStream in = getInputStream(mic, includeModel);
072        if(in == null) {
073            inError = true;
074            return;
075        }
076
077        SaxEventRecorder recorder = null;
078
079        try {
080            recorder = populateSaxEventRecorder(in);
081
082            List<SaxEvent> saxEvents = recorder.getSaxEventList();
083            if (saxEvents.isEmpty()) {
084                addWarn("Empty sax event list");
085                return;
086            }
087
088            Supplier<? extends GenericXMLConfigurator> jcSupplier = mic.getConfiguratorSupplier();
089            if (jcSupplier == null) {
090                addError("null configurator supplier. Abandoning inclusion of [" + attributeInUse + "]");
091                inError = true;
092                return;
093            }
094
095            GenericXMLConfigurator genericXMLConfigurator = jcSupplier.get();
096            genericXMLConfigurator.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG);
097
098            Model modelFromIncludedFile = genericXMLConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList());
099            if (modelFromIncludedFile == null) {
100                addError(ErrorCodes.EMPTY_MODEL_STACK);
101                return;
102            }
103
104            includeModel.getSubModels().addAll(modelFromIncludedFile.getSubModels());
105
106        } catch (JoranException e) {
107            inError = true;
108            addError("Error processing XML data in [" + attributeInUse + "]", e);
109        }
110    }
111
112    public SaxEventRecorder populateSaxEventRecorder(final InputStream inputStream) throws JoranException {
113        SaxEventRecorder recorder = new SaxEventRecorder(context);
114        recorder.recordEvents(inputStream);
115        return recorder;
116    }
117
118    private InputStream getInputStream(ModelInterpretationContext mic, IncludeModel includeModel) {
119        URL inputURL = getInputURL(mic, includeModel);
120        if (inputURL == null)
121            return null;
122
123        ConfigurationWatchListUtil.addToWatchList(context, inputURL);
124        return openURL(inputURL);
125    }
126
127}