/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.commons.log.logback.internal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.action.ActionConst; import ch.qos.logback.core.joran.event.EndEvent; import ch.qos.logback.core.joran.event.InPlayListener; import ch.qos.logback.core.joran.event.SaxEvent; import ch.qos.logback.core.joran.event.SaxEventRecorder; import ch.qos.logback.core.joran.spi.ActionException; import ch.qos.logback.core.joran.spi.InterpretationContext; import ch.qos.logback.core.joran.spi.JoranException; import org.apache.sling.commons.log.logback.internal.util.Util; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import static org.apache.sling.commons.log.logback.internal.ConfigSourceTracker.ConfigSourceInfo; /** * Joran action enabling integration between OSGi and Logback. It is based on * {@link ch.qos.logback.core.joran.action.IncludeAction}. It supports including * config fragments provided through OSGi ServiceRegistry */ public class OsgiInternalAction extends Action { private static final String INCLUDED_TAG = "included"; @SuppressWarnings("unchecked") @Override public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { ec.addInPlayListener(new ConfigCompleteListener(ec)); populateSubstitutionProperties(ec); // TO CHECK Should we add the config fragment at end final Collection<ConfigSourceInfo> providers = getFragmentProviders(); List<SaxEvent> consolidatedEventList = new ArrayList<SaxEvent>(); for (ConfigSourceInfo cp : providers) { InputSource is = cp.getConfigProvider().getConfigSource(); try { SaxEventRecorder recorder = new SaxEventRecorder(context); recorder.recordEvents(is); // remove the <included> tag from the beginning and </included> // from the end trimHeadAndTail(recorder); consolidatedEventList.addAll(recorder.getSaxEventList()); } catch (JoranException e) { addError("Error while parsing xml obtained from [" + cp + "]", e); } finally { Util.close(is); } } // offset = 2, because we need to get past this element as well as the // end element ec.getJoranInterpreter().getEventPlayer().addEventsDynamically(consolidatedEventList, 2); } private void populateSubstitutionProperties(InterpretationContext ec) { getLogbackManager().addSubsitutionProperties(ec); } @Override public void end(InterpretationContext ec, String name) throws ActionException { // do nothing } private Collection<ConfigSourceInfo> getFragmentProviders() { ConfigSourceTracker tracker = (ConfigSourceTracker) getContext().getObject(ConfigSourceTracker.class.getName()); if (tracker != null) { return tracker.getSources(); } return Collections.emptyList(); } private LogbackManager getLogbackManager() { LogbackManager lm = (LogbackManager) getContext().getObject(LogbackManager.class.getName()); if (lm == null) { throw new IllegalStateException("LogbackManager not found in Context map"); } return lm; } private static void trimHeadAndTail(SaxEventRecorder recorder) { // Let's remove the two <included> events before // adding the events to the player. List<SaxEvent> saxEventList = recorder.saxEventList; if (saxEventList.size() == 0) { return; } SaxEvent first = saxEventList.get(0); if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) { saxEventList.remove(0); } SaxEvent last = saxEventList.get(recorder.saxEventList.size() - 1); if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) { saxEventList.remove(recorder.saxEventList.size() - 1); } } /** * Logback does not provide any standard hook point through which we can be * notified of config complete. Hence as a work around we listen for EndEvent for * 'configuration' tag and then fire the listeners * * Also Logback does not expose the configured appenders map which it maintains * in InterpretationContext. ConfigCompleteListener would extract that map * and would export it to LoggerContext Object map so that any * listener can make use of that */ private class ConfigCompleteListener implements InPlayListener { private static final String CONFIG_TAG = "configuration"; private final String[] OBJECT_NAMES = { ActionConst.APPENDER_BAG, OsgiAppenderRefInternalAction.OSGI_APPENDER_REF_BAG, }; private final InterpretationContext ic; public ConfigCompleteListener(InterpretationContext ec) { this.ic = ec; } @Override public void inPlay(SaxEvent event) { if(event instanceof EndEvent && event.qName.equalsIgnoreCase(CONFIG_TAG)){ //Export the appender bag to LoggerContext object transferObjectsToContext(); getLogbackManager().fireResetCompleteListeners(); //Clear the appender bag entry removeTransferredObjects(); } } private void transferObjectsToContext(){ for(String name : OBJECT_NAMES){ getContext().putObject(name,ic.getObjectMap().get(name)); } } private void removeTransferredObjects(){ for(String name : OBJECT_NAMES){ getContext().putObject(name,null); } } } }