/** * Logback: the reliable, generic, fast and flexible logging framework. * Copyright (C) 1999-2013, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ package ch.qos.logback.core.joran.action; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.List; import ch.qos.logback.core.joran.event.SaxEvent; import ch.qos.logback.core.joran.event.SaxEventRecorder; import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; public class IncludeAction extends AbstractIncludeAction { private static final String INCLUDED_TAG = "included"; private static final String CONFIG_TAG = "configuration"; private int eventOffset = 2; /** * Sets the list index into the event player's list, where the events * from the processed include will be inserted * @param offset the array index */ protected void setEventOffset(int offset) { this.eventOffset = offset; } /** * Creates a SAX event recorder based on given parameters. Subclasses * should override this as necessary. * @param in input stream * @param url URL to opened file/resource * @return the newly created recorder */ protected SaxEventRecorder createRecorder(InputStream in, URL url) { return new SaxEventRecorder(getContext()); } /** * Processes an include * @param ic context * @param url URL to file/resource being included */ @Override protected void processInclude(InterpretationContext ic, URL url) throws JoranException { InputStream in = openURL(url); try { if (in != null) { // add URL to watch list in case the "scan" flag is true, in // which case this URL is periodically checked for changes ConfigurationWatchListUtil.addToWatchList(getContext(), url); // parse the include SaxEventRecorder recorder = createRecorder(in, url); recorder.setContext(getContext()); recorder.recordEvents(in); // remove the leading/trailing tags (<included> or <configuration>) trimHeadAndTail(recorder); ic.getJoranInterpreter().getEventPlayer().addEventsDynamically(recorder.getSaxEventList(), this.eventOffset); } } catch (JoranException e) { addError("Failed processing [" + url.toString() + "]", e); } finally { close(in); } } /** * Opens the given URL, logging any exceptions * @param url URL of file/resource to open * @return an input stream to the URL; or {@code null} if the URL could not be opened */ private InputStream openURL(URL url) { try { return url.openStream(); } catch (IOException e) { if (!isOptional()) { String errMsg = "Failed to open [" + url.toString() + "]"; addError(errMsg, e); } return null; } } /** * Removes the head tag and tail tag if they are named either * "included" or "configuration" * @param recorder the SAX Event recorder containing the tags */ private void trimHeadAndTail(SaxEventRecorder recorder) { List<SaxEvent> saxEventList = recorder.getSaxEventList(); if (saxEventList.size() == 0) { return; } boolean includedTagFound = false; boolean configTagFound = false; // find opening element SaxEvent first = saxEventList.get(0); if (first != null) { String elemName = getEventName(first); includedTagFound = INCLUDED_TAG.equalsIgnoreCase(elemName); configTagFound = CONFIG_TAG.equalsIgnoreCase(elemName); } // if opening element found, remove it, and then remove the closing element if (includedTagFound || configTagFound) { saxEventList.remove(0); final int listSize = saxEventList.size(); if (listSize == 0) { return; } final int lastIndex = listSize - 1; SaxEvent last = saxEventList.get(lastIndex); if (last != null) { String elemName = getEventName(last); if ((includedTagFound && INCLUDED_TAG.equalsIgnoreCase(elemName)) || (configTagFound && CONFIG_TAG.equalsIgnoreCase(elemName))) { saxEventList.remove(lastIndex); } } } } /** * Gets the event name of a {@code SaxEvent} * @param event SaxEvent to evaluate * @return {@code event.qName} is if it's not empty; otherwise, {@code event.localName} */ private String getEventName(SaxEvent event) { return event.qName.length() > 0 ? event.qName : event.localName; } }