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