    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.ui.admin;
    020
    021import org.apache.logging.log4j.LogManager;
    022import org.apache.logging.log4j.Logger;
    023import org.apache.wiki.api.Release;
    024import org.apache.wiki.api.core.Engine;
    025import org.apache.wiki.event.WikiEngineEvent;
    026import org.apache.wiki.event.WikiEvent;
    027import org.apache.wiki.event.WikiEventListener;
    028import org.apache.wiki.modules.ModuleManager;
    029import org.apache.wiki.modules.WikiModuleInfo;
    030import org.apache.wiki.ui.admin.beans.CoreBean;
    031import org.apache.wiki.ui.admin.beans.FilterBean;
    032import org.apache.wiki.ui.admin.beans.PluginBean;
    033import org.apache.wiki.ui.admin.beans.SearchManagerBean;
    034import org.apache.wiki.ui.admin.beans.UserBean;
    035import org.apache.wiki.util.ClassUtil;
    036
    037import javax.management.DynamicMBean;
    038import javax.management.InstanceAlreadyExistsException;
    039import javax.management.InstanceNotFoundException;
    040import javax.management.MBeanRegistrationException;
    041import javax.management.MBeanServer;
    042import javax.management.MalformedObjectNameException;
    043import javax.management.NotCompliantMBeanException;
    044import javax.management.ObjectName;
    045import java.lang.management.ManagementFactory;
    046import java.util.ArrayList;
    047import java.util.Collection;
    048import java.util.List;
    049
    050
    051/**
    052 *  Provides a manager class for all AdminBeans within JSPWiki.  This class also manages registration for any
    053 *  AdminBean which is also a JMX bean.
    054 *
    055 *  @since  2.5.52
    056 */
    057public class DefaultAdminBeanManager implements WikiEventListener, AdminBeanManager {
    058
    059    private final Engine m_engine;
    060    private final String applicationName;
    061    private ArrayList< AdminBean > m_allBeans;
    062    private final MBeanServer m_mbeanServer;
    063
    064    private static final Logger LOG = LogManager.getLogger( DefaultAdminBeanManager.class );
    065
    066    public DefaultAdminBeanManager( final Engine engine ) {
    067        LOG.info("Using JDK 1.5 Platform MBeanServer");
    068        m_mbeanServer = MBeanServerFactory15.getServer();
    069
    070        m_engine = engine;
    071        applicationName = m_engine.getWikiProperties().getProperty("jspwiki.applicationName").trim();
    072
    073        if( m_mbeanServer != null ) {
    074            LOG.info( m_mbeanServer.getClass().getName() );
    075            LOG.info( m_mbeanServer.getDefaultDomain() );
    076        }
    077
    078        m_engine.addWikiEventListener( this );
    079        initialize();
    080    }
    081
    082    /** {@inheritDoc} */
    083    @Override
    084    public void initialize() {
    085        reload();
    086    }
    087
    088    private String getJMXTitleString( final int title ) {
    089        switch( title ) {
    090            case AdminBean.CORE:
    091                return "Core";
    092
    093            case AdminBean.EDITOR:
    094                return "Editors";
    095
    096            case AdminBean.UNKNOWN:
    097            default:
    098                return "Unknown";
    099        }
    100    }
    101
    102
    103    /**
    104     *  Register an AdminBean.  If the AdminBean is also a JMX MBean, it also gets registered to the MBeanServer we've found.
    105     *
    106     *  @param ab AdminBean to register.
    107     */
    108    private void registerAdminBean( final AdminBean ab ) {
    109        try {
    110            if( ab instanceof DynamicMBean && m_mbeanServer != null ) {
    111                final ObjectName name = getObjectName( ab );
    112                if( !m_mbeanServer.isRegistered( name ) ) {
    113                    m_mbeanServer.registerMBean( ab, name );
    114                }
    115            }
    116
    117            m_allBeans.add( ab );
    118
    119            LOG.info( "Registered new admin bean " + ab.getTitle() );
    120        } catch( final InstanceAlreadyExistsException e ) {
    121            LOG.error( "Admin bean already registered to JMX", e );
    122        } catch( final MBeanRegistrationException e ) {
    123            LOG.error( "Admin bean cannot be registered to JMX", e );
    124        } catch( final NotCompliantMBeanException e ) {
    125            LOG.error( "Your admin bean is not very good", e );
    126        } catch( final MalformedObjectNameException e ) {
    127            LOG.error( "Your admin bean name is not very good", e );
    128        } catch( final NullPointerException e ) {
    129            LOG.error( "Evil NPE occurred", e );
    130        }
    131    }
    132
    133    private ObjectName getObjectName( final AdminBean ab ) throws MalformedObjectNameException {
    134        final String component = getJMXTitleString( ab.getType() );
    135        final String title     = ab.getTitle();
    136        return new ObjectName(String.format("%s:component=%s,name=%s (%s)", Release.APPNAME, component, title, applicationName));
    137    }
    138
    139    /**
    140     *  Registers all the beans from a collection of WikiModuleInfos.  If some of the beans fail, logs the message and keeps going to the
    141     *  next bean.
    142     *
    143     *  @param c Collection of WikiModuleInfo instances
    144     */
    145    private void registerBeans( final Collection< WikiModuleInfo > c ) {
    146        for( final WikiModuleInfo wikiModuleInfo : c ) {
    147            final String abname = wikiModuleInfo.getAdminBeanClass();
    148            try {
    149                if( abname != null && !abname.isEmpty() ) {
    150                    final AdminBean ab = ClassUtil.buildInstance( abname );
    151                    registerAdminBean( ab );
    152                }
    153            } catch( final ReflectiveOperationException e ) {
    154                LOG.error( e.getMessage(), e );
    155            }
    156        }
    157
    158    }
    159
    160    // FIXME: Should unload the beans first.
    161    private void reload() {
    162        m_allBeans = new ArrayList<>();
    163
    164        try {
    165            registerAdminBean( new CoreBean( m_engine ) );
    166            registerAdminBean( new UserBean( m_engine ) );
    167            registerAdminBean( new SearchManagerBean( m_engine ) );
    168            registerAdminBean( new PluginBean( m_engine ) );
    169            registerAdminBean( new FilterBean( m_engine ) );
    170        } catch( final NotCompliantMBeanException e ) {
    171            LOG.error( e.getMessage(), e );
    172        }
    173        for( final ModuleManager moduleManager : m_engine.getManagers( ModuleManager.class ) ) {
    174            registerBeans( moduleManager.modules() );
    175        }
    176    }
    177
    178    /* (non-Javadoc)
    179     * @see org.apache.wiki.ui.admin.AdminBeanManager#getAllBeans()
    180     */
    181    @Override
    182    public List< AdminBean > getAllBeans() {
    183        if( m_allBeans == null ) {
    184            reload();
    185        }
    186
    187        return m_allBeans;
    188    }
    189
    190    /* (non-Javadoc)
    191     * @see org.apache.wiki.ui.admin.AdminBeanManager#findBean(java.lang.String)
    192     */
    193    @Override
    194    public AdminBean findBean( final String id ) {
    195        return m_allBeans.stream().filter(ab -> ab.getId().equals(id)).findFirst().orElse(null);
    196
    197    }
    198
    199    /**
    200     *  Provides a JDK 1.5-compliant version of the MBeanServerFactory. This will simply bind to the
    201     *  platform MBeanServer.
    202     */
    203    private static final class MBeanServerFactory15 {
    204        private MBeanServerFactory15()
    205        {}
    206
    207        public static MBeanServer getServer() {
    208            return ManagementFactory.getPlatformMBeanServer();
    209        }
    210    }
    211
    212    /**
    213     *  Returns the type identifier for a string type.
    214     *
    215     *  @param type A type string.
    216     *  @return A type value.
    217     */
    218    @Override
    219    public int getTypeFromString( final String type ) {
    220        if( "core".equals( type ) ) {
    221            return AdminBean.CORE;
    222        } else if( "editors".equals( type ) ) {
    223            return AdminBean.EDITOR;
    224        }
    225
    226        return AdminBean.UNKNOWN;
    227    }
    228
    229    /* (non-Javadoc)
    230     * @see org.apache.wiki.ui.admin.AdminBeanManager#actionPerformed(org.apache.wiki.event.WikiEvent)
    231     */
    232    @Override
    233    public void actionPerformed( final WikiEvent event ) {
    234        if( event instanceof WikiEngineEvent ) {
    235            if( event.getType() == WikiEngineEvent.SHUTDOWN ) {
    236                for( final AdminBean m_allBean : m_allBeans ) {
    237                    try {
    238                        final ObjectName on = getObjectName( m_allBean );
    239                        if( m_mbeanServer.isRegistered( on ) ) {
    240                            m_mbeanServer.unregisterMBean( on );
    241                            LOG.info( "Unregistered AdminBean " + m_allBean.getTitle() );
    242                        }
    243                    } catch( final MalformedObjectNameException e ) {
    244                        LOG.error( "Malformed object name when unregistering", e );
    245                    } catch( final InstanceNotFoundException e ) {
    246                        LOG.error( "Object was registered; yet claims that it's not there", e );
    247                    } catch( final MBeanRegistrationException e ) {
    248                        LOG.error( "Registration exception while unregistering", e );
    249                    }
    250                }
    251            }
    252        }
    253    }
    254
    255}