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;
035
036import javax.management.DynamicMBean;
037import javax.management.InstanceAlreadyExistsException;
038import javax.management.InstanceNotFoundException;
039import javax.management.MBeanRegistrationException;
040import javax.management.MBeanServer;
041import javax.management.MalformedObjectNameException;
042import javax.management.NotCompliantMBeanException;
043import javax.management.ObjectName;
044import java.lang.management.ManagementFactory;
045import java.util.ArrayList;
046import java.util.Collection;
047import java.util.List;
048
049
050/**
051 *  Provides a manager class for all AdminBeans within JSPWiki.  This class also manages registration for any
052 *  AdminBean which is also a JMX bean.
053 *
054 *  @since  2.5.52
055 */
056public class DefaultAdminBeanManager implements WikiEventListener, AdminBeanManager {
057
058    private final Engine m_engine;
059    private ArrayList< AdminBean > m_allBeans;
060    private final MBeanServer m_mbeanServer;
061
062    private static final Logger log = LogManager.getLogger( DefaultAdminBeanManager.class );
063
064    public DefaultAdminBeanManager( final Engine engine ) {
065        log.info("Using JDK 1.5 Platform MBeanServer");
066        m_mbeanServer = MBeanServerFactory15.getServer();
067
068        m_engine = engine;
069
070        if( m_mbeanServer != null ) {
071            log.info( m_mbeanServer.getClass().getName() );
072            log.info( m_mbeanServer.getDefaultDomain() );
073        }
074
075        m_engine.addWikiEventListener( this );
076        initialize();
077    }
078
079    /** {@inheritDoc} */
080    @Override
081    public void initialize() {
082        reload();
083    }
084
085    private String getJMXTitleString( final int title ) {
086        switch( title ) {
087            case AdminBean.CORE:
088                return "Core";
089
090            case AdminBean.EDITOR:
091                return "Editors";
092
093            case AdminBean.UNKNOWN:
094            default:
095                return "Unknown";
096        }
097    }
098
099
100    /**
101     *  Register an AdminBean.  If the AdminBean is also a JMX MBean, it also gets registered to the MBeanServer we've found.
102     *
103     *  @param ab AdminBean to register.
104     */
105    private void registerAdminBean( final AdminBean ab ) {
106        try {
107            if( ab instanceof DynamicMBean && m_mbeanServer != null ) {
108                final ObjectName name = getObjectName( ab );
109                if( !m_mbeanServer.isRegistered( name ) ) {
110                    m_mbeanServer.registerMBean( ab, name );
111                }
112            }
113
114            m_allBeans.add( ab );
115
116            log.info( "Registered new admin bean " + ab.getTitle() );
117        } catch( final InstanceAlreadyExistsException e ) {
118            log.error( "Admin bean already registered to JMX", e );
119        } catch( final MBeanRegistrationException e ) {
120            log.error( "Admin bean cannot be registered to JMX", e );
121        } catch( final NotCompliantMBeanException e ) {
122            log.error( "Your admin bean is not very good", e );
123        } catch( final MalformedObjectNameException e ) {
124            log.error( "Your admin bean name is not very good", e );
125        } catch( final NullPointerException e ) {
126            log.error( "Evil NPE occurred", e );
127        }
128    }
129
130    private ObjectName getObjectName( final AdminBean ab ) throws MalformedObjectNameException {
131        final String component = getJMXTitleString( ab.getType() );
132        final String title     = ab.getTitle();
133        return new ObjectName( Release.APPNAME + ":component=" + component + ",name=" + title );
134    }
135
136    /**
137     *  Registers all the beans from a collection of WikiModuleInfos.  If some of the beans fail, logs the message and keeps going to the
138     *  next bean.
139     *
140     *  @param c Collection of WikiModuleInfo instances
141     */
142    private void registerBeans( final Collection< WikiModuleInfo > c ) {
143        for( final WikiModuleInfo wikiModuleInfo : c ) {
144            final String abname = wikiModuleInfo.getAdminBeanClass();
145            try {
146                if( abname != null && !abname.isEmpty() ) {
147                    final Class< ? > abclass = Class.forName( abname );
148                    final AdminBean ab = ( AdminBean )abclass.newInstance();
149                    registerAdminBean( ab );
150                }
151            } catch( final ClassNotFoundException | InstantiationException | IllegalAccessException 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        for( final AdminBean ab : m_allBeans ) {
194            if( ab.getId().equals( id ) ) {
195                return ab;
196            }
197        }
198
199        return null;
200    }
201
202    /**
203     *  Provides a JDK 1.5-compliant version of the MBeanServerFactory. This will simply bind to the
204     *  platform MBeanServer.
205     */
206    private static final class MBeanServerFactory15 {
207        private MBeanServerFactory15()
208        {}
209
210        public static MBeanServer getServer() {
211            return ManagementFactory.getPlatformMBeanServer();
212        }
213    }
214
215    /**
216     *  Returns the type identifier for a string type.
217     *
218     *  @param type A type string.
219     *  @return A type value.
220     */
221    @Override
222    public int getTypeFromString( final String type ) {
223        if( "core".equals( type ) ) {
224            return AdminBean.CORE;
225        } else if( "editors".equals( type ) ) {
226            return AdminBean.EDITOR;
227        }
228
229        return AdminBean.UNKNOWN;
230    }
231
232    /* (non-Javadoc)
233     * @see org.apache.wiki.ui.admin.AdminBeanManager#actionPerformed(org.apache.wiki.event.WikiEvent)
234     */
235    @Override
236    public void actionPerformed( final WikiEvent event ) {
237        if( event instanceof WikiEngineEvent ) {
238            if( event.getType() == WikiEngineEvent.SHUTDOWN ) {
239                for( final AdminBean m_allBean : m_allBeans ) {
240                    try {
241                        final ObjectName on = getObjectName( m_allBean );
242                        if( m_mbeanServer.isRegistered( on ) ) {
243                            m_mbeanServer.unregisterMBean( on );
244                            log.info( "Unregistered AdminBean " + m_allBean.getTitle() );
245                        }
246                    } catch( final MalformedObjectNameException e ) {
247                        log.error( "Malformed object name when unregistering", e );
248                    } catch( final InstanceNotFoundException e ) {
249                        log.error( "Object was registered; yet claims that it's not there", e );
250                    } catch( final MBeanRegistrationException e ) {
251                        log.error( "Registration exception while unregistering", e );
252                    }
253                }
254            }
255        }
256    }
257
258}