/**
 * 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.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.management.ObjectName;

import org.apache.activemq.broker.jmx.AnnotatedMBean;
import org.apache.activemq.broker.jmx.ManagementContext;
import org.apache.activemq.util.JMXSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Class implementing the TransportLoggerViewMBean interface.
 * When an object of this class is created, it registers itself in
 * the MBeanServer of the management context provided.
 * When a TransportLogger object is finalized because the Transport Stack
 * where it resides is no longer in use, the method unregister() will be called.
 * 
 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com
 *  
 * @see TransportLoggerViewMBean.
 */
public class TransportLoggerView implements TransportLoggerViewMBean {

    private static final Logger log = LoggerFactory.getLogger(TransportLoggerView.class);
    
    /**
     * Set with the TransportLoggerViews objects created.
     * Used by the methods enableAllTransportLoggers and diablellTransportLoggers.
     * The method unregister() removes objects from this set.
     */
    private static Set<TransportLoggerView> transportLoggerViews = Collections.synchronizedSet(new HashSet<TransportLoggerView>());

    private final WeakReference<TransportLogger> transportLogger;
    private final String nextTransportName;
    private final int id;
    private final ManagementContext managementContext;
    private final ObjectName name;

    /**
     * Constructor.
     * @param transportLogger The TransportLogger object which is to be managed by this MBean.
     * @param nextTransportName The name of the next TransportLayer. This is used to give a unique
     * name for each MBean of the TransportLoggerView class.
     * @param id The id of the TransportLogger to be watched.
     * @param managementContext The management context who has the MBeanServer where this MBean will be registered.
     */
    public TransportLoggerView (TransportLogger transportLogger, String nextTransportName, int id, ManagementContext managementContext) {
        this.transportLogger = new WeakReference<TransportLogger>(transportLogger);
        this.nextTransportName = nextTransportName;
        this.id = id;
        this.managementContext = managementContext;
        this.name = this.createTransportLoggerObjectName();
        
        TransportLoggerView.transportLoggerViews.add(this);
        this.register();
    }
    
    /**
     * Enable logging for all Transport Loggers at once.
     */
    public static void enableAllTransportLoggers() {
        for (TransportLoggerView view : transportLoggerViews) {
            view.enableLogging();
        }
    }
    
    /**
     * Disable logging for all Transport Loggers at once.
     */
    public static void disableAllTransportLoggers() {
        for (TransportLoggerView view : transportLoggerViews) {
            view.disableLogging();
        }
    }

    // doc comment inherited from TransportLoggerViewMBean
    public void enableLogging() {
        this.setLogging(true);
    }

    // doc comment inherited from TransportLoggerViewMBean
    public void disableLogging() {
        this.setLogging(false);
    }   

    // doc comment inherited from TransportLoggerViewMBean
    public boolean isLogging() {
        return transportLogger.get().isLogging();
    }

    // doc comment inherited from TransportLoggerViewMBean
    public void setLogging(boolean logging) {
        transportLogger.get().setLogging(logging);
    }

    /**
     * Registers this MBean in the MBeanServer of the management context
     * provided at creation time. This method is only called by the constructor.
     */
    private void register() {
        try {
        	AnnotatedMBean.registerMBean(this.managementContext, this, this.name);
        } catch (Exception e) {
            log.error("Could not register MBean for TransportLoggerView " + id + "with name " + this.name.toString() + ", reason: " + e, e);
        }

    }

    /**
     * Unregisters the MBean from the MBeanServer of the management context
     * provided at creation time.
     * This method is called by the TransportLogger object being managed when
     * the TransportLogger object is finalized, to avoid the memory leak that
     * would be caused if MBeans were not unregistered. 
     */
    public void unregister() {
        
        TransportLoggerView.transportLoggerViews.remove(this);
        
        try {
            this.managementContext.unregisterMBean(this.name);
        } catch (Exception e) {
            log.error("Could not unregister MBean for TransportLoggerView " + id + "with name " + this.name.toString() + ", reason: " + e, e);
        }
    }

    /**
     * Creates the ObjectName to be used when registering the MBean.
     * @return the ObjectName to be used when registering the MBean.
     */
    private ObjectName createTransportLoggerObjectName()  {
        try {
            return new ObjectName(
                    createTransportLoggerObjectNameRoot(this.managementContext)
                    + JMXSupport.encodeObjectNamePart(TransportLogger.class.getSimpleName()
                            + " " + this.id + ";" + this.nextTransportName));
        } catch (Exception e) {
            log.error("Could not create ObjectName for TransportLoggerView " + id + ", reason: " + e, e);
            return null;
        }
    }

    /**
     * Creates the part of the ObjectName that will be used by all MBeans.
     * This method is public so it can be used by the TransportLoggerControl class.
     * @param managementContext
     * @return A String with the part of the ObjectName common to all the TransportLoggerView MBeans.
     */
    public static String createTransportLoggerObjectNameRoot(ManagementContext managementContext) {
        return managementContext.getJmxDomainName()+":"+"Type=TransportLogger,"+"TransportLoggerName=";
    }

}
