/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ package org.beanfabrics.log; import java.lang.reflect.Constructor; import java.lang.reflect.UndeclaredThrowableException; import java.util.HashMap; import java.util.Map; /** * The {@link LoggerFactory} is used for creating (or accessing cached) * {@link Logger} objects. Which logger implementation is used depends on * configuration. At first request the <code>LoggerFactory</code> <b>chooses</b> * the <b>concrete constructor</b> by <b>evaluating the following rules</b> and * reuses it for every subsequent request. * <ol> * <li>The <code>LoggerFactory</code> checks if the system property * "org.beanfabrics.log.LoggerFactory.loggerclass" for the name of a valid class * with a valid constructor. The constructor is valid if it is public, returns * an instance of {@link Logger}, and requires a single parameter of type * {@link Class}. This parameter is used as the logging category.</li> * <li>The <code>LoggerFactory</code> checks if <a * href="http://www.slf4j.org">slf4j</a> is in the classpath. If that is the * case, the factory uses the constructor of {@link Slf4jLogger}.</li> * <li>If none of the above is true, the <code>LoggerFactory</code> falls back * to the default {@link Jdk14Logger}, that delegates to * {@link java.util.logging.Logger}.</li> * </ol> * <p> * Usually Beanfabrics classes store their logger reference in a static field, * hence these logger instances are created when the calling class is loaded by * the classloader. * * @see <a * href="http://www.beanfabrics.org/index.php/Logging_Example">Logging Example</a> * @author Michael Karneim * @author Max Gensthaler */ public class LoggerFactory { /** The key of the system property to set a custom {@link Logger}. */ public static final String SYSPROPKEY_LOGGER = LoggerFactory.class.getName() + ".loggerclass"; @SuppressWarnings("unchecked") private static final Map<Class, Logger> map = new HashMap<Class, Logger>(); private static ReflectiveFactoryHolder internalFactoryHolder = new ReflectiveFactoryHolder(); /** * Use this method to access <code>internalFactory</code> so that it is * constructed at first access not at construction time of * <code>LoggerFactory</code>. * * @return the <code>internalFactory</code> */ private static ReflectiveFactory getInternalFactory() { return internalFactoryHolder.getInternalFactory(); } /** * Returns a the {@link Logger} instance for a certain {@link Class}. * * @param clazz the <code>Class</code> the <code>Logger</code> is for * @return the <code>Logger</code> instance */ @SuppressWarnings("unchecked") public static Logger getLogger(Class clazz) { Logger result = map.get(clazz); if (result == null) { result = getInternalFactory().createLogger(clazz); map.put(clazz, result); } return result; } /** * Set the {@link Class} implementing the {@link Logger}. This class will be * used to create new instances by {@link #getLogger(Class)}. * * @param clazz the <code>Class</code> implementing the {@link Logger} * @throws IllegalArgumentException if <code>clazz</code> is * <code>null</code>, does not implement {@link Logger} or has * no constructor with the argument "{@link Class} * <code>clazz</code>" */ @SuppressWarnings("unchecked") public static void setLoggerClass(Class clazz) throws IllegalArgumentException { if (clazz == null) { throw new IllegalArgumentException("class must not be null"); } if (Logger.class.isAssignableFrom(clazz) == false) { // clazz does not implement Logger throw new IllegalArgumentException("Can only use loggers that implement " + Logger.class.getName()); } try { Constructor constr = clazz.getConstructor(new Class[] { Class.class }); getInternalFactory().setConstr(constr); map.clear(); // Newly build all following Logger instances } catch (Exception e) { throw new IllegalArgumentException("The constructor \"public " + clazz.getName() + "(Class clazz)\" doesn't exist. Please implement it."); } } /** * Using the holder pattern to lazy create the {@link ReflectiveFactory} on * first access to the holder. */ private static class ReflectiveFactoryHolder { private ReflectiveFactory internalFactory = new ReflectiveFactory(); public ReflectiveFactory getInternalFactory() { return internalFactory; } } private static class ReflectiveFactory { private Constructor<Logger> constr; @SuppressWarnings("unchecked") public ReflectiveFactory() { try { Class clazz; String loggerclassSysProp = System.getProperty(SYSPROPKEY_LOGGER); if (loggerclassSysProp != null) { clazz = Class.forName(loggerclassSysProp); } else { try { // try if slf4j is available Class.forName("org.slf4j.Logger"); clazz = Class.forName("org.beanfabrics.log.Slf4jLogger"); } catch (ClassNotFoundException e) { // else use the java logging framework clazz = Class.forName("org.beanfabrics.log.Jdk14Logger"); } } constr = clazz.getConstructor(new Class[] { Class.class }); } catch (ClassCastException e) { e.printStackTrace(System.err); } catch (Exception e) { // Logging not available } } public void setConstr(Constructor<Logger> constr) { this.constr = constr; } @SuppressWarnings("unchecked") public Logger createLogger(Class clazz) { try { if (constr == null) { return new NopLogger(); } else { return constr.newInstance(new Object[] { clazz }); } } catch (Exception e) { throw new UndeclaredThrowableException(e, "Can't create an instance of the logger"); } } } }