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