Java程序  |  367行  |  11.22 KB

//
//  ========================================================================
//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.jmx;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;

import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
import org.eclipse.jetty.util.component.Container;
import org.eclipse.jetty.util.component.Container.Relationship;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.thread.ShutdownThread;

/**
 * Container class for the MBean instances
 */
public class MBeanContainer extends AbstractLifeCycle implements Container.Listener, Dumpable
{
    private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
    private final static HashMap<String, Integer> __unique = new HashMap<String, Integer>();
    
    public final static void resetUnique()
    {
        synchronized (__unique)
        {
            __unique.clear();
        }
    }
    
    private final MBeanServer _server;
    private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
    private final WeakHashMap<ObjectName,List<Container.Relationship>> _relations = new WeakHashMap<ObjectName,List<Container.Relationship>>();
    private String _domain = null;

    /**
     * Lookup an object name by instance
     *
     * @param object instance for which object name is looked up
     * @return object name associated with specified instance, or null if not found
     */
    public synchronized ObjectName findMBean(Object object)
    {
        ObjectName bean = _beans.get(object);
        return bean == null ? null : bean;
    }

    /**
     * Lookup an instance by object name
     *
     * @param oname object name of instance
     * @return instance associated with specified object name, or null if not found
     */
    public synchronized Object findBean(ObjectName oname)
    {
        for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
        {
            ObjectName bean = entry.getValue();
            if (bean.equals(oname))
                return entry.getKey();
        }
        return null;
    }

    /**
     * Constructs MBeanContainer
     *
     * @param server instance of MBeanServer for use by container
     */
    public MBeanContainer(MBeanServer server)
    {
        _server = server;
    }

    /**
     * Retrieve instance of MBeanServer used by container
     *
     * @return instance of MBeanServer
     */
    public MBeanServer getMBeanServer()
    {
        return _server;
    }

    /**
     * Set domain to be used to add MBeans
     *
     * @param domain domain name
     */
    public void setDomain(String domain)
    {
        _domain = domain;
    }

    /**
     * Retrieve domain name used to add MBeans
     *
     * @return domain name
     */
    public String getDomain()
    {
        return _domain;
    }

    /**
     * Implementation of Container.Listener interface
     *
     * @see org.eclipse.jetty.util.component.Container.Listener#add(org.eclipse.jetty.util.component.Container.Relationship)
     */
    public synchronized void add(Relationship relationship)
    {
        LOG.debug("add {}",relationship);
        ObjectName parent = _beans.get(relationship.getParent());
        if (parent == null)
        {
            addBean(relationship.getParent());
            parent = _beans.get(relationship.getParent());
        }

        ObjectName child = _beans.get(relationship.getChild());
        if (child == null)
        {
            addBean(relationship.getChild());
            child = _beans.get(relationship.getChild());
        }

        if (parent != null && child != null)
        {
            List<Container.Relationship> rels = _relations.get(parent);
            if (rels==null)
            {
                rels=new ArrayList<Container.Relationship>();
                _relations.put(parent,rels);
            }
            rels.add(relationship);
        }
    }

    /**
     * Implementation of Container.Listener interface
     *
     * @see org.eclipse.jetty.util.component.Container.Listener#remove(org.eclipse.jetty.util.component.Container.Relationship)
     */
    public synchronized void remove(Relationship relationship)
    {
        LOG.debug("remove {}",relationship);
        ObjectName parent = _beans.get(relationship.getParent());
        ObjectName child = _beans.get(relationship.getChild());

        if (parent != null && child != null)
        {
            List<Container.Relationship> rels = _relations.get(parent);
            if (rels!=null)
            {
                for (Iterator<Container.Relationship> i=rels.iterator();i.hasNext();)
                {
                    Container.Relationship r = i.next();
                    if (relationship.equals(r) || r.getChild()==null)
                        i.remove();
                }
            }
        }
    }

