001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.log4j.jmx;
019
020
021import org.apache.log4j.Appender;
022import org.apache.log4j.Category;
023import org.apache.log4j.Level;
024import org.apache.log4j.LogManager;
025import org.apache.log4j.Logger;
026import org.apache.log4j.helpers.OptionConverter;
027import org.apache.log4j.spi.HierarchyEventListener;
028import org.apache.log4j.spi.LoggerRepository;
029
030import javax.management.Attribute;
031import javax.management.AttributeNotFoundException;
032import javax.management.InvalidAttributeValueException;
033import javax.management.JMException;
034import javax.management.ListenerNotFoundException;
035import javax.management.MBeanAttributeInfo;
036import javax.management.MBeanConstructorInfo;
037import javax.management.MBeanException;
038import javax.management.MBeanInfo;
039import javax.management.MBeanNotificationInfo;
040import javax.management.MBeanOperationInfo;
041import javax.management.MBeanParameterInfo;
042import javax.management.Notification;
043import javax.management.NotificationBroadcaster;
044import javax.management.NotificationBroadcasterSupport;
045import javax.management.NotificationFilter;
046import javax.management.NotificationFilterSupport;
047import javax.management.NotificationListener;
048import javax.management.ObjectName;
049import javax.management.ReflectionException;
050import javax.management.RuntimeOperationsException;
051import java.lang.reflect.Constructor;
052import java.util.Vector;
053
054public class HierarchyDynamicMBean extends AbstractDynamicMBean
055                                   implements HierarchyEventListener,
056                                              NotificationBroadcaster {
057
058  static final String ADD_APPENDER = "addAppender.";
059  static final String THRESHOLD = "threshold";
060
061  private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
062  private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
063
064  private Vector vAttributes = new Vector();
065  private String dClassName = this.getClass().getName();
066  private String dDescription =
067     "This MBean acts as a management facade for org.apache.log4j.Hierarchy.";
068
069  private NotificationBroadcasterSupport nbs = new NotificationBroadcasterSupport();
070
071
072  private LoggerRepository hierarchy;
073
074  private static Logger log = Logger.getLogger(HierarchyDynamicMBean.class);
075
076  public HierarchyDynamicMBean() {
077    hierarchy = LogManager.getLoggerRepository();
078    buildDynamicMBeanInfo();
079  }
080
081  private
082  void buildDynamicMBeanInfo() {
083    Constructor[] constructors = this.getClass().getConstructors();
084    dConstructors[0] = new MBeanConstructorInfo(
085         "HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance",
086         constructors[0]);
087
088    vAttributes.add(new MBeanAttributeInfo(THRESHOLD,
089                                           "java.lang.String",
090                                           "The \"threshold\" state of the hiearchy.",
091                                           true,
092                                           true,
093                                           false));
094
095    MBeanParameterInfo[] params = new MBeanParameterInfo[1];
096    params[0] = new MBeanParameterInfo("name", "java.lang.String",
097                                       "Create a logger MBean" );
098    dOperations[0] = new MBeanOperationInfo("addLoggerMBean",
099                                    "addLoggerMBean(): add a loggerMBean",
100                                    params ,
101                                    "javax.management.ObjectName",
102                                    MBeanOperationInfo.ACTION);
103  }
104
105
106  public
107  ObjectName addLoggerMBean(String name) {
108    Logger cat = LogManager.exists(name);
109
110    if(cat != null) {
111      return addLoggerMBean(cat);
112    } else {
113      return null;
114    }
115  }
116
117  ObjectName addLoggerMBean(Logger logger) {
118    String name = logger.getName();
119    ObjectName objectName = null;
120    try {
121      LoggerDynamicMBean loggerMBean = new LoggerDynamicMBean(logger);
122      objectName = new ObjectName("log4j", "logger", name);
123      
124      if (!server.isRegistered(objectName)) {
125        registerMBean(loggerMBean, objectName);
126        NotificationFilterSupport nfs = new NotificationFilterSupport();
127        nfs.enableType(ADD_APPENDER + logger.getName());
128        log.debug("---Adding logger [" + name + "] as listener.");
129        nbs.addNotificationListener(loggerMBean, nfs, null);
130        vAttributes.add(new MBeanAttributeInfo("logger=" + name, "javax.management.ObjectName",
131                "The " + name + " logger.", true, true, // this makes the object
132                // clickable
133                false));
134        
135      }
136
137    } catch(JMException e) {
138      log.error("Could not add loggerMBean for ["+name+"].", e);
139    } catch(RuntimeException e) {
140      log.error("Could not add loggerMBean for ["+name+"].", e);
141    }
142    return objectName;
143  }
144
145  public
146  void addNotificationListener(NotificationListener listener,
147                               NotificationFilter filter,
148                               java.lang.Object handback) {
149    nbs.addNotificationListener(listener, filter, handback);
150  }
151
152  protected
153  Logger getLogger() {
154    return log;
155  }
156
157  public
158  MBeanInfo getMBeanInfo() {
159    //cat.debug("getMBeanInfo called.");
160
161    MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[vAttributes.size()];
162    vAttributes.toArray(attribs);
163
164    return new MBeanInfo(dClassName,
165                         dDescription,
166                         attribs,
167                         dConstructors,
168                         dOperations,
169                         new MBeanNotificationInfo[0]);
170  }
171
172  public
173  MBeanNotificationInfo[] getNotificationInfo(){
174    return nbs.getNotificationInfo();
175  }
176
177  public
178  Object invoke(String operationName,
179                Object params[],
180                String signature[]) throws MBeanException,
181                                           ReflectionException {
182
183    if (operationName == null) {
184      throw new RuntimeOperationsException(
185        new IllegalArgumentException("Operation name cannot be null"),
186        "Cannot invoke a null operation in " + dClassName);
187    }
188    // Check for a recognized operation name and call the corresponding operation
189
190    if(operationName.equals("addLoggerMBean")) {
191      return addLoggerMBean((String)params[0]);
192    } else {
193      throw new ReflectionException(
194            new NoSuchMethodException(operationName),
195            "Cannot find the operation " + operationName + " in " + dClassName);
196    }
197
198  }
199
200
201  public
202  Object getAttribute(String attributeName) throws AttributeNotFoundException,
203                                                    MBeanException,
204                                                    ReflectionException {
205
206    // Check attributeName is not null to avoid NullPointerException later on
207    if (attributeName == null) {
208      throw new RuntimeOperationsException(new IllegalArgumentException(
209                        "Attribute name cannot be null"),
210       "Cannot invoke a getter of " + dClassName + " with null attribute name");
211    }
212
213    log.debug("Called getAttribute with ["+attributeName+"].");
214
215    // Check for a recognized attributeName and call the corresponding getter
216    if (attributeName.equals(THRESHOLD)) {
217      return hierarchy.getThreshold();
218    } else if(attributeName.startsWith("logger")) {
219      int k = attributeName.indexOf("%3D");
220      String val = attributeName;
221      if(k > 0) {
222        val = attributeName.substring(0, k)+'='+ attributeName.substring(k+3);
223      }
224      try {
225        return new ObjectName("log4j:"+val);
226      } catch(JMException e) {
227            log.error("Could not create ObjectName" + val);
228      } catch(RuntimeException e) {
229            log.error("Could not create ObjectName" + val);
230      }
231    }
232
233
234
235    // If attributeName has not been recognized throw an AttributeNotFoundException
236    throw(new AttributeNotFoundException("Cannot find " + attributeName +
237                                         " attribute in " + dClassName));
238
239  }
240
241
242  public
243  void addAppenderEvent(Category logger, Appender appender) {
244    log.debug("addAppenderEvent called: logger="+logger.getName()+
245              ", appender="+appender.getName());
246    Notification n = new Notification(ADD_APPENDER+logger.getName(), this, 0);
247    n.setUserData(appender);
248    log.debug("sending notification.");
249    nbs.sendNotification(n);
250  }
251
252 public
253  void removeAppenderEvent(Category cat, Appender appender) {
254    log.debug("removeAppenderCalled: logger="+cat.getName()+
255              ", appender="+appender.getName());
256  }
257
258  public
259  void postRegister(java.lang.Boolean registrationDone) {
260    log.debug("postRegister is called.");
261    hierarchy.addHierarchyEventListener(this);
262    Logger root = hierarchy.getRootLogger();
263    addLoggerMBean(root);
264  }
265
266  public
267  void removeNotificationListener(NotificationListener listener)
268                                         throws ListenerNotFoundException {
269    nbs.removeNotificationListener(listener);
270  }
271
272  public
273  void setAttribute(Attribute attribute) throws AttributeNotFoundException,
274                                                InvalidAttributeValueException,
275                                                MBeanException,
276                                                ReflectionException {
277
278    // Check attribute is not null to avoid NullPointerException later on
279    if (attribute == null) {
280      throw new RuntimeOperationsException(
281                  new IllegalArgumentException("Attribute cannot be null"),
282          "Cannot invoke a setter of "+dClassName+" with null attribute");
283    }
284    String name = attribute.getName();
285    Object value = attribute.getValue();
286
287    if (name == null) {
288      throw new RuntimeOperationsException(
289               new IllegalArgumentException("Attribute name cannot be null"),
290               "Cannot invoke the setter of "+dClassName+
291               " with null attribute name");
292    }
293
294    if(name.equals(THRESHOLD)) {
295      Level l = OptionConverter.toLevel((String) value,
296                                           hierarchy.getThreshold());
297      hierarchy.setThreshold(l);
298    }
299
300
301  }
302}