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