Package com.sun.sgs.impl.kernel.logging

Source Code of com.sun.sgs.impl.kernel.logging.TransactionAwareLogManager$SimpleLogger

/*
* Copyright 2007-2009 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.sun.sgs.impl.kernel.logging;

import com.sun.sgs.impl.kernel.StandardProperties;

import com.sun.sgs.service.TransactionProxy;

import java.util.ArrayDeque;
import java.util.Properties;
import java.util.Queue;

import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.LogManager;

/**
* A {@code LogManager} class that adds transactional semantics to {@code
* Logger} instances in the application's namespace.  This class will either
* infer the application's name space from the package used by the main {@code
* AppListener}, or it can be specified manually by the following property:
*
* <p><dl style="margin-left: 1em">
*
* <dt> <i>Property:</i> <b>
*  {@code com.sun.sgs.logging.app.namespace}
</b><br>
<i>Default:</i> the package named used in the {@code
*  com.sun.sgs.app.listener} property.
*
* <dd style="padding-top: .5em">This property specifies the root of the
* application namespace.  All loggers under this root will be have
* transactional-semantics.  If this property is left unset the system will use
* the namespace specified in the {@code com.sun.sgs.app.listener} property
* value. <p></dd></dl>
*
* In order to load this class as the default {@code LogManager}, applications
* must set the {@code java.util.logging.manager} system property to {@code
* com.sun.sgs.impl.kernel.logging.TransactionAwareLogManager} prior to JVM
* start up.
*
* <p>
*
* All {@code Logger} instances outside of the application's name space will
* retain their default, non-transactional semantics.
*
* @see TransactionalHandler
*/
public final class TransactionAwareLogManager extends LogManager {

    /**
     * The namespace prefix used by all the properties for this class.
     */
    private static final String PROPERTIES_PREFIX = "com.sun.sgs.logging";

    /**
     * The property specified in the system properties for denoting the root
     * namespace of the application.
     */
    private static final String APP_NAMESPACE_PROPERTY =
  PROPERTIES_PREFIX + ".app.namespace";

    /**
     * The {@code TransactionProxy} used by the {@link TransactionalHandler}
     * handlers.
     */
    private TransactionProxy txnProxy;

    /**
     * The listing of {@code TransactionalLogger} instances that have yet to be
     * configured with a {@link TransactionProxy}, but were created prior to
     * this {@code TransactionAwareLogManager} being configured.  Note that not
     * all of these instances will end up being transactional.  However, due to
     * the order in which the {@code LogManager} is created in the JVM, some
     * {@code Logger} instances will be created prior to this component being
     * configured.  For this reason, we keep this list to later reconfigure
     * them when the {@code TransactionProxy} is available.
     */
    private final Queue<TransactionalLogger> unconfiguredLoggers;

    /**
     * The namespace of the application, which is used to determine which
     * {@code Logger} instances should be transactional.  Any namespace under
     * this will have transactional semantics.
     */
    private String appNamespace;

    /**
     * Default constructor used by the JVM at startup.
     */
    public TransactionAwareLogManager() {
  super();
  unconfiguredLoggers = new ArrayDeque<TransactionalLogger>();
  appNamespace = null;
    }

    /**
     * Configures this {@code LogManager} with the provided properties and uses
     * the {@code TransactionProxy} to add transactional semantics to any of
     * the specified application {@code Logger} instances.
     *
     * <p>
     *
     * Note that prior to this call, the application's namespace is not known
     * so any {@code Logger} instances will be of type {@code
     * TransactionalLogger}, but will be configured as non-transactional.
     * After this call, any of these instances that were in the application's
     * namespace will be transactional.
     *
     * @param properties the properties for configuring this component
     * @param txnProxy the transaction proxy    
     */
    public synchronized void configure(Properties properties,
               TransactionProxy txnProxy) {
  this.txnProxy = txnProxy;

  String appListener =
      properties.getProperty(StandardProperties.APP_LISTENER);

  int lastDotBeforeClass = appListener.lastIndexOf(".");

  // Check to ensure that the main class has a package
  if (lastDotBeforeClass < 0) {
      // in the event that the main class has no package, set the
      // position of the now phantom dot to the length of the app
      // Listener's name, which ensures that any loggers created based on
      // the class's name will be transactional
      lastDotBeforeClass = appListener.length();
  }

  String defaultAppNamespace =
      appListener.substring(0, lastDotBeforeClass);

  // if the applicate does not specify a specific namespace, we use the
  // namespace provided by the application listener.
  appNamespace = properties.getProperty(APP_NAMESPACE_PROPERTY,
                defaultAppNamespace);

  // Now that the namespace is known, check any non-transaction
  // TransactionalLoggers that were created prior to this LogManager
  // being configured.  This could have happened if the Loggers were
  // created statically when the application's classes were loaded.  If
  // any of these Loggers are in the application's namespace, have them
  // configured to be transactional.
  for (TransactionalLogger lgr : unconfiguredLoggers) {
     
      // This list will likely include any of the servers loggers that
      // were created statically, so we test to see whether the Logger
      // belongs to the app's namespace before configuring its handlers
      if (lgr.getName().startsWith(appNamespace)) {
    lgr.configure(txnProxy);
    lgr.config("This logger now has transactional semantics");
      }
  }
  unconfiguredLoggers.clear()
    }

