    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.bootstrap;
    020
    021import org.apache.logging.log4j.LogManager;
    022import org.apache.logging.log4j.Logger;
    023import org.apache.logging.log4j.core.LoggerContext;
    024import org.apache.logging.log4j.core.config.Configuration;
    025import org.apache.logging.log4j.core.config.ConfigurationSource;
    026import org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory;
    027import org.apache.wiki.api.spi.Wiki;
    028import org.apache.wiki.util.TextUtil;
    029
    030import javax.servlet.ServletContextEvent;
    031import javax.servlet.ServletContextListener;
    032import java.io.ByteArrayInputStream;
    033import java.io.ByteArrayOutputStream;
    034import java.io.IOException;
    035import java.io.InputStream;
    036import java.util.Properties;
    037
    038
    039public class WikiBootstrapServletContextListener implements ServletContextListener {
    040
    041    private static final Logger LOG = LogManager.getLogger( WikiBootstrapServletContextListener.class );
    042    private static final String[] LOG4J_CONF = new String[] { "appender", "logger", "rootLogger", "filter", "status", "dest", "name", "properties", "property", "log4j2" };
    043
    044    /** {@inheritDoc} */
    045    @Override
    046    public void contextInitialized( final ServletContextEvent sce ) {
    047        final Properties properties = initWikiSPIs( sce );
    048        initWikiLoggingFramework( properties );
    049    }
    050
    051    /**
    052     * Locate and init JSPWiki SPIs' implementations
    053     *
    054     * @param sce associated servlet context.
    055     * @return JSPWiki configuration properties.
    056     */
    057    Properties initWikiSPIs( final ServletContextEvent sce ) {
    058        return Wiki.init( sce.getServletContext() );
    059    }
    060
    061    /**
    062     * Initialize the logging framework(s). By default, we try to load the log config statements from jspwiki.properties,
    063     * unless the property jspwiki.use.external.logconfig=true, in that case we let the logging framework figure out the
    064     * logging configuration.
    065     *
    066     * @param properties JSPWiki configuration properties.
    067     * @return {@code true} if configuration was read from jspwiki.properties, {@code false} otherwise.
    068     */
    069    boolean initWikiLoggingFramework( final Properties properties ) {
    070        final String useExternalLogConfig = TextUtil.getStringProperty( properties, "jspwiki.use.external.logconfig", "false" );
    071        if ( useExternalLogConfig.equals( "false" ) ) {
    072            final ConfigurationSource source = createConfigurationSource( properties );
    073            if( source != null ) {
    074                final PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory();
    075                final LoggerContext ctx = ( LoggerContext ) LogManager.getContext( this.getClass().getClassLoader(), false );
    076                final Configuration conf = factory.getConfiguration( ctx, source );
    077                conf.initialize();
    078                ctx.setConfiguration( conf );
    079                LOG.info( "Log configuration reloaded from Wiki properties" );
    080            }
    081        }
    082        return useExternalLogConfig.equals( "false" );
    083    }
    084
    085    ConfigurationSource createConfigurationSource( final Properties properties ) {
    086        final ByteArrayOutputStream out = new ByteArrayOutputStream();
    087        try {
    088            final Properties log4JProperties = new Properties();
    089            properties.forEach( ( k, v ) -> {
    090                for( final String log4JNsProp : LOG4J_CONF ) {
    091                    if( k.toString().startsWith( log4JNsProp ) ) {
    092                        log4JProperties.put( k, v );
    093                    }
    094                }
    095            } );
    096            log4JProperties.store( out, null );
    097            final InputStream in = new ByteArrayInputStream( out.toByteArray() );
    098            return new ConfigurationSource( in );
    099        } catch( final IOException ioe ) {
    100            LOG.error( "Unable to load the properties file into Log4j2, default Log4J2 configuration will be applied.", ioe );
    101            return null;
    102        }
    103    }
    104
    105    /** {@inheritDoc} */
    106    @Override
    107    public void contextDestroyed( final ServletContextEvent sce ) {
    108    }
    109}