001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.geronimo.mavenplugins.geronimo; 021 022 import java.util.HashMap; 023 import java.util.Map; 024 import java.util.Set; 025 import java.util.Iterator; 026 import java.io.IOException; 027 028 import javax.management.remote.JMXServiceURL; 029 import javax.management.remote.JMXConnector; 030 import javax.management.remote.JMXConnectorFactory; 031 import javax.management.MBeanServerConnection; 032 033 // 034 // FIXME: It should be possible to query state with-out any Geronimo classes, 035 // just using JMX interfaces. 036 // 037 038 import org.apache.geronimo.gbean.AbstractName; 039 import org.apache.geronimo.gbean.AbstractNameQuery; 040 import org.apache.geronimo.kernel.Kernel; 041 import org.apache.geronimo.kernel.config.PersistentConfigurationList; 042 import org.apache.geronimo.system.serverinfo.ServerInfo; 043 import org.apache.commons.logging.Log; 044 import org.apache.commons.logging.LogFactory; 045 046 /** 047 * Helper to communicate with a remote server via JMX. 048 * 049 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 050 */ 051 public class ServerProxy 052 { 053 private static final Log log = LogFactory.getLog(ServerProxy.class); 054 055 private JMXServiceURL url; 056 057 private JMXConnector connector; 058 059 private Map environment; 060 061 private MBeanServerConnection mbeanConnection; 062 063 private Throwable lastError; 064 065 public ServerProxy(final JMXServiceURL url, final Map environment) throws Exception { 066 assert url != null; 067 assert environment != null; 068 069 this.url = url; 070 this.environment = environment; 071 072 log.debug("Initialized with URL: " + url + ", environment: " + environment); 073 } 074 075 public ServerProxy(final String hostname, final int port, final String username, final String password) throws Exception { 076 this("service:jmx:rmi://" + hostname + "/jndi/rmi://" + hostname + ":" + port + "/JMXConnector", username, password); 077 } 078 079 public ServerProxy(final String url, final String username, final String password) throws Exception { 080 assert url != null; 081 assert username != null; 082 assert password != null; 083 084 this.url = new JMXServiceURL(url); 085 this.environment = new HashMap(); 086 this.environment.put("jmx.remote.credentials", new String[] {username, password}); 087 088 log.debug("Initialized with URL: " + url + ", environment: " + environment); 089 } 090 091 private MBeanServerConnection getConnection() throws IOException { 092 if (this.mbeanConnection == null) { 093 log.debug("Connecting to: " + url); 094 095 this.connector = JMXConnectorFactory.connect(url, environment); 096 this.mbeanConnection = this.connector.getMBeanServerConnection(); 097 098 log.debug("Connected"); 099 } 100 101 return mbeanConnection; 102 } 103 104 public void closeConnection() { 105 if (this.connector != null) { 106 try { 107 this.connector.close(); 108 } catch (IOException e) { 109 String msg = "Failed to close JMXConnector"; 110 if (log.isTraceEnabled()) { 111 log.trace(msg,e); 112 } 113 if (log.isDebugEnabled()) { 114 log.debug(msg + ":" + e); 115 } 116 } finally { 117 this.connector = null; 118 this.mbeanConnection = null; 119 this.lastError = null; 120 } 121 } 122 } 123 124 public boolean isFullyStarted() { 125 boolean fullyStarted = true; 126 127 try { 128 AbstractNameQuery query = new AbstractNameQuery(PersistentConfigurationList.class.getName()); 129 Set result = listGBeans(query); 130 Iterator iter = result.iterator(); 131 while (iter.hasNext()) { 132 AbstractName name = (AbstractName)iter.next(); 133 boolean started = getBooleanAttribute(name, "kernelFullyStarted"); 134 if (!started) { 135 fullyStarted = false; 136 break; 137 } 138 } 139 } 140 catch (IOException e) { 141 String msg = "Connection failure; ignoring"; 142 if (log.isTraceEnabled()) { 143 log.trace(msg, e); 144 } 145 else if (log.isDebugEnabled()) { 146 log.debug(msg + ": " + e); 147 } 148 149 fullyStarted = false; 150 lastError = e; 151 } 152 catch (Exception e) { 153 String msg = "Unable to determine if the server is fully started; ignoring"; 154 if (log.isTraceEnabled()) { 155 log.trace(msg, e); 156 } 157 else if (log.isDebugEnabled()) { 158 log.debug(msg + ": " + e); 159 } 160 161 fullyStarted = false; 162 lastError = e; 163 } 164 165 return fullyStarted; 166 } 167 168 public String getGeronimoHome() { 169 String home = null; 170 171 try { 172 AbstractNameQuery query = new AbstractNameQuery(ServerInfo.class.getName()); 173 Set result = listGBeans(query); 174 Iterator iter = result.iterator(); 175 while (iter.hasNext()) { 176 AbstractName name = (AbstractName)iter.next(); 177 home = (String)getAttribute(name, "currentBaseDirectory"); 178 if (home != null) { 179 break; 180 } 181 } 182 } 183 catch (IOException e) { 184 String msg = "Connection failure; ignoring"; 185 if (log.isTraceEnabled()) { 186 log.trace(msg, e); 187 } 188 else if (log.isDebugEnabled()) { 189 log.debug(msg + ": " + e); 190 } 191 192 lastError = e; 193 } 194 catch (Exception e) { 195 String msg = "Unable to determine if the server is fully started; ignoring"; 196 if (log.isTraceEnabled()) { 197 log.trace(msg, e); 198 } 199 else if (log.isDebugEnabled()) { 200 log.debug(msg + ": " + e); 201 } 202 203 lastError = e; 204 } 205 206 return home; 207 } 208 209 public Throwable getLastError() { 210 return lastError; 211 } 212 213 public void shutdown() { 214 try { 215 invoke("shutdown"); 216 } 217 catch (Exception e) { 218 log.warn("Unable to shutdown the server", e); 219 lastError = e; 220 } 221 } 222 223 // 224 // Kernel invocation helpers 225 // 226 227 private Object invoke(final String operation, final Object[] args, final String[] signature) throws Exception { 228 assert operation != null; 229 assert args != null; 230 assert signature != null; 231 232 return getConnection().invoke(Kernel.KERNEL, operation, args, signature); 233 } 234 235 private Object invoke(final String operation, final Object[] args) throws Exception { 236 assert args != null; 237 238 String[] signature = new String[args.length]; 239 for (int i=0; i<args.length; i++) { 240 signature[i] = args[i].getClass().getName(); 241 } 242 243 return invoke(operation, args, signature); 244 } 245 246 private Object invoke(final String operation) throws Exception { 247 return invoke(operation, new Object[0]); 248 } 249 250 private Set listGBeans(final AbstractNameQuery query) throws Exception { 251 return (Set)invoke("listGBeans", new Object[] { query }); 252 } 253 254 private Object getAttribute(final AbstractName name, final String attribute) throws Exception { 255 assert name != null; 256 assert attribute != null; 257 258 return invoke("getAttribute", new Object[] { name, attribute }); 259 } 260 261 private boolean getBooleanAttribute(final AbstractName name, final String attribute) throws Exception { 262 Object obj = getAttribute(name, attribute); 263 if (obj instanceof Boolean) { 264 return ((Boolean)obj).booleanValue(); 265 } 266 else { 267 throw new RuntimeException("Attribute is not of type Boolean: " + attribute); 268 } 269 } 270 }