Index: src/main/java/org/apache/log4j/jmx/AbstractModelMBean.java =================================================================== --- src/main/java/org/apache/log4j/jmx/AbstractModelMBean.java (revision 0) +++ src/main/java/org/apache/log4j/jmx/AbstractModelMBean.java (revision 0) @@ -0,0 +1,168 @@ +package org.apache.log4j.jmx; + +import javax.management.Descriptor; +import javax.management.MBeanException; +import javax.management.MBeanParameterInfo; +import javax.management.RuntimeOperationsException; +import javax.management.modelmbean.DescriptorSupport; +import javax.management.modelmbean.ModelMBeanAttributeInfo; +import javax.management.modelmbean.ModelMBeanConstructorInfo; +import javax.management.modelmbean.ModelMBeanInfo; +import javax.management.modelmbean.ModelMBeanInfoSupport; +import javax.management.modelmbean.ModelMBeanNotificationInfo; +import javax.management.modelmbean.ModelMBeanOperationInfo; +import javax.management.modelmbean.RequiredModelMBean; + +/** + * Base class for Model MBeans. + * To provide a Mode MBean simply extend this class and override getModelMbeanInfo using + * createModelMBeanInfo. + * For registering attributes, methods, constructors, ... there exist createModelMBean*-methods. + * @author Stefan Fleiter + */ +public abstract class AbstractModelMBean extends RequiredModelMBean { + + private final Object delegate; + + /** + * Constructor to use an own instance of a Delegate to represent per JMX. + * @param delegate Instance of LoggerManager to represent as MBean + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + public AbstractModelMBean(Object delegate) throws MBeanException, RuntimeOperationsException { + this.delegate = delegate; + setModelMBeanInfo(getModelMBeanInfo()); + try { + setManagedResource(delegate, "ObjectReference"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public Object getDelegate() { + return delegate; + } + + protected abstract ModelMBeanInfo getModelMBeanInfo() throws RuntimeOperationsException, MBeanException; + + /** + * Creates the ModelMBeanNotificationInfos for this ModelMBean, extracted for easy extendability. + * @return Array of ModelMBeanNotificationInfo describing the methods available per JMX + */ + protected ModelMBeanNotificationInfo[] createModelMBeanNotificationInfos() { + return new ModelMBeanNotificationInfo[] {}; + } + + /** + * Creates the ModelMBeanConstructorInfos for this ModelMBean, extracted for easy extendability. + * @return Array of ModelMBeanConstructorInfo describing the methods available per JMX + */ + protected ModelMBeanConstructorInfo[] createModelMBeanConstructorInfos() { + return new ModelMBeanConstructorInfo[] {}; + } + + /** + * Creates the ModelMBeanOperationInfos for this ModelMBean, extracted for easy extendability. + * @return Array of ModelMBeanOperationInfo describing the methods available per JMX + */ + protected ModelMBeanOperationInfo[] createModelMBeanOperationInfos() { + return new ModelMBeanOperationInfo[] {}; + } + + /** + * Creates the ModelMBeanAttributeInfos for this ModelMBean, extracted for easy extendability. + * @return Array of ModelMBeanAttributeInfo describing the methods available per JMX + */ + protected ModelMBeanAttributeInfo[] createModelMBeanAttributeInfos() { + return new ModelMBeanAttributeInfo[] {}; + } + + /** + * Creates the ModelMBeanInfo for this ModelMBean. + * @param displayName Name for this mbean to display in manager applications + * @param description Description what this MBean does + * @param clazz Class implementing the functionality and this MBean delegates to + * @param mbeanName Name of + * @return Array of ModelMBeanConstructorInfo describing the methods available per JMX + */ + protected ModelMBeanInfo createModelMBeanInfo(String displayName, String description, + Class clazz, String mbeanName) { + ModelMBeanInfo result; + Descriptor managerDescriptor = new DescriptorSupport(new String[] { + "name=" + mbeanName, + "descriptorType=mbean", + "displayName=" + displayName, + "type=" + clazz.getName(), + }); + + result = new ModelMBeanInfoSupport( + clazz.getName(), + description, + createModelMBeanAttributeInfos(), + createModelMBeanConstructorInfos(), + createModelMBeanOperationInfos(), + createModelMBeanNotificationInfos(), + managerDescriptor + ); + return result; + } + + /** + * Helper Method for registering operations. + * @param name Name of operation + * @param description describes what the operation does + * @param parameters parameters of the operation + * @param returnType class which is returned by the to be registered method + * @param operationInfo INFO if non-modifying and ACTION if modifying status + * @return ModelMBeanOperationInfo for the described method + */ + protected ModelMBeanOperationInfo createOperationInfo(String name, String description, + MBeanParameterInfo[] parameters, Class returnType, int operationInfo) { + return new ModelMBeanOperationInfo( + name, + description, + parameters, + returnType.getName(), + operationInfo, + new DescriptorSupport(new String[] { + "name=" + name, + "displayName=" + description, + "descriptorType=operation", + "class=" + getDelegate().getClass().getName(), + "role=operation", + }) + ); + } + + /** + * Helper Method for registering attributes. + * @param name Name of attribute + * @param description describes what the attribute is for + * @param type type of attribute (return value of get and parameter of set Operation) + * @param readable true, if attribute can be read + * @param writable true, if attribute can be set + * @param is true if the attribute has an "is" getter, false otherwise + * @param getMethod name of getter Method + * @param setMethod name of setter Method + * @return ModelMBeanAttributeInfo for the described method + */ + protected ModelMBeanAttributeInfo createAttributeInfo(String name, String description, + Class type, boolean readable, boolean writable, boolean is, String getMethod, + String setMethod) { + return new ModelMBeanAttributeInfo( + name, + type.getName(), + description, + readable, writable, is, + new DescriptorSupport(new String[] { + "name=" + name, + "descriptorType=attribute", + "displayName=" + description, + "getMethod=" + getMethod, + "setMethod=" + setMethod, + }) + ); + } + +} Index: src/main/java/org/apache/log4j/jmx/RollbackableLoggerManager.java =================================================================== --- src/main/java/org/apache/log4j/jmx/RollbackableLoggerManager.java (revision 0) +++ src/main/java/org/apache/log4j/jmx/RollbackableLoggerManager.java (revision 0) @@ -0,0 +1,135 @@ +package org.apache.log4j.jmx; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Stack; +import java.util.Map.Entry; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + +/** + * LoggerManager implementation with additional rollback support to be able to undo the changes + * done by this class. + * @see LoggerManager + * @author Stefan Fleiter + */ +public class RollbackableLoggerManager extends LoggerManager { + + /** + * Thread safety guaranteed by locking on this (instance of LoggerManager) on every access. + * Invariant guarded is that savedLoggerConfiguration and loggerConfigurationUndoStack + * contain consistent information. + */ + private final Map initialLoggerConfigurations = new HashMap(); + + /** + * Thread safety guaranteed by locking on this (instance of LoggerManager) on every access. + * Invariant guarded is that savedLoggerConfiguration and loggerConfigurationUndoStack + * contain consistent information. + */ + private final Stack loggerConfigurationUndoStack = new Stack(); + + /** + * Holder for a Logger and a Level combination. + * This object ist immutable. + * @author Stefan Fleiter + */ + class LoggerConfiguration { + private final Logger logger; + private final Level formerLevel; + + /** + * Constructor + * @param logger a logger + * @param formerLevel level of the logger + */ + LoggerConfiguration(Logger logger, Level formerLevel) { + super(); + this.logger = logger; + this.formerLevel = formerLevel; + } + + public Logger getLogger() { + return logger; + } + public Level getFormerLevel() { + return formerLevel; + } + } + + + /** + * {@inheritDoc} + * Saves all needed data for supporting rollback. + */ + protected boolean doSetLogLogLevel(Logger logger, String level) { + Level originalLevel = logger.getLevel(); + boolean modified = super.doSetLogLogLevel(logger, level); + if (modified) { + synchronized (this) { + if (!initialLoggerConfigurations.containsKey(logger)) { + initialLoggerConfigurations.put(logger, originalLevel); + } + loggerConfigurationUndoStack.push(new LoggerConfiguration(logger, originalLevel)); + } + } + return modified; + } + + /** + * Returns count of modifications to logging which can be undone. + * This count decreases after calls to the rollback methods. + * @return count of modifications which can be undone + */ + public int getLogConfigurationModificationCount() { + synchronized (this) { + return loggerConfigurationUndoStack.size(); + } + } + + /** + * Rolls back all modifications which where done to the log configuration by this instance. + */ + public void rollbackLogConfiguration() { + synchronized (this) { + for (Iterator it = initialLoggerConfigurations.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = (Entry) it.next(); + Logger logger = (Logger) entry.getKey(); + Level initialLevel = (Level) entry.getValue(); + logger.setLevel(initialLevel); + } + destroy(); + } + } + + /** + * Rolls back last modification which was done to the log configuration by this instance. + * @return true, wenn undo durchgeführt wurde, false wenn es keine Modifikationen + * zum undo mehr gab + */ + public void rollbackLastLogConfigurationModification() { + synchronized (this) { + if (!loggerConfigurationUndoStack.empty()) { + LoggerConfiguration loggerState = (LoggerConfiguration) loggerConfigurationUndoStack.pop(); + Logger logger = loggerState.getLogger(); + Level originalLevel = loggerState.getFormerLevel(); + logger.setLevel(originalLevel); + } + } + } + + /** + * Shutdown hook which clears all references, but does not call resetLogConfiguration + * @see #resetLogConfiguration() + */ + public void destroy() { + super.destroy(); + synchronized (this) { + initialLoggerConfigurations.clear(); + loggerConfigurationUndoStack.clear(); + } + } + +} Index: src/main/java/org/apache/log4j/jmx/LoggerManager.java =================================================================== --- src/main/java/org/apache/log4j/jmx/LoggerManager.java (revision 0) +++ src/main/java/org/apache/log4j/jmx/LoggerManager.java (revision 0) @@ -0,0 +1,183 @@ +package org.apache.log4j.jmx; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; +import org.apache.log4j.spi.LoggerRepository; + +/** + * Helper for reading and modifying the logger configuration of a running log4j instance. + * This class is thread safe. + * @author Stefan Fleiter + */ +public class LoggerManager { + + /** + * Returns a Level instance matching the given string representation. + * @param levelName Name of the level of INHERITED to inherit level from the next ancestor. + * @return Level matching levelName, null for INHERITED + * @throws IllegalArgumentException levelName does not represent a known level + */ + Level getLevelByName(String levelName) throws IllegalArgumentException { + Level level = Level.toLevel(levelName, null); + if (null == level && null != levelName) { + String lowerLevelName = levelName.toLowerCase(); + if (!"inherit".equals(lowerLevelName) && + !"inherited".equals(lowerLevelName) && + !"null".equals(lowerLevelName)) { + throw new IllegalArgumentException('"' + levelName + "\" is not a valid level"); + } + } + return level; + } + + /** + * Returns the string representation of the given Level. + * @param level Level instance + * @return Name of level or null if level is null + */ + String getLogLevelString(Level level) { + return null == level ? null : level.toString(); + } + + + /** + * Returns the explicit set level of a specified logger. + * @param loggerName Name of the logger + * @return Level of the logger + */ + public String getLogLevel(String loggerName) { + return getLogLevelString(LogManager.getLogger(loggerName).getLevel()); + } + + /** + * Returns the effective level (the level set on this logger or if not set explicitly the + * level of the next ancestor) of the specified logger. + * @param loggerName Name of logger either existing or to be created + * @return Effective level of the logger + */ + public String getEffectiveLogLevel(String loggerName) { + return getLogLevelString(LogManager.getLogger(loggerName).getEffectiveLevel()); + } + + /** + * Compares two levels and returns whether they are equal. + * @param first first level + * @param second second level + * @return true if levels are equal + */ + boolean levelEquals(Level first, Level second) { + if (null == first) { + return null == second; + } else { + return first.equals(second); + } + } + + /** + * Sets the level of the specified logger. + * @param loggerName Name of logger either existing or to be created + * @param level New level + * @throws IllegalArgumentException levelName does not represent a known level + */ + public void setLogLevel(String loggerName, String level) throws IllegalArgumentException { + Logger logger = LogManager.getLogger(loggerName); + doSetLogLogLevel(logger, level); + } + + /** + * Returns the level of the root logger. + * @return Level of the logger + */ + public String getRootLogLevel() { + return getLogLevelString(LogManager.getRootLogger().getLevel()); + } + + /** + * Sets the level of the specified logger. + * @param level New level + * @throws IllegalArgumentException levelName does not represent a known level + */ + public void setRootLogLevel(String level) throws IllegalArgumentException { + Logger logger = LogManager.getRootLogger(); + doSetLogLogLevel(logger, level); + } + + /** + * Sets the level of the specified logger. + * Can be overriden to do something additional when modifying loggers. + * @param logger Log4j Logger Instance + * @param level New level as string + * @return true, if level has changed, false if level is same as before + */ + protected boolean doSetLogLogLevel(Logger logger, String level) { + Level originalLevel = logger.getLevel(); + Level newLevel = getLevelByName(level); + boolean modified = false; + if (!levelEquals(newLevel, originalLevel)) { + modified = true; + logger.setLevel(newLevel); + } + return modified; + } + + /** + * Shutdown hook. + */ + public void destroy() { + // nothing to do + } + + /** + * Returns a map of all explicitly configured loggers with the level as value. + * @return map of logger names and level names + */ + public Map getLogConfiguration() { + Map result = new HashMap(); + List loggers = getConfiguredLoggersInternal(); + for (Iterator it = loggers.iterator(); it.hasNext(); ) { + Logger logger = (Logger) it.next(); + result.put(logger.getName(), logger.getEffectiveLevel().toString()); + } + return result; + } + + /** + * @return all explicitly configured loggers + */ + List getConfiguredLoggersInternal() { + List result = new ArrayList(); + LoggerRepository r = LogManager.getLoggerRepository(); + Enumeration loggers = r.getCurrentLoggers(); + while (loggers.hasMoreElements()) { + Logger logger = (Logger) loggers.nextElement(); + if (null != logger.getLevel()) { + result.add(logger); + } + } + result.add(LogManager.getRootLogger()); + return result; + } + + /** + * Returns the names of all loggers with an explicit set level + * (Logger{@link #getLogLevel(String)} is not null). + * @return array of names of all explicitly configured loggers + */ + public String[] getConfiguredLoggers() { + List configuredLoggers = getConfiguredLoggersInternal(); + String[] result = new String[configuredLoggers.size()]; + for (int i = 0; i < configuredLoggers.size(); i++) { + result[i] = ((Logger) configuredLoggers.get(i)).getName(); + } + return result; + } + +} Index: src/main/java/org/apache/log4j/jmx/RollbackableLoggerManagerModelMBean.java =================================================================== --- src/main/java/org/apache/log4j/jmx/RollbackableLoggerManagerModelMBean.java (revision 0) +++ src/main/java/org/apache/log4j/jmx/RollbackableLoggerManagerModelMBean.java (revision 0) @@ -0,0 +1,113 @@ +package org.apache.log4j.jmx; + +import java.util.ArrayList; +import java.util.List; + +import javax.management.MBeanException; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.RuntimeOperationsException; +import javax.management.modelmbean.ModelMBeanAttributeInfo; +import javax.management.modelmbean.ModelMBeanOperationInfo; + +/** + * Model MBean which makes the methods of RollbackableLoggerManager + * available per JMX. + * @author Stefan Fleiter + * @see RollbackableLoggerManager + */ +public class RollbackableLoggerManagerModelMBean extends LoggerManagerModelMBean { + + /** + * Constructor. + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + public RollbackableLoggerManagerModelMBean() throws MBeanException, RuntimeOperationsException { + this(new RollbackableLoggerManager()); + } + + /** + * Constructor to use an own instance of LoggerManager to represent per JMX. + * @param loggerManager Instance of RollbackableLoggerManager to represent as MBean + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + public RollbackableLoggerManagerModelMBean(RollbackableLoggerManager loggerManager) throws MBeanException, RuntimeOperationsException { + super(loggerManager); + } + + /** + * Method to create a single list of the content of two arrays. + * @param arrayA first Array + * @param arrayB second array + * @return List with content of both input arrays + */ + private List arraysToList(Object[] arrayA, Object[] arrayB) { + List result = new ArrayList(arrayA.length + arrayB.length); + for (int i = 0; i < arrayA.length; i++) { + result.add(arrayA[i]); + } + for (int i = 0; i < arrayB.length; i++) { + result.add(arrayB[i]); + } + return result; + } + + /** + * {@inheritDoc} + */ + protected ModelMBeanOperationInfo[] createModelMBeanOperationInfos() { + ModelMBeanOperationInfo[] modelMBeanOperationInfos = super.createModelMBeanOperationInfos(); + + ModelMBeanOperationInfo[] additionalOperations = new ModelMBeanOperationInfo[] { + createOperationInfo( + "rollbackLogConfiguration", + "Resets all loggers to its' states before calling actions in this MBean", + new MBeanParameterInfo[0], + void.class, + MBeanOperationInfo.ACTION + ), + createOperationInfo( + "rollbackLastLogConfigurationModification", + "Resets last modification to log configuration", + new MBeanParameterInfo[0], + void.class, + MBeanOperationInfo.ACTION + ), + createOperationInfo( + "getLogConfigurationModificationCount", + "Modification count auf log configuration, equals number of possible undo steps", + new MBeanParameterInfo[0], + int.class, + MBeanOperationInfo.INFO + ), + }; + + List result = arraysToList(modelMBeanOperationInfos, additionalOperations); + return (ModelMBeanOperationInfo[]) result.toArray(new ModelMBeanOperationInfo[result.size()]); + } + + /** + * {@inheritDoc} + */ + protected ModelMBeanAttributeInfo[] createModelMBeanAttributeInfos() { + ModelMBeanAttributeInfo[] modelMBeanAttributeInfos = super.createModelMBeanAttributeInfos(); + + ModelMBeanAttributeInfo[] additionalAttributes = new ModelMBeanAttributeInfo[] { + createAttributeInfo( + "logConfigurationModificationCount", + "Modification count auf log configuration, equals number of possible undo steps", + int.class, + true, + false, + false, + "getLogConfigurationModificationCount", + null + ), + }; + List result = arraysToList(modelMBeanAttributeInfos, additionalAttributes); + return (ModelMBeanAttributeInfo[]) result.toArray(new ModelMBeanAttributeInfo[result.size()]); + } + +} Index: src/main/java/org/apache/log4j/jmx/LoggerManagerModelMBean.java =================================================================== --- src/main/java/org/apache/log4j/jmx/LoggerManagerModelMBean.java (revision 0) +++ src/main/java/org/apache/log4j/jmx/LoggerManagerModelMBean.java (revision 0) @@ -0,0 +1,210 @@ +package org.apache.log4j.jmx; + +import java.util.Map; + +import javax.management.MBeanException; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.RuntimeOperationsException; +import javax.management.modelmbean.ModelMBeanAttributeInfo; +import javax.management.modelmbean.ModelMBeanInfo; +import javax.management.modelmbean.ModelMBeanOperationInfo; + +/** + * Model MBean which makes the methods of LoggerManager available per JMX. + * @author Stefan Fleiter + * @see LoggerManager + */ +public class LoggerManagerModelMBean extends AbstractModelMBean { + + /** + * Constructor. + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + public LoggerManagerModelMBean() throws MBeanException, RuntimeOperationsException { + this(new LoggerManager()); + } + + /** + * Constructor to use an own instance of LoggerManager to represent per JMX. + * @param loggerManager Instance of LoggerManager to represent as MBean + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + public LoggerManagerModelMBean(LoggerManager loggerManager) throws MBeanException, RuntimeOperationsException { + super(loggerManager); + setModelMBeanInfo(getModelMBeanInfo()); + try { + setManagedResource(loggerManager, "ObjectReference"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * {@inheritDoc} + */ + protected ModelMBeanOperationInfo[] createModelMBeanOperationInfos() { + ModelMBeanOperationInfo getLogConfiguration = createOperationInfo( + "getLogConfiguration", + "Return the levels of all explicitly configured loggers", + new MBeanParameterInfo[0], + Map.class, + ModelMBeanOperationInfo.INFO + ); + ModelMBeanOperationInfo getConfiguredLoggers = createOperationInfo( + "getConfiguredLoggers", + "Return the names of the explicitly configured loggers", + new MBeanParameterInfo[0], + String[].class, + ModelMBeanOperationInfo.INFO + ); + + + ModelMBeanOperationInfo getRootLogLevel = createOperationInfo( + "getRootLogLevel", + "Return the level of the root logger", + new MBeanParameterInfo[0], + String.class, + MBeanOperationInfo.INFO + ); + ModelMBeanOperationInfo setRootLogLevel = createOperationInfo( + "setRootLogLevel", + "Set the level of the root logger", + new MBeanParameterInfo[] { + new MBeanParameterInfo( + "level", + String.class.getName(), + "New level of root logger (INHERITED to inherit level from next ancestor)" + ), + + }, + void.class, + ModelMBeanOperationInfo.ACTION + ); + + ModelMBeanOperationInfo getLogLevel = createOperationInfo( + "getLogLevel", + "Return the level explicitly set at the specified logger, null if this logger INHERITS its level", + new MBeanParameterInfo[] { + new MBeanParameterInfo( + "logger", + String.class.getName(), + "Name of logger to query level" + ), + }, + String.class, + MBeanOperationInfo.INFO + ); + ModelMBeanOperationInfo getEffectiveLogLevel = createOperationInfo( + "getEffectiveLogLevel", + "Return the effective level of the specified logger " + + "(the explicitly specified level or the level of the next ancestor)", + new MBeanParameterInfo[] { + new MBeanParameterInfo( + "logger", + String.class.getName(), + "Name of logger to query level" + ), + }, + String.class, + MBeanOperationInfo.INFO + ); + ModelMBeanOperationInfo setLogLevel = createOperationInfo( + "setLogLevel", + "Set the level of the specified logger", + new MBeanParameterInfo[] { + new MBeanParameterInfo( + "logger", + String.class.getName(), + "name of logger to modify level" + + ), + new MBeanParameterInfo( + "level", + String.class.getName(), + "new level of specified logger (INHERITED to inherit level from next ancestor)" + ), + }, + void.class, + MBeanOperationInfo.ACTION + ); + + return new ModelMBeanOperationInfo[] { + getLogConfiguration, getConfiguredLoggers, + getRootLogLevel, setRootLogLevel, + getLogLevel, getEffectiveLogLevel, setLogLevel, + }; + } + + /** + * {@inheritDoc} + */ + protected ModelMBeanAttributeInfo[] createModelMBeanAttributeInfos() { + ModelMBeanAttributeInfo rootLoggerAttribute = createAttributeInfo( + "rootLogger", + "Level of root logger", + String.class, + true, + true, + false, + "getRootLogLevel", + "setRootLogLevel" + ); + + ModelMBeanAttributeInfo configuredLoggersAttribute = createAttributeInfo( + "configuredLoggers", + "Names of all loggers with explicitly set log levels", + String[].class, + true, + false, + false, + "getConfiguredLoggers", + null + ); + + ModelMBeanAttributeInfo logConfigurationAttribute = createAttributeInfo( + "logConfiguration", + "Name of all explicitly configured loggers with their log level", + Map.class, + true, + false, + false, + "getLogConfiguration", + null + ); + + return new ModelMBeanAttributeInfo[] { + rootLoggerAttribute, + configuredLoggersAttribute, + logConfigurationAttribute, + }; + } + + /** + * Constructs the ModelMBeanInfo for this Model MBean. + * @return ModelMBeanInfo instance + * @throws MBeanException user Exception of MBean + * @throws RuntimeOperationsException Runtime Exceptions in the MBeanServer + */ + protected ModelMBeanInfo getModelMBeanInfo() throws RuntimeOperationsException, MBeanException { + Class clazz = getDelegate().getClass(); + String displayName = "Log4jManager"; + String description = "Model MBean for Manager Logger Configuration of Log4j"; + String mbeanName = "log4j:manager"; + return createModelMBeanInfo(displayName, description, clazz, mbeanName); + } + + /** + * Calls LoggerManager#destroy(). + * @see LoggerManager#destroy() + * {@inheritDoc}} + */ + public void postDeregister() { + ((LoggerManager) getDelegate()).destroy(); + super.postDeregister(); + } + + +}