blob: b6c178fcb18d4b3d106ad9fc1b91270c43127f27 [file] [log] [blame]
/*
* 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);
}
}
}
}