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