package org.apache.velocity.runtime.log;
/*
* 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.
*/
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.velocity.exception.VelocityException;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.util.ClassUtils;
/**
* <p>
* This class is responsible for instantiating the correct LogChute
* </p>
*
* <p>
* The approach is :
* </p>
* <ul>
* <li>
* First try to see if the user is passing in a living object
* that is a LogChute, allowing the app to give its living
* custom loggers.
* </li>
* <li>
* Next, run through the (possible) list of classes specified
* specified as loggers, taking the first one that appears to
* work. This is how we support finding logkit, log4j or
* jdk logging, whichever is in the classpath and found first,
* as all three are listed as defaults.
* </li>
* <li>
* Finally, we turn to the System.err stream and print log messages
* to it if nothing else works.
* </li>
*
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:nbubna@apache.org">Nathan Bubna</a>
* @version $Id: LogManager.java 991708 2010-09-01 21:17:56Z nbubna $
*/
public class LogManager
{
// Creates a new logging system or returns an existing one
// specified by the application.
private static LogChute createLogChute(RuntimeServices rsvc) throws Exception
{
Log log = rsvc.getLog();
/* If a LogChute or LogSystem instance was set as a configuration
* value, use that. This is any class the user specifies.
*/
Object o = rsvc.getProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM);
if (o != null)
{
// first check for a LogChute
if (o instanceof LogChute)
{
try
{
((LogChute)o).init(rsvc);
return (LogChute)o;
}
catch (Exception e)
{
String msg = "Could not init runtime.log.logsystem " + o;
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
// then check for a LogSystem
else if (o instanceof LogSystem)
{
// inform the user about the deprecation
log.debug("LogSystem has been deprecated. Please use a LogChute implementation.");
try
{
// wrap the LogSystem into a chute.
LogChute chute = new LogChuteSystem((LogSystem)o);
chute.init(rsvc);
return chute;
}
catch (Exception e)
{
String msg = "Could not init runtime.log.logsystem " + o;
log.error(msg, e);
throw new VelocityException(msg, e);
}
}
else
{
String msg = o.getClass().getName() + " object set as runtime.log.logsystem is not a valid log implementation.";
log.error(msg);
throw new VelocityException(msg);
}
}
/* otherwise, see if a class was specified. You can put multiple
* classes, and we use the first one we find.
*
* Note that the default value of this property contains the
* AvalonLogChute, the Log4JLogChute, CommonsLogLogChute,
* ServletLogChute, and the JdkLogChute for
* convenience - so we use whichever we works first.
*/
List classes = new ArrayList();
Object obj = rsvc.getProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS );
/*
* we might have a list, or not - so check
*/
if ( obj instanceof List)
{
classes = (List) obj;
}
else if ( obj instanceof String)
{
classes.add( obj );
}
/*
* now run through the list, trying each. It's ok to
* fail with a class not found, as we do this to also
* search out a default simple file logger
*/
for( Iterator ii = classes.iterator(); ii.hasNext(); )
{
String claz = (String) ii.next();
if (claz != null && claz.length() > 0 )
{
log.debug("Trying to use logger class " + claz );
try
{
o = ClassUtils.getNewInstance( claz );
if (o instanceof LogChute)
{
((LogChute)o).init(rsvc);
log.debug("Using logger class " + claz);
return (LogChute)o;
}
else if (o instanceof LogSystem)
{
// inform the user about the deprecation
log.debug("LogSystem has been deprecated. Please use a LogChute implementation.");
LogChute chute = new LogChuteSystem((LogSystem)o);
chute.init(rsvc);
return chute;
}
else
{
String msg = "The specified logger class " + claz +
" does not implement the "+LogChute.class.getName()+" interface.";
log.error(msg);
// be extra informative if it appears to be a classloader issue
// this should match all our provided LogChutes
if (isProbablyProvidedLogChute(claz))
{
// if it's likely to be ours, tip them off about classloader stuff
log.error("This appears to be a ClassLoader issue. Check for multiple Velocity jars in your classpath.");
}
throw new VelocityException(msg);
}
}
catch(NoClassDefFoundError ncdfe)
{
// note these errors for anyone debugging the app
if (isProbablyProvidedLogChute(claz))
{
log.debug("Target log system for " + claz +
" is not available (" + ncdfe.toString() +
"). Falling back to next log system...");
}
else
{
log.debug("Couldn't find class " + claz +
" or necessary supporting classes in classpath.",
ncdfe);
}
}
catch(UnsupportedOperationException uoe)
{
// note these errors for anyone debugging the app
if (isProbablyProvidedLogChute(claz))
{
log.debug("Target log system for " + claz +
" is not supported (" + uoe.toString() +
"). Falling back to next log system...");
}
else
{
log.debug("Couldn't find necessary resources for "+claz, uoe);
}
}
catch(Exception e)
{
String msg = "Failed to initialize an instance of " + claz +
" with the current runtime configuration.";
// log unexpected init exception at higher priority
log.error(msg, e);
throw new VelocityException(msg,e);
}
}
}
/* If the above failed, that means either the user specified a
* logging class that we can't find, there weren't the necessary
* dependencies in the classpath for it, or there were the same
* problems for the default loggers, log4j and Java1.4+.
* Since we really don't know and we want to be sure the user knows
* that something went wrong with the logging, let's fall back to the
* surefire SystemLogChute. No panicking or failing to log!!
*/
LogChute slc = new SystemLogChute();
slc.init(rsvc);
log.debug("Using SystemLogChute.");
return slc;
}
/**
* Simply tells whether the specified classname probably is provided
* by Velocity or is implemented by someone else. Not surefire, but
* it'll probably always be right. In any case, this method shouldn't
* be relied upon for anything important.
*/
private static boolean isProbablyProvidedLogChute(String claz)
{
if (claz == null)
{
return false;
}
else
{
return (claz.startsWith("org.apache.velocity.runtime.log")
&& claz.endsWith("LogChute"));
}
}
/**
* Update the Log instance with the appropriate LogChute and other
* settings determined by the RuntimeServices.
* @param log
* @param rsvc
* @throws Exception
* @since 1.5
*/
public static void updateLog(Log log, RuntimeServices rsvc) throws Exception
{
// create a new LogChute using the RuntimeServices
LogChute newLogChute = createLogChute(rsvc);
LogChute oldLogChute = log.getLogChute();
// pass the new LogChute to the log first,
// (if the old was a HoldingLogChute, we don't want it
// to accrue new messages during the transfer below)
log.setLogChute(newLogChute);
// If the old LogChute was the pre-Init logger,
// dump its messages into the new system.
if (oldLogChute instanceof HoldingLogChute)
{
HoldingLogChute hlc = (HoldingLogChute)oldLogChute;
hlc.transferTo(newLogChute);
}
}
}