content: /**
   * 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.
   */
  package org.apache.activemq.transport;
  
  import java.io.IOException;
  
  import javax.management.ObjectName;
  
  import org.apache.activemq.broker.jmx.AnnotatedMBean;
  import org.apache.activemq.broker.jmx.ManagementContext;
  import org.apache.activemq.util.IOExceptionSupport;
  import org.apache.activemq.util.LogWriterFinder;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import static org.apache.activemq.TransportLoggerSupport.defaultJmxPort;
  
  /**
   * Singleton class to create TransportLogger objects.
   * When the method getInstance() is called for the first time,
   * a TransportLoggerControlMBean is created and registered.
   * This MBean permits enabling and disabling the logging for
   * all TransportLogger objects at once.
   *
   * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com
   *
   * @see TransportLoggerControlMBean
   */
  public class TransportLoggerFactory {
  
      private static final Logger LOG = LoggerFactory.getLogger(TransportLoggerFactory.class);
  
      private static TransportLoggerFactory instance;
      private static int lastId=0;
      private static final LogWriterFinder logWriterFinder = new LogWriterFinder("META-INF/services/org/apache/activemq/transport/logwriters/");
  
      /**
       * LogWriter that will be used if none is specified.
       */
      public static String defaultLogWriterName = "default";
      /**
       * If transport logging is enabled, it will be possible to control
       * the transport loggers or not based on this value
       */
      private static boolean defaultDynamicManagement = false;
      /**
       * If transport logging is enabled, the transport loggers will initially
       * output or not depending on this value.
       * This setting only has a meaning if
       */
      private static boolean defaultInitialBehavior = true;
  
      private boolean transportLoggerControlCreated = false;
      private ManagementContext managementContext;
      private ObjectName objectName;
  
      /**
       * Private constructor.
       */
      private TransportLoggerFactory() {
      }
  
      /**
       * Returns a TransportLoggerFactory object which can be used to create TransportLogger objects.
       * @return a TransportLoggerFactory object
       */
      public static synchronized TransportLoggerFactory getInstance() {
          if (instance == null) {
              instance = new TransportLoggerFactory();
          }
          return instance;
      }
  
      public void stop() {
          try {
              if (this.transportLoggerControlCreated) {
                  this.managementContext.unregisterMBean(this.objectName);
                  this.managementContext.stop();
                  this.managementContext = null;
              }
          } catch (Exception e) {
              LOG.error("TransportLoggerFactory could not be stopped, reason: " + e, e);
          }
  
      }
  
      /**
       * Creates a TransportLogger object, that will be inserted in the Transport Stack.
       * Uses the default initial behavior, the default log writer, and creates a new
       * log4j object to be used by the TransportLogger.
       * @param next The next Transport layer in the Transport stack.
       * @return A TransportLogger object.
       * @throws IOException
       */
      public TransportLogger createTransportLogger(Transport next) throws IOException {
          int id = getNextId();
          return createTransportLogger(next, id, createLog(id), defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior, defaultJmxPort);
      }
  
      /**
       * Creates a TransportLogger object, that will be inserted in the Transport Stack.
       * Uses the default initial behavior and the default log writer.
       * @param next The next Transport layer in the Transport stack.
       * @param log The log4j log that will be used by the TransportLogger.
       * @return A TransportLogger object.
       * @throws IOException
       */
      public TransportLogger createTransportLogger(Transport next, Logger log) throws IOException {
          return createTransportLogger(next, getNextId(), log, defaultLogWriterName, defaultDynamicManagement, defaultInitialBehavior, defaultJmxPort);
      }
  
      /**
       * Creates a TransportLogger object, that will be inserted in the Transport Stack.
       * Creates a new log4j object to be used by the TransportLogger.
       * @param next The next Transport layer in the Transport stack.
       * @param startLogging Specifies if this TransportLogger should be initially active or not.
       * @param logWriterName The name or the LogWriter to be used. Different log writers can output
       * logs with a different format.
       * @return A TransportLogger object.
       * @throws IOException
       */
      public TransportLogger createTransportLogger(Transport next, String logWriterName,
              boolean useJmx, boolean startLogging, int jmxport) throws IOException {
          int id = -1; // new default to single logger
          // allow old behaviour with incantation
          if (!useJmx && jmxport != defaultJmxPort) {
              id = getNextId();
          }
          return createTransportLogger(next, id, createLog(id), logWriterName, useJmx, startLogging, jmxport);
      }
  
  
  
      /**
       * Creates a TransportLogger object, that will be inserted in the Transport Stack.
       * @param next The next Transport layer in the Transport stack.
       * @param id The id of the transport logger.
       * @param log The log4j log that will be used by the TransportLogger.
       * @param logWriterName The name or the LogWriter to be used. Different log writers can output
       * @param dynamicManagement Specifies if JMX will be used to switch on/off the TransportLogger to be created.
       * @param startLogging Specifies if this TransportLogger should be initially active or not. Only has a meaning if
       * dynamicManagement = true.
       * @param jmxport the port to be used by the JMX server. It should only be different from 1099 (broker's default JMX port)
       * when it's a client that is using Transport Logging. In a broker, if the port is different from 1099, 2 JMX servers will
       * be created, both identical, with all the MBeans.
       * @return A TransportLogger object.
       * @throws IOException
       */
      public TransportLogger createTransportLogger(Transport next, int id, Logger log,
              String logWriterName, boolean dynamicManagement, boolean startLogging, int jmxport) throws IOException {
          try {
              LogWriter logWriter = logWriterFinder.newInstance(logWriterName);
              if (id == -1) {
                  logWriter.setPrefix(String.format("%08X: ", getNextId()));
              }
              TransportLogger tl =  new TransportLogger (next, log, startLogging, logWriter);
              if (dynamicManagement) {
                  synchronized (this) {
                      if (!this.transportLoggerControlCreated) {
                          this.createTransportLoggerControl(jmxport);
                      }
                  }
                  TransportLoggerView tlv = new TransportLoggerView(tl, next.toString(), id, this.managementContext);
                  tl.setView(tlv);
              }
              return tl;
          } catch (Throwable e) {
              throw IOExceptionSupport.create("Could not create log writer object for: " + logWriterName + ", reason: " + e, e);
          }
      }
  
      synchronized private static int getNextId() {
          return ++lastId;
      }
  
      private static Logger createLog(int id) {
          return LoggerFactory.getLogger(TransportLogger.class.getName()+".Connection" + (id > 0 ? ":"+id : "" ));
      }
  
      /**
       * Starts the management context.
       * Creates and registers a TransportLoggerControl MBean which enables the user
       * to enable/disable logging for all transport loggers at once.
       */
       private void createTransportLoggerControl(int port) {
           try {
               this.managementContext = new ManagementContext();
               this.managementContext.setConnectorPort(port);
               this.managementContext.start();
           } catch (Exception e) {
               LOG.error("Management context could not be started, reason: " + e, e);
           }
  
           try {
               this.objectName = new ObjectName(this.managementContext.getJmxDomainName()+":"+ "Type=TransportLoggerControl");
               AnnotatedMBean.registerMBean(this.managementContext, new TransportLoggerControl(this.managementContext),this.objectName);
  
               this.transportLoggerControlCreated = true;
  
           } catch (Exception e) {
               LOG.error("TransportLoggerControlMBean could not be registered, reason: " + e, e);
           }
       }
  
  }
  