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  package org.apache.commons.modeler.modules;
18  
19  import java.io.FileNotFoundException;
20  import java.io.FileOutputStream;
21  import java.io.InputStream;
22  import java.net.URL;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  
27  import javax.management.Attribute;
28  import javax.management.MBeanServer;
29  import javax.management.ObjectName;
30  import javax.management.loading.MLet;
31  import javax.xml.transform.TransformerException;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.commons.modeler.AttributeInfo;
36  import org.apache.commons.modeler.BaseModelMBean;
37  import org.apache.commons.modeler.ManagedBean;
38  import org.apache.commons.modeler.Registry;
39  import org.apache.commons.modeler.util.DomUtil;
40  import org.w3c.dom.Document;
41  import org.w3c.dom.Node;
42  
43  
44  /*** This will create mbeans based on a config file.
45   *  The format is an extended version of MLET.
46   *
47   * Classloading. We don't support any explicit classloader tag. 
48   * A ClassLoader is just an mbean ( it can be the standard MLetMBean or
49   * a custom one ). 
50   * 
51   * XXX add a special attribute to reference the loader mbean,
52   * XXX figure out how to deal with private loaders
53   */
54  public class MbeansSource extends ModelerSource implements MbeansSourceMBean
55  {
56      private static Log log = LogFactory.getLog(MbeansSource.class);
57      Registry registry;
58      String type;
59  
60      // true if we are during the original loading
61      boolean loading=true;
62      List mbeans=new ArrayList();
63      static boolean loaderLoaded=false;
64      private Document document;
65      private HashMap object2Node = new HashMap();
66  
67      long lastUpdate;
68      long updateInterval=10000; // 10s
69  
70      public void setRegistry(Registry reg) {
71          this.registry=reg;
72      }          
73  
74      public void setLocation( String loc ) {
75          this.location=loc;
76      }
77  
78      /*** Used if a single component is loaded
79       *
80       * @param type
81       */
82      public void setType( String type ) {
83         this.type=type;
84      }
85  
86      public void setSource( Object source ) {
87          this.source=source;
88      }
89  
90      public Object getSource() {
91          return source;
92      }
93  
94      public String getLocation() {
95          return location;
96      }
97      
98      /*** Return the list of mbeans created by this source.
99       *  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 }