    /**
     * Implementation of Container.Listener interface
     *
     * @see org.eclipse.jetty.util.component.Container.Listener#removeBean(java.lang.Object)
     */
    public synchronized void removeBean(Object obj)
    {
        LOG.debug("removeBean {}",obj);
        ObjectName bean = _beans.remove(obj);

        if (bean != null)
        {
            List<Container.Relationship> beanRelations= _relations.remove(bean);
            if (beanRelations != null)
            {
                LOG.debug("Unregister {}", beanRelations);
                List<?> removeList = new ArrayList<Object>(beanRelations);
                for (Object r : removeList)
                {
                    Container.Relationship relation = (Relationship)r;
                    relation.getContainer().update(relation.getParent(), relation.getChild(), null, relation.getRelationship(), true);
                }
            }

            try
            {
                _server.unregisterMBean(bean);
                LOG.debug("Unregistered {}", bean);
            }
            catch (javax.management.InstanceNotFoundException e)
            {
                LOG.ignore(e);
            }
            catch (Exception e)
            {
                LOG.warn(e);
            }
        }
    }

    /**
     * Implementation of Container.Listener interface
     *
     * @see org.eclipse.jetty.util.component.Container.Listener#addBean(java.lang.Object)
     */
    public synchronized void addBean(Object obj)
    {
        LOG.debug("addBean {}",obj);
        try
        {
            if (obj == null || _beans.containsKey(obj))
                return;

            Object mbean = ObjectMBean.mbeanFor(obj);
            if (mbean == null)
                return;

            ObjectName oname = null;
            if (mbean instanceof ObjectMBean)
            {
                ((ObjectMBean)mbean).setMBeanContainer(this);
                oname = ((ObjectMBean)mbean).getObjectName();
            }

            //no override mbean object name, so make a generic one
            if (oname == null)
            {
                String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
                int dot = type.lastIndexOf('.');
                if (dot >= 0)
                    type = type.substring(dot + 1);

                String context = null;
                if (mbean instanceof ObjectMBean)
                {
                    context = makeName(((ObjectMBean)mbean).getObjectContextBasis());
                }

                String name = null;
                if (mbean instanceof ObjectMBean)
                {
                    name = makeName(((ObjectMBean)mbean).getObjectNameBasis());
                }

                StringBuffer buf = new StringBuffer();
                buf.append("type=").append(type);
                if (context != null && context.length()>1)
                {
                    buf.append(buf.length()>0 ? ",":"");
                    buf.append("context=").append(context);
                }
                if (name != null && name.length()>1)
                {
                    buf.append(buf.length()>0 ? ",":"");
                    buf.append("name=").append(name);
                }
                    
                String basis = buf.toString();
                Integer count;
                synchronized (__unique)
                {
                    count = __unique.get(basis);
                    count = count == null ? 0 : 1 + count;
                    __unique.put(basis, count);
                }

                //if no explicit domain, create one
                String domain = _domain;
                if (domain == null)
                    domain = obj.getClass().getPackage().getName();

                oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count);
            }

            ObjectInstance oinstance = _server.registerMBean(mbean, oname);
            LOG.debug("Registered {}", oinstance.getObjectName());
            _beans.put(obj, oinstance.getObjectName());

        }
        catch (Exception e)
        {
            LOG.warn("bean: " + obj, e);
        }
    }

    /**
     * @param basis name to strip of special characters.
     * @return normalized name
     */
    public String makeName(String basis)
    {
        if (basis==null)
            return basis;
        return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
    }

    /**
     * Perform actions needed to start lifecycle
     *
     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
     */
    public void doStart()
    {
        ShutdownThread.register(this);
    }

    /**
     * Perform actions needed to stop lifecycle
     *
     * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
     */
    public void doStop()
    {
        Set<Object> removeSet = new HashSet<Object>(_beans.keySet());
        for (Object removeObj : removeSet)
        {
            removeBean(removeObj);
        }
    }

    public void dump(Appendable out, String indent) throws IOException
    {
        AggregateLifeCycle.dumpObject(out,this);
        AggregateLifeCycle.dump(out, indent, _beans.entrySet());
    }

    public String dump()
    {
        return AggregateLifeCycle.dump(this);
    }
}