    /**
     * Returns the existing {@code Logger} for the provided name or creates a
     * new instance if none is found.  Note that <i>unlike the default
     * <tt>LogManager</tt> implementation</i>, this call will create a new
     * {@code Logger} instance.
     *
     * @param name the name of the logger
     *
     * @return the existing {@code Logger} for the provided name, or a new
     *         instance that was created by this call.
     */
    // NOTE: we rely on the fact that Logger.getLogger() will in turn call
    // LogManager.demandLogger().  LogManager.demandLogger() first checks to
    // see if a Logger has already been created by calling
    // LogManager.getLogger() and checking the results.  In order to subvert
    // the default LogManager behavior, we override that method to create a
    // new, possibly transaction-aware Logger instance.

    public synchronized Logger getLogger(String name) {
  // The root logger will have a 0-length name, in which case we should
  // return null and let the default LogManager code create the
  // RootLogger instance correctly.
  if (name == null || name.length() == 0) {
      return null;
        }
 
  Logger result = super.getLogger(name);

  if (result == null) {     
            boolean configured = (txnProxy != null);
      // if no current Logger associated with that name, create the
      // appropriate type of Logger.  If the transactionProxy is null, we
      // haven't been configured yet, so create a TransactionalLogger but
      // mark it non-transactional.
      //
      // If we have been configured, see if the requested Logger's name
            // is in the application's namespace.
      result = (!configured || name.startsWith(appNamespace))
    ? new TransactionalLogger(name, null, txnProxy)
    : new SimpleLogger(name, null);

      // there is a chance that an application may demand a Logger prior
      // to the TxnAwareLogManager being configured with the
      // TransactionProxy and the application's namespace.  Therefore, we
      // add any such Loggers to a list of unconfigured ones and revisit
      // them upon the manager's configuration.
      if (!configured && (result instanceof TransactionalLogger)) {
                unconfiguredLoggers.add((TransactionalLogger) result);
            } else {
    result.config("This logger now has transactional semantics");
      }

      // this call is necessary to install all the handlers associated
      // with the Logger.
      addLogger(result);
  }
  return result;
    }

    /**
     * A utility class that exposes the {@code protected} constructor of the
     * {@code Logger} class.  We need this class so that we can create new
     * {@code Logger} instances in the {@link
     * TransactionAwareLogManager#getLogger(String)} method.
     */
    private static final class SimpleLogger extends Logger {

  public SimpleLogger(String name, String resourceBundleName) {
      super(name, resourceBundleName);
  }
    }

    /**
     * A {@code Logger} class that provides optional transactional semantics.
     * Instances of this class will provide transaction semantics if they are
     * constructed with a valid, non-{@code null} instance of a {@link
     * TransactionProxy}, or if they are configured after construction with a
     * valid, non-{@code null} instance of a {@code TransactionProxy}.
     *
     * <p>
     *
     * This class does not interact with the transactions directly but instead
     * relies on {@link TransactionalHandler} instances to do so.  Adding a
     * {@link Handler} at run-time will function as expected and will not
     * change the output semantics.
     */
    private static final class TransactionalLogger extends Logger {

  /**
   * The proxy that allows any {@link TransactionalHandler} associated
   * with this {@code Logger} to join the current transaction.  Note that
   * if this {@code Logger} was created before the {@link
   * TransactionAwareLogManager#configure(Properties,TransactionProxy}}
   * has been called, this will be {@code null}.
   */
  private TransactionProxy txnProxy;

  /**
   * Constructs a {@code TransactionalLogger} that will have
   * transactional semantics if {@code txnProxy} is valid and non-{@code
   * null}.
   *
   * @param name the name of this logger
   * @param resourceBundleName the name of a {@link ResourceBundle} to be
   *                           used for localizing messages for this
   *                           logger.  May be {@code null} if none of
   *                           the messages require localization.
   * @param txnProxy the {@code TransactionProxy} used to join the
   *                 current transaction when a report is logged.
   */
  public TransactionalLogger(String name,
           String resourceBundleName,
           TransactionProxy txnProxy) {
      super(name, resourceBundleName);
      this.txnProxy = txnProxy;
  }
             
