    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        m_engine = engine;
    070        applicationName = m_engine.getWikiProperties().getProperty("jspwiki.applicationName").trim();
    071
    072        if( m_mbeanServer != null ) {
    073            LOG.info( m_mbeanServer.getClass().getName() );
    074            LOG.info( m_mbeanServer.getDefaultDomain() );
    075        }
    076
    077        m_engine.addWikiEventListener( this );
    078        initialize();
    079    }
    080
    081    /** {@inheritDoc} */
    082    @Override
    083    public void initialize() {
    084        reload();
    085    }
    086
    087    private String getJMXTitleString( final int title ) {
    088        switch( title ) {
    089            case AdminBean.CORE:
    090                return "Core";
    091
    092            case AdminBean.EDITOR:
    093                return "Editors";
    094
    095            case AdminBean.UNKNOWN:
    096            default:
    097                return "Unknown";
    098        }
    099    }
    100
    101
    102    /**
    103     *  Register an AdminBean.  If the AdminBean is also a JMX MBean, it also gets registered to the MBeanServer we've found.
    104     *
    105     *  @param ab AdminBean to register.
    106     */
    107    private void registerAdminBean( final AdminBean ab ) {
    108        try {
    109            if( ab instanceof DynamicMBean && m_mbeanServer != null ) {
    110                final ObjectName name = getObjectName( ab );
    111                if( !m_mbeanServer.isRegistered( name ) ) {
    112                    m_mbeanServer.registerMBean( ab, name );
    113                }
    114            }
    115
    116            m_allBeans.add( ab );
    117
    118            LOG.info( "Registered new admin bean " + ab.getTitle() );
    119        } catch( final InstanceAlreadyExistsException e ) {
    120            LOG.error( "Admin bean already registered to JMX", e );
    121        } catch( final MBeanRegistrationException e ) {
    122            LOG.error( "Admin bean cannot be registered to JMX", e );
    123        } catch( final NotCompliantMBeanException e ) {
    124            LOG.error( "Your admin bean is not very good", e );
    125        } catch( final MalformedObjectNameException e ) {
    126            LOG.error( "Your admin bean name is not very good", e );
    127        } catch( final NullPointerException e ) {
    128            LOG.error( "Evil NPE occurred", e );
    129        }
    130    }
    131
    132    private void unregisterAdminBean( final AdminBean ab ) {
    133        try {
    134            if( ab instanceof DynamicMBean && m_mbeanServer != null ) {
    135                final ObjectName name = getObjectName( ab );
    136                if( m_mbeanServer.isRegistered( name ) ) {
    137                    m_mbeanServer.unregisterMBean( name );
    138                }
    139            }
    140
    141            m_allBeans.add( ab );
    142
    143            LOG.info( "Registered new admin bean " + ab.getTitle() );
    144        } catch( final MBeanRegistrationException e ) {
    145            LOG.error( "Admin bean cannot be registered to JMX", e );
    146        } catch( final MalformedObjectNameException e ) {
    147            LOG.error( "Your admin bean name is not very good", e );
    148        } catch( final NullPointerException e ) {
    149            LOG.error( "Evil NPE occurred", e );
    150        } catch (InstanceNotFoundException ex) {
    151            LOG.error( "Failed to unregister mbean " + ab.getClass().getSimpleName(), ex );
    152        }
    153    }
    154
    155    private ObjectName getObjectName( final AdminBean ab ) throws MalformedObjectNameException {
    156        final String component = getJMXTitleString( ab.getType() );
    157        final String title     = ab.getTitle();
    158        return new ObjectName( String.format( "%s:component=%s,name=%s (%s)", Release.APPNAME, component, title, applicationName ) );
    159    }
    160
    161    /**
    162     *  Registers all the beans from a collection of WikiModuleInfos.  If some of the beans fail, logs the message and keeps going to the
    163     *  next bean.
    164     *
    165     *  @param c Collection of WikiModuleInfo instances
    166     */
    167    private void registerBeans( final Collection< WikiModuleInfo > c ) {
    168        for( final WikiModuleInfo wikiModuleInfo : c ) {
    169            final String abname = wikiModuleInfo.getAdminBeanClass();
    170            try {
    171                if( abname != null && !abname.isEmpty() ) {
    172                    final AdminBean ab = ClassUtil.buildInstance( abname );
    173                    registerAdminBean( ab );
    174                }
    175            } catch( final ReflectiveOperationException e ) {
    176                LOG.error( e.getMessage(), e );
    177            }
    178        }
    179
    180    }
    181
    182
    183    private void reload() {
    184        //unload the beans first.
    185        if (m_allBeans != null) {
    186            for (AdminBean b : m_allBeans) {
    187                unregisterAdminBean(b);
    188            }
    189        }
    190        m_allBeans = new ArrayList<>();
    191
    192        try {
    193            registerAdminBean( new CoreBean( m_engine ) );
    194            registerAdminBean( new UserBean( m_engine ) );
    195            registerAdminBean( new SearchManagerBean( m_engine ) );
    196            registerAdminBean( new PluginBean( m_engine ) );
    197            registerAdminBean( new FilterBean( m_engine ) );
    198        } catch( final NotCompliantMBeanException e ) {
    199            LOG.error( e.getMessage(), e );
    200        }
    201        for( final ModuleManager moduleManager : m_engine.getManagers( ModuleManager.class ) ) {
    202            registerBeans( moduleManager.modules() );
    203        }
    204    }
    205
    206    /* (non-Javadoc)
    207     * @see org.apache.wiki.ui.admin.AdminBeanManager#getAllBeans()
    208     */
    209    @Override
    210    public List< AdminBean > getAllBeans() {
    211        if( m_allBeans == null ) {
    212            reload();
    213        }
    214
    215        return m_allBeans;
    216    }
    217
    218    /* (non-Javadoc)
    219     * @see org.apache.wiki.ui.admin.AdminBeanManager#findBean(java.lang.String)
    220     */
    221    @Override
    222    public AdminBean findBean( final String id ) {
    223        return m_allBeans.stream().filter(ab -> ab.getId().equals(id)).findFirst().orElse(null);
    224
    225    }
    226
    227    /**
    228     *  Provides a JDK 1.5-compliant version of the MBeanServerFactory. This will simply bind to the
    229     *  platform MBeanServer.
    230     */
    231    private static final class MBeanServerFactory15 {
    232        private MBeanServerFactory15()
    233        {}
    234
    235        public static MBeanServer getServer() {
    236            try {
    237                return ManagementFactory.getPlatformMBeanServer();
    238            } catch( final Exception e ) {
    239                LOG.error( "Unable to obtain platform MBeanServer, JMX admin beans will not be available", e );
    240                return null;
    241            }
    242        }
    243    }
    244
    245    /**
    246     *  Returns the type identifier for a string type.
    247     *
    248     *  @param type A type string.
    249     *  @return A type value.
    250     */
    251    @Override
    252    public int getTypeFromString( final String type ) {
    253        if( "core".equals( type ) ) {
    254            return AdminBean.CORE;
    255        } else if( "editors".equals( type ) ) {
    256            return AdminBean.EDITOR;
    257        }
    258
    259        return AdminBean.UNKNOWN;
    260    }
    261
    262    /* (non-Javadoc)
    263     * @see org.apache.wiki.ui.admin.AdminBeanManager#actionPerformed(org.apache.wiki.event.WikiEvent)
    264     */
    265    @Override
    266    public void actionPerformed( final WikiEvent event ) {
    267        if( m_mbeanServer != null ) {
    268            if( event instanceof WikiEngineEvent ) {
    269                if( event.getType() == WikiEngineEvent.SHUTDOWN ) {
    270                    for( final AdminBean m_allBean : m_allBeans ) {
    271                        try {
    272                            final ObjectName on = getObjectName( m_allBean );
    273                            if( m_mbeanServer.isRegistered( on ) ) {
    274                                m_mbeanServer.unregisterMBean( on );
    275                                LOG.info( "Unregistered AdminBean " + m_allBean.getTitle() );
    276                            }
    277                        } catch( final MalformedObjectNameException e ) {
    278                            LOG.error( "Malformed object name when unregistering", e );
    279                        } catch( final InstanceNotFoundException e ) {
    280                            LOG.error( "Object was registered; yet claims that it's not there", e );
    281                        } catch( final MBeanRegistrationException e ) {
    282                            LOG.error( "Registration exception while unregistering", e );
    283                        }
    284                    }
    285                }
    286            }
    287        }
    288    }
    289
    290}