1  /*
     2   * Copyright the original author or authors.
     3   * 
     4   * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   * 
     8   *      http://www.mozilla.org/MPL/MPL-1.1.html
     9   * 
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  import org.as2lib.core.BasicClass;
    18  import org.as2lib.env.reflect.ReflectUtil;
    19  import org.as2lib.env.log.Logger;
    20  import org.as2lib.env.log.LoggerRepository;
    21  
    22  /**
    23   * {@code LogManager} is the core access point of the As2lib Logging API.
    24   * 
    25   * <p>You use it to set the underlying repository that stores and releases loggers
    26   * and to obtain a logger according to a logger's name of the repository.
    27   * 
    28   * <p>The repository must be set before anything else when you are using this
    29   * class as access point to obtain loggers. There is no default repository. This
    30   * means that all messages sent to loggers obtained from the {@link #getLogger} method,
    31   * before the repository has been set will not be logged.
    32   * 
    33   * <p>This class could be used as follows with a non-singleton repository. Note
    34   * that you can of course also use any other kind of logger repository.
    35   *
    36   * <code>
    37   *   // configuration: when setting everything up
    38   *   var loggerHierarchy:LoggerHierarchy = new LoggerHierarchy();
    39   *   var traceLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.mydomain");
    40   *   traceLogger.addHandler(new TraceHandler());
    41   *   loggerHierarchy.addLogger(traceLogger);
    42   *   LogManager.setLoggerRepository(loggerHierarchy);
    43   *   // usage: in the class org.mydomain.MyClass
    44   *   var myLogger:Logger = LogManager.getLogger("org.mydomain.MyClass");
    45   *   if (myLogger.isInfoEnabled()) {
    46   *       myLogger.info("This is an informative log message.");
    47   *   }
    48   * </code>
    49   * 
    50   * <p>If you have one logger that shall always be returned you can use the
    51   * convenience method {@link #setLogger} that does all the work with the repository
    52   * for you.
    53   * 
    54   * <code>
    55   *   // configuration: when setting everything up
    56   *   var traceLogger:SimpleLogger = new SimpleLogger();
    57   *   traceLogger.addHandler(new TraceHandler());
    58   *   LogManager.setLogger(traceLogger);
    59   *   // usage: in the class org.mydomain.MyClass
    60   *   var myLogger:Logger = LogManager.getLogger("org.mydomain.MyClass");
    61   *   if (myLogger.isInfoEnabled()) {
    62   *       myLogger.info("This is an informative log message.");
    63   *   }
    64   * </code>
    65   * 
    66   * <p>It is common practice to obtain loggers per class. You may thus consider the
    67   * following strategy of obtaining loggers:
    68   * <code>private static var logger:Logger = LogManager.getLogger("org.as2lib.MyClass");</code>
    69   * 
    70   * <p>Applying this strategy you have a logger per class that can be used within
    71   * per class and per instance methods of the logging class.
    72   *
    73   * @author Simon Wacker
    74   */
    75  class org.as2lib.env.log.LogManager extends BasicClass {
    76  	
    77  	/** Repository that stores already retrieved loggers. */
    78  	private static var repository:LoggerRepository;
    79  	
    80  	/** Proxies of loggers that are replaced by real loggers as soon as repository gets set. */
    81  	private static var loggerProxies:Array;
    82  	
    83  	/**
    84  	 * @overload #getLoggerByName
    85  	 * @overload #getLoggerByObject
    86  	 */
    87  	public static function getLogger():Logger {
    88  		// do not use Overloading API hear because 'LogManager' must be as light-weight as possible
    89  		if (arguments[0].__proto__ != String.prototype) {
    90  			return getLoggerByObject(arguments[0]);
    91  		}
    92  		return getLoggerByName(arguments[0]);
    93  	}
    94  	
    95  	/**
    96  	 * Returns the logger according to the passed-in {@code object}.
    97  	 * 
    98  	 * <p>If {@code object} is of type 'function' it is supposed that this is the type
    99  	 * to get the name of otherwise it is supposed to be the instance of the type to get
   100  	 * the name of.
   101  	 * 
   102  	 * <p>The name of the type is used as logger name.
   103  	 * 
   104  	 * <p>Note that evaluating the name is rather slow. It is thus recommended to
   105  	 * hardcode the name and use the {@link #getLoggerByName} method.
   106  	 * 
   107  	 * @param object the object to return the type name of
   108  	 * @return the logger for the given {@code object}
   109  	 * @see #getLoggerByName
   110  	 */
   111  	public static function getLoggerByObject(object):Logger {
   112  		return getLoggerByName(ReflectUtil.getTypeName(object));
   113  	}
   114  	
   115  	/**
   116  	 * Returns the logger according the passed-in {@code loggerName}.
   117  	 * 
   118  	 * <p>Uses the set logger repository to receive the logger that is returned.
   119  	 * 
   120  	 * <p>{@code null} is only returned if the logger repository is initialized and
   121  	 * returns {@code null} or {@code undefined}.
   122  	 * 
   123  	 * <p>If the logger repository has not been initialized yet a proxy gets returned
   124  	 * that is replaced by the actual logger of the repository, as soon as the
   125  	 * repository gets initialized. This means that the following access in classes is
   126  	 * possible:
   127  	 * <code>private static var logger:Logger = LogManager.getLogger("org.as2lib.MyClass");</code>
   128  	 * 
   129  	 * <p>But note that you shall not log messages before the actual initialization of
   130  	 * the repository; these messages will never be logged. Proxies are just returne to
   131  	 * enable the convenient logger access above.
   132  	 * 
   133  	 * @param loggerName the name of the logger to return
   134  	 * @return the logger according to the passed-in {@code name}
   135  	 */
   136  	public static function getLoggerByName(loggerName:String):Logger {
   137  		if (!repository) {
   138  			if (loggerProxies[loggerName]) return loggerProxies[loggerName];
   139  			if (!loggerProxies) loggerProxies = new Array();
   140  			var result:Logger = getBlankLogger();
   141  			result["__resolve"] = function() {
   142  				return false;
   143  			};
   144  			result["name"] = loggerName;
   145  			loggerProxies.push(result);
   146  			loggerProxies[loggerName] = result;
   147  			return result;
   148  		}
   149  		var result:Logger = repository.getLogger(loggerName);
   150  		if (result) return result;
   151  		return null;
   152  	}
   153  	
   154  	/**
   155  	 * Returns a blank logger.
   156  	 *
   157  	 * <p>This is a {@code Logger} instance with no implemented methods.
   158  	 *
   159  	 * @return a blank logger
   160  	 */
   161  	private static function getBlankLogger(Void):Logger {
   162  		var result = new Object();
   163  		result.__proto__ = Logger["prototype"];
   164  		result.__constructor__ = Logger;
   165  		return result;
   166  	}
   167  	
   168  	/**
   169  	 * Sets the {@code logger} that is returned on calls to the {@link #getLogger}
   170  	 * method.
   171  	 *
   172  	 * <p>This method actually sets a singleton repository via the static
   173  	 * {@link #setLoggerRepository} that always returns the passed-in {@code logger}
   174  	 * and ignores the name.
   175  	 *
   176  	 * <p>You could also set the repository by hand, this is just an easier way of
   177  	 * doing it if you always want the same logger to be returned.
   178  	 *
   179  	 * @param logger the logger to return on calls to the {@code #getLogger} method
   180  	 */
   181  	public static function setLogger(logger:Logger):Void {
   182  		repository = getBlankLoggerRepository();
   183  		repository.getLogger = function(loggerName:String):Logger {
   184  			return logger;
   185  		};
   186  	}
   187  	
   188  	/**
   189  	 * Returns a blank logger repository.
   190  	 *
   191  	 * <p>This is a {@code LoggerRepository} instance with no implemented methods.
   192  	 *
   193  	 * @return a blank logger repository
   194  	 */
   195  	private static function getBlankLoggerRepository(Void):LoggerRepository {
   196  		var result = new Object();
   197  		result.__proto__ = LoggerRepository["prototype"];
   198  		result.__constructor__ = LoggerRepository;
   199  		return result;
   200  	}
   201  	
   202  	/**
   203  	 * Reutrns the logger repository set via {@link #setLoggerRepository}.
   204  	 *
   205  	 * <p>There is no default logger repository, so you must set it before anything
   206  	 * else.
   207  	 *
   208  	 * @return the set logger repository
   209  	 */
   210  	public static function getLoggerRepository(Void):LoggerRepository {
   211  		return repository;
   212  	}
   213  	
   214  	/**
   215  	 * Sets a new repositroy returned by {@link #getLoggerRepository}.
   216  	 *
   217  	 * <p>The {@link #getLogger} method uses this repository to obtain the logger for
   218  	 * the given logger name.
   219  	 *
   220  	 * @param loggerRepository the new logger repository
   221  	 */
   222  	public static function setLoggerRepository(loggerRepository:LoggerRepository):Void {
   223  		repository = loggerRepository;
   224  		if (loggerProxies) {
   225  			for (var i:Number = loggerProxies.length - 1; i >= 0; i--) {
   226  				var proxy:Logger = loggerProxies[i];
   227  				var name:String = proxy["name"];
   228  				delete proxy["__constructor__"];
   229  				delete proxy["__resolve"];
   230  				delete proxy["name"];
   231  				loggerProxies.pop();
   232  				delete loggerProxies[name];
   233  				var logger:Logger = loggerRepository.getLogger(name);
   234  				proxy["__proto__"] = logger;
   235  			}
   236  		}
   237  	}
   238  	
   239  	/**
   240  	 * Returns whether a logger repository has been added.
   241  	 * 
   242  	 * @return {@code true} if a logger repository has been added else {@code false}
   243  	 */
   244  	public static function hasLoggerRepository(Void):Boolean {
   245  		return (repository != null);
   246  	}
   247  	
   248  	/**
   249  	 * Private constructor.
   250  	 */
   251  	private function LogManager(Void) {
   252  	}
   253  	
   254  }