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}