View Javadoc

1   package org.apache.commons.modeler.modules;
2   
3   import java.io.FileNotFoundException;
4   import java.io.FileOutputStream;
5   import java.io.InputStream;
6   import java.net.URL;
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.List;
10  
11  import javax.management.Attribute;
12  import javax.management.MBeanServer;
13  import javax.management.ObjectName;
14  import javax.management.loading.MLet;
15  import javax.xml.transform.TransformerException;
16  
17  import org.apache.commons.logging.Log;
18  import org.apache.commons.logging.LogFactory;
19  import org.apache.commons.modeler.AttributeInfo;
20  import org.apache.commons.modeler.BaseModelMBean;
21  import org.apache.commons.modeler.ManagedBean;
22  import org.apache.commons.modeler.Registry;
23  import org.apache.commons.modeler.util.DomUtil;
24  import org.w3c.dom.Document;
25  import org.w3c.dom.Node;
26  
27  
28  /*** This will create mbeans based on a config file.
29   *  The format is an extended version of MLET.
30   *
31   * Classloading. We don't support any explicit classloader tag. 
32   * A ClassLoader is just an mbean ( it can be the standard MLetMBean or
33   * a custom one ). 
34   * 
35   * XXX add a special attribute to reference the loader mbean,
36   * XXX figure out how to deal with private loaders
37   */
38  public class MbeansSource extends ModelerSource implements MbeansSourceMBean
39  {
40      private static Log log = LogFactory.getLog(MbeansSource.class);
41      Registry registry;
42      String type;
43  
44      // true if we are during the original loading
45      boolean loading=true;
46      List mbeans=new ArrayList();
47      static boolean loaderLoaded=false;
48      private Document document;
49      private HashMap object2Node = new HashMap();
50  
51      long lastUpdate;
52      long updateInterval=10000; // 10s
53  
54      public void setRegistry(Registry reg) {
55          this.registry=reg;
56      }          
57  
58      public void setLocation( String loc ) {
59          this.location=loc;
60      }
61  
62      /*** Used if a single component is loaded
63       *
64       * @param type
65       */
66      public void setType( String type ) {
67         this.type=type;
68      }
69  
70      public void setSource( Object source ) {
71          this.source=source;
72      }
73  
74      public Object getSource() {
75          return source;
76      }
77  
78      public String getLocation() {
79          return location;
80      }
81      
82      /*** Return the list of mbeans created by this source.
83       *  It can be used to implement runtime services.
84       */
85      public List getMBeans() {
86          return mbeans;
87      }
88  
89      public List loadDescriptors( Registry registry, String location,
90                                   String type, Object source)
91              throws Exception
92      {
93          setRegistry(registry);
94          setLocation(location);
95          setType(type);
96          setSource(source);
97          execute();
98          return mbeans;
99      }
100     
101     public void start() throws Exception {
102         registry.invoke(mbeans, "start", false);        
103     }
104 
105     public void stop() throws Exception {
106         registry.invoke(mbeans, "stop", false);        
107     }
108     
109     public void init() throws Exception {
110         if( mbeans==null) execute();
111         if( registry==null ) registry=Registry.getRegistry();
112         
113         registry.invoke(mbeans, "init", false);
114     }
115     
116     public void destroy() throws Exception {
117         registry.invoke(mbeans, "destroy", false);                
118     }
119     
120     public void load() throws Exception {
121         execute(); // backward compat
122     }
123 
124     public void execute() throws Exception {
125         if( registry==null ) registry=Registry.getRegistry();
126         try {
127             InputStream stream=getInputStream();
128             long t1=System.currentTimeMillis();
129             document = DomUtil.readXml(stream);
130 
131             // We don't care what the root node is.
132             Node descriptorsN=document.getDocumentElement();
133 
134             if( descriptorsN == null ) {
135                 log.error("No descriptors found");
136                 return;
137             }
138 
139             Node firstMbeanN=DomUtil.getChild(descriptorsN, null);
140 
141             if( firstMbeanN==null ) {
142                 // maybe we have a single mlet
143                 if( log.isDebugEnabled() )
144                     log.debug("No child " + descriptorsN);
145                 firstMbeanN=descriptorsN;
146             }
147 
148             MBeanServer server=(MBeanServer)Registry.getServer();
149 
150             // XXX Not very clean...  Just a workaround
151             if( ! loaderLoaded ) {
152                 // Register a loader that will be find ant classes.
153                 ObjectName defaultLoader= new ObjectName("modeler",
154                         "loader", "modeler");
155                 MLet mlet=new MLet( new URL[0], this.getClass().getClassLoader());
156                 server.registerMBean(mlet, defaultLoader);
157                 loaderLoaded=true;
158             }
159         
160             // Process nodes
161             for (Node mbeanN = firstMbeanN; mbeanN != null;
162                  mbeanN= DomUtil.getNext(mbeanN, null, Node.ELEMENT_NODE))
163             {
164                 String nodeName=mbeanN.getNodeName();
165 
166                 // mbean is the "official" name
167                 if( "mbean".equals(nodeName) || "MLET".equals(nodeName) )
168                 {
169                     String code=DomUtil.getAttribute( mbeanN, "code" );
170                     String objectName=DomUtil.getAttribute( mbeanN, "objectName" );
171                     if( objectName==null ) {
172                         objectName=DomUtil.getAttribute( mbeanN, "name" );
173                     }
174                     
175                     if( log.isDebugEnabled())
176                         log.debug( "Processing mbean objectName=" + objectName +
177                                 " code=" + code);
178 
179                     // args can be grouped in constructor or direct childs
180                     Node constructorN=DomUtil.getChild(mbeanN, "constructor");
181                     if( constructorN == null ) constructorN=mbeanN;
182 
183                     ArgsInfo info = processArg(constructorN);
184 
185                     try {
186                         ObjectName oname=new ObjectName(objectName);
187                         if( ! server.isRegistered( oname )) {
188                             // We wrap everything in a model mbean.
189                             // XXX need to support "StandardMBeanDescriptorsSource"
190                             String modelMBean=BaseModelMBean.class.getName();                            
191                             if(info == null) {
192                                 server.createMBean(modelMBean, oname,
193                                                    new Object[] { code, this},
194                                                    new String[] { String.class.getName(),
195                                                                   ModelerSource.class.getName() } 
196                                                    );
197                             } else {
198                                 server.createMBean(modelMBean, oname,
199                                                    new Object[] { code, this,
200                                                                   info.getValues(),
201                                                                   info.getSigs()
202                                                    },
203                                                    new String[] { String.class.getName(),
204                                                                   ModelerSource.class.getName(),
205                                                                   Object[].class.getName(),
206                                                                   String[].class.getName()
207                                                    }
208                                                    );
209                             }
210                                                    
211                             mbeans.add(oname);
212                         }
213                         object2Node.put( oname, mbeanN );
214                         // XXX Arguments, loader !!!
215                     } catch( Exception ex ) {
216                         log.error( "Error creating mbean " + objectName, ex);
217                     }
218 
219                     Node firstAttN=DomUtil.getChild(mbeanN, "attribute");
220                     for (Node descN = firstAttN; descN != null;
221                          descN = DomUtil.getNext( descN ))
222                     {
223                         processAttribute(server, descN, objectName);
224                     }
225                 } else if("jmx-operation".equals(nodeName) ) {
226                     String name=DomUtil.getAttribute(mbeanN, "objectName");
227                     if( name==null )
228                         name=DomUtil.getAttribute(mbeanN, "name");
229 
230                     String operation=DomUtil.getAttribute(mbeanN, "operation");
231 
232                     if( log.isDebugEnabled())
233                         log.debug( "Processing invoke objectName=" + name +
234                                 " code=" + operation);
235                     try {
236                         ObjectName oname=new ObjectName(name);
237 
238                         ArgsInfo info = processArg( mbeanN );
239                         if(info == null) {
240                             server.invoke( oname, operation, null, null);
241                         } else {
242                             server.invoke( oname, operation, info.getValues(), info.getSigs());
243                         }
244                     } catch (Exception e) {
245                         log.error( "Error in invoke " + name + " " + operation);
246                     }
247                 }
248 
249                 ManagedBean managed=new ManagedBean();
250                 DomUtil.setAttributes(managed, mbeanN);
251                 Node firstN;
252 
253                 // process attribute info
254                 firstN=DomUtil.getChild( mbeanN, "attribute");
255                 for (Node descN = firstN; descN != null;
256                      descN = DomUtil.getNext( descN ))
257                 {
258                     AttributeInfo ci=new AttributeInfo();
259                     DomUtil.setAttributes(ci, descN);
260                     managed.addAttribute( ci );
261                 }
262 
263             }
264 
265             long t2=System.currentTimeMillis();
266             log.info( "Reading mbeans  " + (t2-t1));
267             loading=false;
268         } catch( Exception ex ) {
269             log.error( "Error reading mbeans ", ex);
270         }
271     }
272     
273     public void updateField( ObjectName oname, String name, 
274                              Object value )
275     {
276         if( loading ) return;
277         // nothing by default
278         //log.info( "XXX UpdateField " + oname + " " + name + " " + value);
279         Node n=(Node)object2Node.get( oname );
280         if( n == null ) {
281             log.info( "Node not found " + oname );
282             return;
283         }
284         Node attNode=DomUtil.findChildWithAtt(n, "attribute", "name", name);
285         if( attNode == null ) {
286             // found no existing attribute with this name
287             attNode=n.getOwnerDocument().createElement("attribute");
288             DomUtil.setAttribute(attNode, "name", name);
289             n.appendChild(attNode);
290         } 
291         String oldValue=DomUtil.getAttribute(attNode, "value");
292         if( oldValue != null ) {
293             // we'll convert all values to text content
294             DomUtil.removeAttribute( attNode, "value");
295         }
296         DomUtil.setText(attNode, value.toString());
297 
298         //store();
299     }
300     
301     /*** Store the mbeans. 
302      * XXX add a background thread to store it periodically 
303      */ 
304     public void save() {
305         // XXX customize no often than ( based on standard descriptor ), etc.
306         // It doesn't work very well if we call this on each set att - 
307         // the triger will work for the first att, but all others will be delayed
308         long time=System.currentTimeMillis();
309         if( location!=null &&
310                 time - lastUpdate > updateInterval ) {
311             lastUpdate=time;
312             try {
313                 FileOutputStream fos=new FileOutputStream(location);
314                 DomUtil.writeXml(document, fos);
315             } catch (TransformerException e) {
316                 log.error( "Error writing");
317             } catch (FileNotFoundException e) {
318                 log.error( "Error writing" ,e );
319             }
320         }
321     }
322 
323     private void processAttribute(MBeanServer server,
324                                   Node descN, String objectName ) {
325         String attName=DomUtil.getAttribute(descN, "name");
326         String value=DomUtil.getAttribute(descN, "value");
327         String type=null; // DomUtil.getAttribute(descN, "type");
328         if( value==null ) {
329             // The value may be specified as CDATA
330             value=DomUtil.getContent(descN);
331         }
332         try {
333             if( log.isDebugEnabled())
334                 log.debug("Set attribute " + objectName + " " + attName +
335                         " " + value);
336             ObjectName oname=new ObjectName(objectName);
337             // find the type
338             if( type==null )
339                 type=registry.getType(  oname, attName );
340 
341             if( type==null ) {
342                 log.info("Can't find attribute " + objectName + " " + attName );
343 
344             } else {
345                 Object valueO=registry.convertValue( type, value);
346                 server.setAttribute(oname, new Attribute(attName, valueO));
347             }
348         } catch( Exception ex) {
349             log.error("Error processing attribute " + objectName + " " +
350                     attName + " " + value, ex);
351         }
352 
353     }
354 
355     private ArgsInfo processArg(Node mbeanN) {
356         Node firstArgN=DomUtil.getChild(mbeanN, "arg" );
357         if(firstArgN == null) {
358             return null;
359         }
360         ArgsInfo info = new ArgsInfo();
361         // process all args
362         for (Node argN = firstArgN; argN != null;
363              argN = DomUtil.getNext( argN ))
364         {
365             String type=DomUtil.getAttribute(argN, "type");
366             String value=DomUtil.getAttribute(argN, "value");
367             if( value==null ) {
368                 // The value may be specified as CDATA
369                 value=DomUtil.getContent(argN);
370             }
371             info.addArgPair(type, registry.convertValue(type,value));
372         }
373         return info;
374     }
375 
376     private static class ArgsInfo {
377         private List sigs = new ArrayList();
378         private List values = new ArrayList();
379 
380         ArgsInfo() {
381         }
382 
383         public String [] getSigs() {
384             return (String [])sigs.toArray(new String[sigs.size()]);
385         }
386 
387         public Object[] getValues () {
388             return values.toArray(new Object[values.size()]);
389         }
390 
391         public void addArgPair(String name, Object val) {
392             sigs.add(name);
393             values.add(val);
394         }
395     }
396 }