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 020import org.apache.log4j.Appender; 021import org.apache.log4j.Layout; 022import org.apache.log4j.Level; 023import org.apache.log4j.Logger; 024import org.apache.log4j.Priority; 025import org.apache.log4j.helpers.OptionConverter; 026import org.apache.log4j.spi.OptionHandler; 027 028import javax.management.Attribute; 029import javax.management.AttributeNotFoundException; 030import javax.management.InvalidAttributeValueException; 031import javax.management.JMException; 032import javax.management.MBeanAttributeInfo; 033import javax.management.MBeanConstructorInfo; 034import javax.management.MBeanException; 035import javax.management.MBeanInfo; 036import javax.management.MBeanNotificationInfo; 037import javax.management.MBeanOperationInfo; 038import javax.management.MBeanParameterInfo; 039import javax.management.MBeanServer; 040import javax.management.MalformedObjectNameException; 041import javax.management.ObjectName; 042import javax.management.ReflectionException; 043import javax.management.RuntimeOperationsException; 044import java.beans.BeanInfo; 045import java.beans.IntrospectionException; 046import java.beans.Introspector; 047import java.beans.PropertyDescriptor; 048import java.lang.reflect.Constructor; 049import java.lang.reflect.InvocationTargetException; 050import java.lang.reflect.Method; 051import java.util.Hashtable; 052import java.util.Vector; 053import java.io.InterruptedIOException; 054 055public class AppenderDynamicMBean extends AbstractDynamicMBean { 056 057 private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1]; 058 private Vector dAttributes = new Vector(); 059 private String dClassName = this.getClass().getName(); 060 061 private Hashtable dynamicProps = new Hashtable(5); 062 private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[2]; 063 private String dDescription = 064 "This MBean acts as a management facade for log4j appenders."; 065 066 // This category instance is for logging. 067 private static Logger cat = Logger.getLogger(AppenderDynamicMBean.class); 068 069 // We wrap this appender instance. 070 private Appender appender; 071 072 public AppenderDynamicMBean(Appender appender) throws IntrospectionException { 073 this.appender = appender; 074 buildDynamicMBeanInfo(); 075 } 076 077 private 078 void buildDynamicMBeanInfo() throws IntrospectionException { 079 Constructor[] constructors = this.getClass().getConstructors(); 080 dConstructors[0] = new MBeanConstructorInfo( 081 "AppenderDynamicMBean(): Constructs a AppenderDynamicMBean instance", 082 constructors[0]); 083 084 085 BeanInfo bi = Introspector.getBeanInfo(appender.getClass()); 086 PropertyDescriptor[] pd = bi.getPropertyDescriptors(); 087 088 int size = pd.length; 089 090 for(int i = 0; i < size; i++) { 091 String name = pd[i].getName(); 092 Method readMethod = pd[i].getReadMethod(); 093 Method writeMethod = pd[i].getWriteMethod(); 094 if(readMethod != null) { 095 Class returnClass = readMethod.getReturnType(); 096 if(isSupportedType(returnClass)) { 097 String returnClassName; 098 if(returnClass.isAssignableFrom(Priority.class)) { 099 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