1. /*
  2. * ====================================================================
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. * Copyright (c) 1999 The Apache Software Foundation. All rights
  7. * reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * 2. Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in
  18. * the documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * 3. The end-user documentation included with the redistribution, if
  22. * any, must include the following acknowlegement:
  23. * "This product includes software developed by the
  24. * Apache Software Foundation (http://www.apache.org/)."
  25. * Alternately, this acknowlegement may appear in the software itself,
  26. * if and wherever such third-party acknowlegements normally appear.
  27. *
  28. * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  29. * Foundation" must not be used to endorse or promote products derived
  30. * from this software without prior written permission. For written
  31. * permission, please contact apache@apache.org.
  32. *
  33. * 5. Products derived from this software may not be called "Apache"
  34. * nor may "Apache" appear in their names without prior written
  35. * permission of the Apache Group.
  36. *
  37. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  38. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  39. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  40. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  41. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  42. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  43. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  44. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  45. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  46. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  47. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48. * SUCH DAMAGE.
  49. * ====================================================================
  50. *
  51. * This software consists of voluntary contributions made by many
  52. * individuals on behalf of the Apache Software Foundation. For more
  53. * information on the Apache Software Foundation, please see
  54. * <http://www.apache.org/>.
  55. *
  56. * [Additional notices, if required by prior licensing conditions]
  57. *
  58. */
  59. package org.apache.commons.modeler.mbeans;
  60. import java.io.IOException;
  61. import java.io.InputStream;
  62. import java.net.URL;
  63. import java.net.URLConnection;
  64. import java.util.HashMap;
  65. import java.util.Iterator;
  66. import java.util.Map;
  67. import java.util.jar.Attributes;
  68. import java.util.jar.Manifest;
  69. import javax.management.Attribute;
  70. import javax.management.AttributeNotFoundException;
  71. import javax.management.MBeanException;
  72. import javax.management.MBeanServer;
  73. import javax.management.ObjectName;
  74. import javax.management.ReflectionException;
  75. import org.apache.commons.logging.Log;
  76. import org.apache.commons.logging.LogFactory;
  77. import org.apache.commons.modeler.Registry;
  78. /**
  79. * Based on jk2 proxy.
  80. *
  81. * Proxy using a very simple HTTP based protocol.
  82. *
  83. * For efficiency, it'll get bulk results and cache them - you
  84. * can force an update by calling the refreshAttributes and refreshMetadata
  85. * operations on this mbean.
  86. *
  87. * TODO: implement the user/pass auth ( right now you must use IP based security )
  88. * TODO: eventually support https
  89. * TODO: support for metadata ( mbean-descriptors ) for description and type conversions
  90. * TODO: filter out trivial components ( mutexes, etc )
  91. *
  92. * @author Costin Manolache
  93. */
  94. public class SimpleRemoteConnector
  95. {
  96. private static Log log = LogFactory.getLog(SimpleRemoteConnector.class);
  97. // HTTP port of the remote JMX
  98. String webServerHost="localhost";
  99. int webServerPort=8080;
  100. // URL of the remote JMX servlet ( or equivalent )
  101. String statusPath="/jkstatus";
  102. // Not used right now
  103. String user;
  104. String pass;
  105. // Domain we mirror
  106. String domain;
  107. // XXX Not used - allow translations
  108. String localDomain;
  109. String filter;
  110. //
  111. long lastRefresh=0;
  112. long updateInterval=5000; // 5 sec - it's min time between updates
  113. String prefix="";
  114. Registry reg;
  115. MBeanServer mserver;
  116. // Last view
  117. HashMap mbeans=new HashMap();
  118. public SimpleRemoteConnector()
  119. {
  120. }
  121. /* -------------------- Public methods -------------------- */
  122. public String getWebServerHost() {
  123. return webServerHost;
  124. }
  125. public void setWebServerHost(String webServerHost) {
  126. this.webServerHost = webServerHost;
  127. }
  128. public int getWebServerPort() {
  129. return webServerPort;
  130. }
  131. public void setWebServerPort(int webServerPort) {
  132. this.webServerPort = webServerPort;
  133. }
  134. public long getUpdateInterval() {
  135. return updateInterval;
  136. }
  137. public void setUpdateInterval(long updateInterval) {
  138. this.updateInterval = updateInterval;
  139. }
  140. public String getUser() {
  141. return user;
  142. }
  143. public void setUser(String user) {
  144. this.user = user;
  145. }
  146. public String getPass() {
  147. return pass;
  148. }
  149. public String getDomain() {
  150. return domain;
  151. }
  152. public void setDomain(String domain) {
  153. this.domain = domain;
  154. }
  155. public void setPass(String pass) {
  156. this.pass = pass;
  157. }
  158. public String getStatusPath() {
  159. return statusPath;
  160. }
  161. public void setStatusPath(String statusPath) {
  162. this.statusPath = statusPath;
  163. }
  164. public String getFilter() {
  165. return filter;
  166. }
  167. public void setFilter(String filter) {
  168. this.filter = filter;
  169. }
  170. /* ==================== Start/stop ==================== */
  171. public void destroy() {
  172. try {
  173. // We should keep track of loaded beans and call stop.
  174. // Modeler should do it...
  175. Iterator mbeansIt=mbeans.values().iterator();
  176. while( mbeansIt.hasNext()) {
  177. MBeanProxy proxy=(MBeanProxy)mbeansIt.next();
  178. ObjectName oname=proxy.getObjectName();
  179. Registry.getRegistry().getMBeanServer().unregisterMBean(oname);
  180. }
  181. } catch( Throwable t ) {
  182. log.error( "Destroy error", t );
  183. }
  184. }
  185. public void init() throws IOException {
  186. try {
  187. //if( log.isDebugEnabled() )
  188. log.info("init " + webServerHost + " " + webServerPort);
  189. reg=Registry.getRegistry();
  190. // Get metadata for all mbeans on the remote side
  191. //refreshMetadata();
  192. // Get current values and mbeans
  193. refreshAttributes();
  194. } catch( Throwable t ) {
  195. log.error( "Init error", t );
  196. }
  197. }
  198. public void start() throws IOException {
  199. System.out.println("XXX start");
  200. if( reg==null)
  201. init();
  202. }
  203. /** Refresh the proxies, if updateInterval passed
  204. *
  205. */
  206. public void refresh() {
  207. long time=System.currentTimeMillis();
  208. if( time - lastRefresh < updateInterval ) {
  209. return;
  210. }
  211. System.out.println("refresh... ");
  212. lastRefresh=time;
  213. //refreshMetadata();
  214. refreshAttributes();
  215. }
  216. public void refreshAttributes() {
  217. try {
  218. int cnt=0;
  219. // connect to apache, get a list of mbeans
  220. if( filter==null ) {
  221. filter=domain + ":*";
  222. }
  223. InputStream is=getStream( "qry=" + filter);
  224. if( is==null ) return;
  225. Manifest mf=new Manifest(is);
  226. HashMap currentObjects=new HashMap(); // used to remove older ones
  227. Map entries=mf.getEntries();
  228. Iterator it=entries.keySet().iterator();
  229. while( it.hasNext() ) {
  230. String name=(String)it.next();
  231. Attributes attrs=(Attributes)entries.get( name );
  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. proxy= new MBeanProxy(this, code);
  241. mbeans.put( oname, proxy );
  242. // Register
  243. MBeanServer mserver=Registry.getRegistry().getMBeanServer();
  244. if( ! mserver.isRegistered(oname ) ) {
  245. mserver.registerMBean(proxy, oname);
  246. }
  247. }
  248. Iterator it2=attrs.keySet().iterator();
  249. while( it2.hasNext() ) {
  250. Object o=it2.next();
  251. String att=(o==null) ? null : o.toString();
  252. if( "modelerType".equals( att )) continue;
  253. String val=attrs.getValue(att);
  254. proxy.update(att, val);
  255. cnt++;
  256. }
  257. }
  258. // Now we have to remove all the mbeans that are no longer there
  259. Iterator existingIt=mbeans.keySet().iterator();
  260. while( existingIt.hasNext() ) {
  261. ObjectName on=(ObjectName)existingIt.next();
  262. if(currentObjects.get( on ) != null )
  263. continue; // still alive
  264. if( log.isDebugEnabled() )
  265. log.debug("No longer alive " + on);
  266. try {
  267. mserver.unregisterMBean(on);
  268. } catch( Throwable t ) {
  269. log.info("Error unregistering " + on + " " + t.toString());
  270. }
  271. }
  272. log.info( "Refreshing attributes " + cnt);
  273. } catch( Exception ex ) {
  274. log.info("Error ", ex);
  275. }
  276. }
  277. // Not used right now - assume the metadata is available locally
  278. // Could use mbeans-descriptors.xml or other formats.
  279. // Will be called if code= is not found locally
  280. public void refreshMetadata() {
  281. try {
  282. int cnt=0;
  283. int newCnt=0;
  284. InputStream is=getStream("getMetadata=" + domain + ":*");
  285. if( is==null ) return;
  286. log.info( "Refreshing metadata " + cnt + " " + newCnt);
  287. } catch( Exception ex ) {
  288. log.info("Error ", ex);
  289. }
  290. }
  291. public Object invoke(Object oname, String name, Object params[], String signature[])
  292. throws MBeanException, ReflectionException {
  293. try {
  294. // we support only string values
  295. InputStream is=this.getStream("invoke=" + name + "&name=" + oname.toString() );
  296. if( is==null ) return null;
  297. // String res=is.readLine();
  298. // if( log.isDebugEnabled())
  299. // log.debug( "Invoking " + jkName + " " + name + " result " + res);
  300. //this.refreshMetadata();
  301. this.refreshAttributes();
  302. } catch( Exception ex ) {
  303. throw new MBeanException(ex);
  304. }
  305. return null;
  306. }
  307. public void setAttribute(ObjectName oname, Attribute attribute)
  308. throws AttributeNotFoundException, MBeanException,
  309. ReflectionException
  310. {
  311. try {
  312. // we support only string values
  313. String val=(String)attribute.getValue();
  314. String name=attribute.getName();
  315. InputStream is=this.getStream("set=" + name + "&name=" + oname.toString()
  316. + "&value=" + val);
  317. if( is==null ) return;
  318. // String res=is.readLine();
  319. // if( log.isDebugEnabled())
  320. // log.debug( "Setting " + jkName + " " + name + " result " + res);
  321. //this.refreshMetadata();
  322. this.refreshAttributes();
  323. } catch( Exception ex ) {
  324. throw new MBeanException(ex);
  325. }
  326. }
  327. /** connect to apache using http, get a list of mbeans. Can be
  328. * overriten to support different protocols ( jk/Unix domain sockets, etc )
  329. */
  330. protected InputStream getStream(String qry) throws Exception {
  331. try {
  332. String path=statusPath + "?" + qry;
  333. URL url=new URL( "http", webServerHost, webServerPort, path);
  334. log.debug( "Connecting to " + url);
  335. URLConnection urlc=url.openConnection();
  336. InputStream is=urlc.getInputStream();
  337. return is;
  338. } catch (IOException e) {
  339. log.info( "Can't connect to jkstatus " + webServerHost + ":" + webServerPort
  340. + " " + e.toString());
  341. return null;
  342. }
  343. }
  344. }