View Javadoc

1   /*
2    * Copyright 1999,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.commons.modeler.mbeans;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.net.URL;
21  import java.net.URLConnection;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Map;
25  import java.util.jar.Attributes;
26  import java.util.jar.Manifest;
27  
28  import javax.management.Attribute;
29  import javax.management.AttributeNotFoundException;
30  import javax.management.MBeanException;
31  import javax.management.MBeanServer;
32  import javax.management.ObjectName;
33  import javax.management.ReflectionException;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.commons.modeler.Registry;
38  
39  /***
40   * Based on jk2 proxy.
41   *
42   * Proxy using a very simple HTTP based protocol.
43   *
44   * For efficiency, it'll get bulk results and cache them - you
45   * can force an update by calling the refreshAttributes and refreshMetadata
46   * operations on this mbean.
47   *
48   * TODO: implement the user/pass auth ( right now you must use IP based security )
49   * TODO: eventually support https
50   * TODO: support for metadata ( mbean-descriptors ) for description and type conversions
51   * TODO: filter out trivial components ( mutexes, etc )
52   *
53   * @author Costin Manolache
54   */
55  public class SimpleRemoteConnector
56  {
57      private static Log log = LogFactory.getLog(SimpleRemoteConnector.class);
58  
59      // HTTP port of the remote JMX
60      String webServerHost="localhost";
61      int webServerPort=8080;
62  
63      // URL of the remote JMX servlet ( or equivalent )
64      String statusPath="/jkstatus";
65  
66      // Not used right now
67      String user;
68      String pass;
69  
70      // Domain we mirror
71      String domain;
72  
73      // XXX Not used - allow translations
74      String localDomain;
75      String filter;
76  
77      //
78      long lastRefresh=0;
79      long updateInterval=5000; // 5 sec - it's min time between updates
80  
81      String prefix="";
82  
83      Registry reg;
84  
85      MBeanServer mserver;
86  
87      // Last view
88      HashMap mbeans=new HashMap();
89  
90      public SimpleRemoteConnector()
91      {
92      }
93  
94      /* -------------------- Public methods -------------------- */
95  
96      public String getWebServerHost() {
97          return webServerHost;
98      }
99  
100     public void setWebServerHost(String webServerHost) {
101         this.webServerHost = webServerHost;
102     }
103 
104     public int getWebServerPort() {
105         return webServerPort;
106     }
107 
108     public void setWebServerPort(int webServerPort) {
109         this.webServerPort = webServerPort;
110     }
111 
112     public long getUpdateInterval() {
113         return updateInterval;
114     }
115 
116     public void setUpdateInterval(long updateInterval) {
117         this.updateInterval = updateInterval;
118     }
119 
120     public String getUser() {
121         return user;
122     }
123 
124     public void setUser(String user) {
125         this.user = user;
126     }
127 
128     public String getPass() {
129         return pass;
130     }
131 
132     public String getDomain() {
133         return domain;
134     }
135 
136     public void setDomain(String domain) {
137         this.domain = domain;
138     }
139 
140     public void setPass(String pass) {
141         this.pass = pass;
142     }
143 
144     public String getStatusPath() {
145         return statusPath;
146     }
147 
148     public void setStatusPath(String statusPath) {
149         this.statusPath = statusPath;
150     }
151 
152     public String getFilter() {
153         return filter;
154     }
155 
156     public void setFilter(String filter) {
157         this.filter = filter;
158     }
159 
160     /* ==================== Start/stop ==================== */
161 
162     public void destroy() {
163         try {
164             // We should keep track of loaded beans and call stop.
165             // Modeler should do it...
166             Iterator mbeansIt=mbeans.values().iterator();
167             while( mbeansIt.hasNext()) {
168                 MBeanProxy proxy=(MBeanProxy)mbeansIt.next();
169                 ObjectName oname=proxy.getJmxName();
170                 Registry.getRegistry().getMBeanServer().unregisterMBean(oname);
171             }
172         } catch( Throwable t ) {
173             log.error( "Destroy error", t );
174         }
175     }
176 
177     public void init() throws IOException {
178         try {
179             //if( log.isDebugEnabled() )
180             log.info("init " + webServerHost + " " + webServerPort);
181             reg=Registry.getRegistry();
182             // Get metadata for all mbeans on the remote side
183             //refreshMetadata();
184             // Get current values and mbeans
185             refreshAttributes();
186         } catch( Throwable t ) {
187             log.error( "Init error", t );
188         }
189     }
190 
191     public void start() throws IOException {
192         System.out.println("XXX start");
193         if( reg==null)
194             init();
195     }
196 
197     /*** Refresh the proxies, if updateInterval passed
198      *
199      */
200     public void refresh()  {
201         long time=System.currentTimeMillis();
202         if( time - lastRefresh < updateInterval ) {
203             return;
204         }
205         System.out.println("refresh... ");
206         lastRefresh=time;
207         //refreshMetadata();
208         refreshAttributes();
209     }
210 
211     public void refreshAttributes()  {
212         try {
213             int cnt=0;
214             // connect to apache, get a list of mbeans
215             if( filter==null ) {
216                 filter=domain + ":*";
217             }
218 
219             InputStream is=getStream( "qry=" + filter);
220             if( is==null ) return;
221 
222             Manifest mf=new Manifest(is);
223             
224             HashMap currentObjects=new HashMap(); // used to remove older ones
225             Map entries=mf.getEntries();
226             Iterator it=entries.keySet().iterator();
227             while( it.hasNext() ) {
228                 String name=(String)it.next();
229                 Attributes attrs=(Attributes)entries.get( name );
230 
231                 ObjectName oname=new ObjectName(name);
232                 currentObjects.put( oname, "");
233                 MBeanProxy proxy=(MBeanProxy)mbeans.get(oname);
234                 if( proxy==null ) {
235                     log.debug( "New object " + name);
236                     String code=attrs.getValue("modelerType");
237                     if(log.isDebugEnabled())
238                         log.debug("Register " + name  + " " + code );
239 
240                     proxy= new MBeanProxy(this, code);
241                     mbeans.put( oname, proxy );
242 
243                     // Register
244                     MBeanServer mserver=Registry.getRegistry().getMBeanServer();
245                     if( ! mserver.isRegistered(oname ) ) {
246                         mserver.registerMBean(proxy, oname);
247                     }
248                 }
249                 Iterator it2=attrs.keySet().iterator();
250                 while( it2.hasNext() ) {
251                     Object o=it2.next();
252                     String att=(o==null) ? null : o.toString();
253                     if( "modelerType".equals( att )) continue;
254                     String val=attrs.getValue(att);
255                     proxy.update(att, val);
256                     cnt++;
257                 }
258             }
259             
260             // Now we have to remove all the mbeans that are no longer there
261             Iterator existingIt=mbeans.keySet().iterator();
262             while( existingIt.hasNext() ) {
263                 ObjectName on=(ObjectName)existingIt.next();
264                 if(currentObjects.get( on ) != null )
265                     continue; // still alive
266                 if( log.isDebugEnabled() )
267                     log.debug("No longer alive " + on);
268                 try {
269                     mserver.unregisterMBean(on);
270                 } catch( Throwable t ) {
271                     log.info("Error unregistering " + on + " " + t.toString());
272                 }
273             }
274             
275             log.info( "Refreshing attributes " + cnt);
276         } catch( Exception ex ) {
277             log.info("Error ", ex);
278         }
279     }
280 
281     // Not used right now - assume the metadata is available locally
282     // Could use mbeans-descriptors.xml or other formats.
283     // Will be called if code= is not found locally
284     public void refreshMetadata() {
285         try {
286             int cnt=0;
287             int newCnt=0;
288             InputStream is=getStream("getMetadata=" + domain + ":*");
289             if( is==null ) return;
290 
291             log.info( "Refreshing metadata " + cnt + " " +  newCnt);
292         } catch( Exception ex ) {
293             log.info("Error ", ex);
294         }
295     }
296 
297     public Object invoke(Object oname, String name, Object params[], String signature[])
298         throws MBeanException, ReflectionException {
299         try {
300             // we support only string values
301             InputStream is=this.getStream("invoke=" + name + "&name=" + oname.toString() );
302             if( is==null ) return null;
303 //                String res=is.readLine();
304 //                if( log.isDebugEnabled())
305 //                    log.debug( "Invoking " + jkName + " " + name + " result " + res);
306 
307             //this.refreshMetadata();
308             this.refreshAttributes();
309         } catch( Exception ex ) {
310             throw new MBeanException(ex);
311         }
312         return null;
313     }
314 
315 
316     public void setAttribute(ObjectName oname, Attribute attribute)
317         throws AttributeNotFoundException, MBeanException,
318         ReflectionException
319     {
320         try {
321             // we support only string values
322             String val=(String)attribute.getValue();
323             String name=attribute.getName();
324             InputStream is=this.getStream("set=" + name + "&name=" + oname.toString()
325                     + "&value=" + val);
326             if( is==null ) return;
327 //                String res=is.readLine();
328 //                if( log.isDebugEnabled())
329 //                    log.debug( "Setting " + jkName + " " + name + " result " + res);
330 
331             //this.refreshMetadata();
332             this.refreshAttributes();
333         } catch( Exception ex ) {
334             throw new MBeanException(ex);
335         }
336     }
337 
338     /*** connect to apache using http, get a list of mbeans. Can be
339      * overriten to support different protocols ( jk/Unix domain sockets, etc )
340       */
341     protected InputStream getStream(String qry) throws Exception {
342         try {
343             String path=statusPath + "?" + qry;
344             URL url=new URL( "http", webServerHost, webServerPort, path);
345             log.debug( "Connecting to " + url);
346             URLConnection urlc=url.openConnection();
347             InputStream is=urlc.getInputStream();
348             return is;
349         } catch (IOException e) {
350             log.info( "Can't connect to jkstatus " + webServerHost + ":" + webServerPort
351             + " " + e.toString());
352             return null;
353         }
354     }
355 
356 
357 }
358