  /**
   * {@inheritDoc}
   *
   * If the provided {@code Handler} is an instance of {@link
   * TransactionalHander} it will be added immediately.  Otherwise, the
   * provided {@code Handler} will be wrapped by a {@code
   * TransactionalHandler} and that handler will be added instead.
   */
  public void addHandler(Handler handler) {
      if (!(handler instanceof TransactionalHandler)) {
    // in the event that this Logger was created prior to the
    // LogManager being configured, the TransactionProxy will be
    // null.  In this case, we add the handler as normal and then
    // wait for the configure() call to wrap it in a
    // TransactionalHandler
    if (txnProxy == null) {
        super.addHandler(handler);
                } else {
        // wrap the original handler in one that has transactional
        // semantics
        super.addHandler(new TransactionalHandler(txnProxy,
                    handler));
    }
            } else {
    // if we were passed an existing TransactionalHandler, use it
    // as is.  This case could occur if the handler had already
    // been created for another Logger.
    super.addHandler(handler);
      }
  }


  /**
   * Searches the logger parent hierarchy of the provided {@code Logger}
   * until a parent is found who has {@code Handler} instances, and then
   * adds those handlers to the provided logger.  Note that this method
   * also modifies the logger to not use its parent handlers in order to
   * prevent duplicate log entries.
   */
  private void attachParentHandlers() {
      Logger parent = this;
      do {
    parent = parent.getParent();
      } while (parent != null && parent.getHandlers().length == 0);
     
      if (parent == null) {
    // NOTE: this case should never happen since we would
    // eventually hit the LogManager$RootLogger which by default
    // has a Handler.  However a developer could feasibly adjust
    // some settings so that the RootLogger had no handler and none
    // of the child Loggers did as well.
    return;
      }
     
      // Add all of the parent handlers to this Logger so that we can
      // later wrap it.
      Handler[] arr = parent.getHandlers();
      for (Handler h : arr) {
    addHandler(h);
      }
     
      // Now that we are using the same handler as the parent, avoid
      // propagating the call to the parent Logger as this will result in
      // duplicate log entries to the handler.
      setUseParentHandlers(false);
  }   

  /**
   * Configures this {@code Logger} with the provided {@code
   * TransactionProxy} so that all messages logged will have transaction
   * semantics.  This method is used for reconfiguring loggers that were
   * created prior to the {@link TransactionAwareLogManager} being
   * configured.
   *
   * @param txnProxy the {@code TransactionProxy} used to join the
   *                 current transaction when a report is logged.
   *
   * @see TransactionAwareLogManager#configure(Properties,
   *                                           TransactionProxy);
   */
  // NOTE: This method does not have a race condition with addHandler due
  // the separate call chain having already acquired lock a on the
  // TxnAwareLogManager.  Therefore neither methods of this class require
  // locks
  void configure(TransactionProxy txnProxy) {
       if (txnProxy == null) {
     return;
            }
     
      this.txnProxy = txnProxy;
     
      // In the event that no handlers have been specified for this
      // logger, we walk the Logger hierarchy until we find a parent that
      // does have a handler set.  The attachParentHandlers will bind
      // those handlers to the current Logger, which results in them
      // being wrapped by a TransactionalHandler
      if (getHandlers().length == 0) {
    attachParentHandlers();
      } else {
                // If handlers have been assigned to this Logger, wrap them in
                // TransactionalHandlers
               
    for (Handler h : getHandlers()) {
        // check that we aren't already dealing with a handler that
        // has already been made transactional.
        if (h instanceof TransactionalHandler) {
      continue;
                    }
        super.addHandler(new TransactionalHandler(txnProxy, h));
        removeHandler(h);
        // ensure that any log calls to this logger don't work
        // their way up the logger hierarchy, which could result in
        // non-transactional logging
        setUseParentHandlers(false);
    }
      }
     
      // lastly, note in the logging stream that this Logger is now
      // transactional.
      config("This logger now has transactional semantics");
  }

  /**
   * Sets the parent of this {@code Logger} and calls {@link
   * #attachParentHandlers()} if this logger has been configured to be
   * transactional and does not yet have any handlers.
   *
   * @param {@inheritDoc}
   */
  public void setParent(Logger parent) {
      super.setParent(parent);
      if (txnProxy != null && getHandlers().length == 0) {
    attachParentHandlers();
      }
  }


  /**
   * Returns the type and name of this {@code Logger} if this instance has
   * transaction semantics, or otherwise returns the default {@link
   * Logger#toString()}.
   *
   * @return a description of this logger
   */
  public String toString() {
      return (txnProxy == null)
    ? "Non-transactional Logger:" + getName()
    : "TransactionalLogger:" + getName();
  }

    }

}
TOP

Related Classes of com.sun.sgs.impl.kernel.logging.TransactionAwareLogManager$SimpleLogger

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.