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