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 package org.apache.commons.modeler.modules; 018 019 import java.io.FileNotFoundException; 020 import java.io.FileOutputStream; 021 import java.io.InputStream; 022 import java.net.URL; 023 import java.util.ArrayList; 024 import java.util.HashMap; 025 import java.util.List; 026 027 import javax.management.Attribute; 028 import javax.management.MBeanServer; 029 import javax.management.ObjectName; 030 import javax.management.loading.MLet; 031 import javax.xml.transform.TransformerException; 032 033 import org.apache.commons.logging.Log; 034 import org.apache.commons.logging.LogFactory; 035 import org.apache.commons.modeler.AttributeInfo; 036 import org.apache.commons.modeler.BaseModelMBean; 037 import org.apache.commons.modeler.ManagedBean; 038 import org.apache.commons.modeler.Registry; 039 import org.apache.commons.modeler.util.DomUtil; 040 import org.w3c.dom.Document; 041 import org.w3c.dom.Node; 042 043 044 /** This will create mbeans based on a config file. 045 * The format is an extended version of MLET. 046 * 047 * Classloading. We don't support any explicit classloader tag. 048 * A ClassLoader is just an mbean ( it can be the standard MLetMBean or 049 * a custom one ). 050 * 051 * XXX add a special attribute to reference the loader mbean, 052 * XXX figure out how to deal with private loaders 053 */ 054 public class MbeansSource extends ModelerSource implements MbeansSourceMBean 055 { 056 private static Log log = LogFactory.getLog(MbeansSource.class); 057 Registry registry; 058 String type; 059 060 // true if we are during the original loading 061 boolean loading=true; 062 List mbeans=new ArrayList(); 063 static boolean loaderLoaded=false; 064 private Document document; 065 private HashMap object2Node = new HashMap(); 066 067 long lastUpdate; 068 long updateInterval=10000; // 10s 069 070 public void setRegistry(Registry reg) { 071 this.registry=reg; 072 } 073 074 public void setLocation( String loc ) { 075 this.location=loc; 076 } 077 078 /** Used if a single component is loaded 079 * 080 * @param type 081 */ 082 public void setType( String type ) { 083 this.type=type; 084 } 085 086 public void setSource( Object source ) { 087 this.source=source; 088 } 089 090 public Object getSource() { 091 return source; 092 } 093 094 public String getLocation() { 095 return location; 096 } 097 098 /** Return the list of mbeans created by this source. 099 * It can be used to implement runtime services. 100 */ 101 public List getMBeans() { 102 return mbeans; 103 } 104 105 public List loadDescriptors( Registry registry, String location, 106 String type, Object source) 107 throws Exception 108 { 109 setRegistry(registry); 110 setLocation(location); 111 setType(type); 112 setSource(source); 113 execute(); 114 return mbeans; 115 } 116 117 public void start() throws Exception { 118 registry.invoke(mbeans, "start", false); 119 } 120 121 public void stop() throws Exception { 122 registry.invoke(mbeans, "stop", false); 123 } 124 125 public void init() throws Exception { 126 if( mbeans==null) execute(); 127 if( registry==null ) registry=Registry.getRegistry(); 128 129 registry.invoke(mbeans, "init", false); 130 } 131 132 public void destroy() throws Exception { 133 registry.invoke(mbeans, "destroy", false); 134 } 135 136 public void load() throws Exception { 137 execute(); // backward compat 138 } 139 140 public void execute() throws Exception { 141 if( registry==null ) registry=Registry.getRegistry(); 142 try { 143 InputStream stream=getInputStream(); 144 long t1=System.currentTimeMillis(); 145 document = DomUtil.readXml(stream); 146 147 // We don't care what the root node is. 148 Node descriptorsN=document.getDocumentElement(); 149 150 if( descriptorsN == null ) { 151 log.error("No descriptors found"); 152 return; 153 } 154 155 Node firstMbeanN=DomUtil.getChild(descriptorsN, null); 156 157 if( firstMbeanN==null ) { 158 // maybe we have a single mlet 159 if( log.isDebugEnabled() ) 160 log.debug("No child " + descriptorsN); 161 firstMbeanN=descriptorsN; 162 } 163 164 MBeanServer server=(MBeanServer)Registry.getServer(); 165 166 // XXX Not very clean... Just a workaround 167 if( ! loaderLoaded ) { 168 // Register a loader that will be find ant classes. 169 ObjectName defaultLoader= new ObjectName("modeler", 170 "loader", "modeler"); 171 MLet mlet=new MLet( new URL[0], this.getClass().getClassLoader()); 172 server.registerMBean(mlet, defaultLoader); 173 loaderLoaded=true; 174 } 175 176 // Process nodes 177 for (Node mbeanN = firstMbeanN; mbeanN != null; 178 mbeanN= DomUtil.getNext(mbeanN, null, Node.ELEMENT_NODE)) 179 { 180 String nodeName=mbeanN.getNodeName(); 181 182 // mbean is the "official" name 183 if( "mbean".equals(nodeName) || "MLET".equals(nodeName) ) 184 { 185 String code=DomUtil.getAttribute( mbeanN, "code" ); 186 String objectName=DomUtil.getAttribute( mbeanN, "objectName" ); 187 if( objectName==null ) { 188 objectName=DomUtil.getAttribute( mbeanN, "name" ); 189 } 190 191 if( log.isDebugEnabled()) 192 log.debug( "Processing mbean objectName=" + objectName + 193 " code=" + code); 194 195 // args can be grouped in constructor or direct childs 196 Node constructorN=DomUtil.getChild(mbeanN, "constructor"); 197 if( constructorN == null ) constructorN=mbeanN; 198 199 ArgsInfo info = processArg(constructorN); 200 201 try { 202 ObjectName oname=new ObjectName(objectName); 203 if( ! server.isRegistered( oname )) { 204 // We wrap everything in a model mbean. 205 // XXX need to support "StandardMBeanDescriptorsSource" 206 String modelMBean=BaseModelMBean.class.getName(); 207 if(info == null) { 208 server.createMBean(modelMBean, oname, 209 new Object[] { code, this}, 210 new String[] { String.class.getName(), 211 ModelerSource.class.getName() } 212 ); 213 } else { 214 server.createMBean(modelMBean, oname, 215 new Object[] { code, this, 216 info.getValues(), 217 info.getSigs() 218 }, 219 new String[] { String.class.getName(), 220 ModelerSource.class.getName(), 221 Object[].class.getName(), 222 String[].class.getName() 223 } 224 ); 225 } 226 227 mbeans.add(oname); 228 } 229 object2Node.put( oname, mbeanN ); 230 // XXX Arguments, loader !!! 231 } catch( Exception ex ) { 232 log.error( "Error creating mbean " + objectName, ex); 233 } 234 235 Node firstAttN=DomUtil.getChild(mbeanN, "attribute"); 236 for (Node descN = firstAttN; descN != null; 237 descN = DomUtil.getNext( descN )) 238 { 239 processAttribute(server, descN, objectName); 240 } 241 } else if("jmx-operation".equals(nodeName) ) { 242 String name=DomUtil.getAttribute(mbeanN, "objectName"); 243 if( name==null ) 244 name=DomUtil.getAttribute(mbeanN, "name"); 245 246 String operation=DomUtil.getAttribute(mbeanN, "operation"); 247 248 if( log.isDebugEnabled()) 249 log.debug( "Processing invoke objectName=" + name + 250 " code=" + operation); 251 try { 252 ObjectName oname=new ObjectName(name); 253 254 ArgsInfo info = processArg( mbeanN ); 255 if(info == null) { 256 server.invoke( oname, operation, null, null); 257 } else { 258 server.invoke( oname, operation, info.getValues(), info.getSigs()); 259 } 260 } catch (Exception e) { 261 log.error( "Error in invoke " + name + " " + operation); 262 } 263 } 264 265 ManagedBean managed=new ManagedBean(); 266 DomUtil.setAttributes(managed, mbeanN); 267 Node firstN; 268 269 // process attribute info 270 firstN=DomUtil.getChild( mbeanN, "attribute"); 271 for (Node descN = firstN; descN != null; 272 descN = DomUtil.getNext( descN )) 273 { 274 AttributeInfo ci=new AttributeInfo(); 275 DomUtil.setAttributes(ci, descN); 276 managed.addAttribute( ci ); 277 } 278 279 } 280 281 long t2=System.currentTimeMillis(); 282 log.info( "Reading mbeans " + (t2-t1)); 283 loading=false; 284 } catch( Exception ex ) { 285 log.error( "Error reading mbeans ", ex); 286 } 287 } 288 289 public void updateField( ObjectName oname, String name, 290 Object value ) 291 { 292 if( loading ) return; 293 // nothing by default 294 //log.info( "XXX UpdateField " + oname + " " + name + " " + value); 295 Node n=(Node)object2Node.get( oname ); 296 if( n == null ) { 297 log.info( "Node not found " + oname ); 298 return; 299 } 300 Node attNode=DomUtil.findChildWithAtt(n, "attribute", "name", name); 301 if( attNode == null ) { 302 // found no existing attribute with this name 303 attNode=n.getOwnerDocument().createElement("attribute"); 304 DomUtil.setAttribute(attNode, "name", name); 305 n.appendChild(attNode); 306 } 307 String oldValue=DomUtil.getAttribute(attNode, "value"); 308 if( oldValue != null ) { 309 // we'll convert all values to text content 310 DomUtil.removeAttribute( attNode, "value"); 311 } 312 DomUtil.setText(attNode, value.toString()); 313 314 //store(); 315 } 316 317 /** Store the mbeans. 318 * XXX add a background thread to store it periodically 319 */ 320 public void save() { 321 // XXX customize no often than ( based on standard descriptor ), etc. 322 // It doesn't work very well if we call this on each set att - 323 // the triger will work for the first att, but all others will be delayed 324 long time=System.currentTimeMillis(); 325 if( location!=null && 326 time - lastUpdate > updateInterval ) { 327 lastUpdate=time; 328 try { 329 FileOutputStream fos=new FileOutputStream(location); 330 DomUtil.writeXml(document, fos); 331 } catch (TransformerException e) { 332 log.error( "Error writing"); 333 } catch (FileNotFoundException e) { 334 log.error( "Error writing" ,e ); 335 } 336 } 337 } 338 339 private void processAttribute(MBeanServer server, 340 Node descN, String objectName ) { 341 String attName=DomUtil.getAttribute(descN, "name"); 342 String value=DomUtil.getAttribute(descN, "value"); 343 String type=null; // DomUtil.getAttribute(descN, "type"); 344 if( value==null ) { 345 // The value may be specified as CDATA 346 value=DomUtil.getContent(descN); 347 } 348 try { 349 if( log.isDebugEnabled()) 350 log.debug("Set attribute " + objectName + " " + attName + 351 " " + value); 352 ObjectName oname=new ObjectName(objectName); 353 // find the type 354 if( type==null ) 355 type=registry.getType( oname, attName ); 356 357 if( type==null ) { 358 log.info("Can't find attribute " + objectName + " " + attName ); 359 360 } else { 361 Object valueO=registry.convertValue( type, value); 362 server.setAttribute(oname, new Attribute(attName, valueO)); 363 } 364 } catch( Exception ex) { 365 log.error("Error processing attribute " + objectName + " " + 366 attName + " " + value, ex); 367 } 368 369 } 370 371 private ArgsInfo processArg(Node mbeanN) { 372 Node firstArgN=DomUtil.getChild(mbeanN, "arg" ); 373 if(firstArgN == null) { 374 return null; 375 } 376 ArgsInfo info = new ArgsInfo(); 377 // process all args 378 for (Node argN = firstArgN; argN != null; 379 argN = DomUtil.getNext( argN )) 380 { 381 String type=DomUtil.getAttribute(argN, "type"); 382 String value=DomUtil.getAttribute(argN, "value"); 383 if( value==null ) { 384 // The value may be specified as CDATA 385 value=DomUtil.getContent(argN); 386 } 387 info.addArgPair(type, registry.convertValue(type,value)); 388 } 389 return info; 390 } 391 392 private static class ArgsInfo { 393 private List sigs = new ArrayList(); 394 private List values = new ArrayList(); 395 396 ArgsInfo() { 397 } 398 399 public String [] getSigs() { 400 return (String [])sigs.toArray(new String[sigs.size()]); 401 } 402 403 public Object[] getValues () { 404 return values.toArray(new Object[values.size()]); 405 } 406 407 public void addArgPair(String name, Object val) { 408 sigs.add(name); 409 values.add(val); 410 } 411 } 412 }