View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.jmx;
19  
20  import org.apache.log4j.Appender;
21  import org.apache.log4j.Layout;
22  import org.apache.log4j.Level;
23  import org.apache.log4j.Logger;
24  import org.apache.log4j.Priority;
25  import org.apache.log4j.helpers.OptionConverter;
26  import org.apache.log4j.spi.OptionHandler;
27  
28  import javax.management.Attribute;
29  import javax.management.AttributeNotFoundException;
30  import javax.management.InvalidAttributeValueException;
31  import javax.management.JMException;
32  import javax.management.MBeanAttributeInfo;
33  import javax.management.MBeanConstructorInfo;
34  import javax.management.MBeanException;
35  import javax.management.MBeanInfo;
36  import javax.management.MBeanNotificationInfo;
37  import javax.management.MBeanOperationInfo;
38  import javax.management.MBeanParameterInfo;
39  import javax.management.MBeanServer;
40  import javax.management.MalformedObjectNameException;
41  import javax.management.ObjectName;
42  import javax.management.ReflectionException;
43  import javax.management.RuntimeOperationsException;
44  import java.beans.BeanInfo;
45  import java.beans.IntrospectionException;
46  import java.beans.Introspector;
47  import java.beans.PropertyDescriptor;
48  import java.lang.reflect.Constructor;
49  import java.lang.reflect.InvocationTargetException;
50  import java.lang.reflect.Method;
51  import java.util.Hashtable;
52  import java.util.Vector;
53  import java.io.InterruptedIOException;
54  
55  public class AppenderDynamicMBean extends AbstractDynamicMBean {
56  
57    private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
58    private Vector dAttributes = new Vector();
59    private String dClassName = this.getClass().getName();
60  
61    private Hashtable dynamicProps = new Hashtable(5);
62    private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[2];
63    private String dDescription =
64       "This MBean acts as a management facade for log4j appenders.";
65  
66    // This category instance is for logging.
67    private static Logger cat = Logger.getLogger(AppenderDynamicMBean.class);
68  
69    // We wrap this appender instance.
70    private Appender appender;
71  
72    public  AppenderDynamicMBean(Appender appender) throws IntrospectionException {
73      this.appender = appender;
74      buildDynamicMBeanInfo();
75    }
76  
77    private
78    void buildDynamicMBeanInfo() throws IntrospectionException {
79      Constructor[] constructors = this.getClass().getConstructors();
80      dConstructors[0] = new MBeanConstructorInfo(
81               "AppenderDynamicMBean(): Constructs a AppenderDynamicMBean instance",
82  	     constructors[0]);
83  
84  
85      BeanInfo bi = Introspector.getBeanInfo(appender.getClass());
86      PropertyDescriptor[] pd = bi.getPropertyDescriptors();
87  
88      int size = pd.length;
89  
90      for(int i = 0; i < size; i++) {
91        String name = pd[i].getName();
92        Method readMethod =  pd[i].getReadMethod();
93        Method writeMethod =  pd[i].getWriteMethod();
94        if(readMethod != null) {
95  	Class returnClass = readMethod.getReturnType();
96  	if(isSupportedType(returnClass)) {
97  	  String returnClassName;
98  	  if(returnClass.isAssignableFrom(Priority.class)) {
99  	    returnClassName = "java.lang.String";
100 	  } else {
101 	    returnClassName = returnClass.getName();
102 	  }
103 
104 	  dAttributes.add(new MBeanAttributeInfo(name,
105 						 returnClassName,
106 						 "Dynamic",
107 						 true,
108 						 writeMethod != null,
109 						 false));
110 	  dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
111 	}
112       }
113     }
114 
115     MBeanParameterInfo[] params = new MBeanParameterInfo[0];
116 
117     dOperations[0] = new MBeanOperationInfo("activateOptions",
118 					    "activateOptions(): add an appender",
119 					    params,
120 					    "void",
121 					    MBeanOperationInfo.ACTION);
122 
123     params = new MBeanParameterInfo[1];
124     params[0] = new MBeanParameterInfo("layout class", "java.lang.String",
125 				       "layout class");
126 
127     dOperations[1] = new MBeanOperationInfo("setLayout",
128 					    "setLayout(): add a layout",
129 					    params,
130 					    "void",
131 					    MBeanOperationInfo.ACTION);
132   }
133 
134   private
135   boolean isSupportedType(Class clazz) {
136     if(clazz.isPrimitive()) {
137       return true;
138     }
139 
140     if(clazz == String.class) {
141       return true;
142     }
143 
144 
145     if(clazz.isAssignableFrom(Priority.class)) {
146       return true;
147     }
148 
149     return false;
150 
151 
152   }
153 
154 
155 
156   public
157   MBeanInfo getMBeanInfo() {
158     cat.debug("getMBeanInfo called.");
159 
160     MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
161     dAttributes.toArray(attribs);
162 
163     return new MBeanInfo(dClassName,
164 			 dDescription,
165 			 attribs,
166 			 dConstructors,
167 			 dOperations,
168 			 new MBeanNotificationInfo[0]);
169   }
170 
171   public
172   Object invoke(String operationName, Object params[], String signature[])
173     throws MBeanException,
174     ReflectionException {
175 
176     if(operationName.equals("activateOptions") &&
177                      appender instanceof OptionHandler) {
178       OptionHandler oh = (OptionHandler) appender;
179       oh.activateOptions();
180       return "Options activated.";
181     } else if (operationName.equals("setLayout")) {
182       Layout layout = (Layout) OptionConverter.instantiateByClassName((String)
183 								      params[0],
184 								      Layout.class,
185 								      null);
186       appender.setLayout(layout);
187       registerLayoutMBean(layout);
188     }
189     return null;
190   }
191 
192   void registerLayoutMBean(Layout layout) {
193     if(layout == null)
194       return;
195 
196     String name = getAppenderName(appender)+",layout="+layout.getClass().getName();
197     cat.debug("Adding LayoutMBean:"+name);
198     ObjectName objectName = null;
199     try {
200       LayoutDynamicMBean appenderMBean = new LayoutDynamicMBean(layout);
201       objectName = new ObjectName("log4j:appender="+name);
202       if (!server.isRegistered(objectName)) {
203         registerMBean(appenderMBean, objectName);
204         dAttributes.add(new MBeanAttributeInfo("appender=" + name, "javax.management.ObjectName",
205                 "The " + name + " layout.", true, true, false));
206       }
207 
208     } catch(JMException e) {
209       cat.error("Could not add DynamicLayoutMBean for ["+name+"].", e);
210     } catch(java.beans.IntrospectionException e) {
211       cat.error("Could not add DynamicLayoutMBean for ["+name+"].", e);
212     } catch(RuntimeException e) {
213       cat.error("Could not add DynamicLayoutMBean for ["+name+"].", e);
214     }
215   }
216 
217   protected
218   Logger getLogger() {
219     return cat;
220   }
221 
222 
223   public
224   Object getAttribute(String attributeName) throws AttributeNotFoundException,
225                                                    MBeanException,
226                                                    ReflectionException {
227 
228        // Check attributeName is not null to avoid NullPointerException later on
229     if (attributeName == null) {
230       throw new RuntimeOperationsException(new IllegalArgumentException(
231 			"Attribute name cannot be null"),
232        "Cannot invoke a getter of " + dClassName + " with null attribute name");
233     }
234 
235     cat.debug("getAttribute called with ["+attributeName+"].");
236     if(attributeName.startsWith("appender="+appender.getName()+",layout")) {
237       try {
238 	    return new ObjectName("log4j:"+attributeName );
239       } catch(MalformedObjectNameException e) {
240 	    cat.error("attributeName", e);
241       } catch(RuntimeException e) {
242 	    cat.error("attributeName", e);
243       }
244     }
245 
246     MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
247 
248     //cat.debug("----name="+attributeName+", b="+b);
249 
250     if(mu != null && mu.readMethod != null) {
251       try {
252 	return mu.readMethod.invoke(appender, null);
253       } catch(IllegalAccessException e) {
254 	    return null;
255       } catch(InvocationTargetException e) {
256           if (e.getTargetException() instanceof InterruptedException
257                   || e.getTargetException() instanceof InterruptedIOException) {
258               Thread.currentThread().interrupt();
259           }
260 	    return null;
261       } catch(RuntimeException e) {
262 	    return null;
263       }
264     }
265 
266 
267 
268     // If attributeName has not been recognized throw an AttributeNotFoundException
269     throw(new AttributeNotFoundException("Cannot find " + attributeName +
270 					 " attribute in " + dClassName));
271 
272   }
273 
274 
275   public
276   void setAttribute(Attribute attribute) throws AttributeNotFoundException,
277                                                 InvalidAttributeValueException,
278                                                 MBeanException,
279                                                 ReflectionException {
280 
281     // Check attribute is not null to avoid NullPointerException later on
282     if (attribute == null) {
283       throw new RuntimeOperationsException(
284                   new IllegalArgumentException("Attribute cannot be null"),
285 		  "Cannot invoke a setter of " + dClassName +
286 		  " with null attribute");
287     }
288     String name = attribute.getName();
289     Object value = attribute.getValue();
290 
291     if (name == null) {
292       throw new RuntimeOperationsException(
293                     new IllegalArgumentException("Attribute name cannot be null"),
294 		    "Cannot invoke the setter of "+dClassName+
295 		    " with null attribute name");
296     }
297 
298 
299 
300     MethodUnion mu = (MethodUnion) dynamicProps.get(name);
301 
302     if(mu != null && mu.writeMethod != null) {
303       Object[] o = new Object[1];
304 
305       Class[] params = mu.writeMethod.getParameterTypes();
306       if(params[0] == org.apache.log4j.Priority.class) {
307 	value = OptionConverter.toLevel((String) value,
308 					(Level) getAttribute(name));
309       }
310       o[0] = value;
311 
312       try {
313 	mu.writeMethod.invoke(appender,  o);
314 
315       } catch(InvocationTargetException e) {
316         if (e.getTargetException() instanceof InterruptedException
317                 || e.getTargetException() instanceof InterruptedIOException) {
318             Thread.currentThread().interrupt();
319         }
320         cat.error("FIXME", e);
321       } catch(IllegalAccessException e) {
322 	    cat.error("FIXME", e);
323       } catch(RuntimeException e) {
324 	    cat.error("FIXME", e);
325       }
326     } else if(name.endsWith(".layout")) {
327 
328     } else {
329       throw(new AttributeNotFoundException("Attribute " + name +
330 					   " not found in " +
331 					   this.getClass().getName()));
332     }
333   }
334 
335   public
336   ObjectName preRegister(MBeanServer server, ObjectName name) {
337     cat.debug("preRegister called. Server="+server+ ", name="+name);
338     this.server = server;
339     registerLayoutMBean(appender.getLayout());
340 
341     return name;
342   }
343 
344 
345 }
346