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}