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.commands; 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.remote.rmi.RMIConnectorServer; 032 import javax.management.MBeanServerConnection; 033 import javax.management.ObjectName; 034 import javax.rmi.ssl.SslRMIClientSocketFactory; 035 036 // 037 // FIXME: It should be possible to query state with-out any Geronimo classes, 038 // just using JMX interfaces. 039 // 040 041 import org.apache.geronimo.gbean.AbstractName; 042 import org.apache.geronimo.gbean.AbstractNameQuery; 043 import org.apache.geronimo.kernel.Kernel; 044 import org.apache.geronimo.kernel.config.PersistentConfigurationList; 045 046 import org.slf4j.Logger; 047 import org.slf4j.LoggerFactory; 048 049 /** 050 * Helper to communicate with a remote server via JMX. 051 * 052 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 053 */ 054 public class ServerProxy 055 { 056 private static final Logger log = LoggerFactory.getLogger(ServerProxy.class); 057 058 private JMXServiceURL url; 059 060 private JMXConnector connector; 061 062 private Map environment; 063 064 private MBeanServerConnection mbeanConnection; 065 066 private Throwable lastError; 067 068 public ServerProxy(final JMXServiceURL url, final Map environment) throws Exception { 069 assert url != null; 070 assert environment != null; 071 072 this.url = url; 073 this.environment = environment; 074 075 log.debug("Initialized with URL: " + url + ", environment: " + environment); 076 } 077 078 public ServerProxy(String hostname, int port, String username, String password) throws Exception { 079 this(hostname, port, username, password, false); 080 } 081 082 public ServerProxy(String hostname, int port, String username, String password, boolean secure) throws Exception { 083 this(createJMXServiceURL(hostname, port, secure), username, password, secure); 084 } 085 086 public ServerProxy(String url, String username, String password) throws Exception { 087 this(url, username, password, false); 088 } 089 090 public ServerProxy(String url, String username, String password, boolean secure) throws Exception { 091 assert url != null; 092 assert username != null; 093 assert password != null; 094 095 this.url = new JMXServiceURL(url); 096 this.environment = new HashMap(); 097 this.environment.put(JMXConnector.CREDENTIALS, new String[] {username, password}); 098 if (secure) { 099 SslRMIClientSocketFactory csf = new SslRMIClientSocketFactory(); 100 this.environment.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); 101 } 102 103 log.debug("Initialized with URL: " + url + ", environment: " + environment); 104 } 105 106 public ServerProxy(JMXConnector connector) throws Exception { 107 this.mbeanConnection = connector.getMBeanServerConnection(); 108 } 109 110 private static String createJMXServiceURL(String hostname, int port, boolean secure) { 111 String connectorName = (secure) ? "/JMXSecureConnector" : "/JMXConnector"; 112 return "service:jmx:rmi://" + hostname + "/jndi/rmi://" + hostname + ":" + port + connectorName; 113 } 114 115 private MBeanServerConnection getConnection() throws IOException { 116 if (this.mbeanConnection == null) { 117 log.debug("Connecting to: " + url); 118 119 connector = JMXConnectorFactory.connect(url, environment); 120 this.mbeanConnection = connector.getMBeanServerConnection(); 121 122 log.debug("Connected"); 123 } 124 125 return mbeanConnection; 126 } 127 128 public void closeConnection() { 129 if (connector != null) { 130 try { 131 connector.close(); 132 } catch (IOException e) { 133 String msg = "Failed to close JMXConnector"; 134 if (log.isTraceEnabled()) { 135 log.trace(msg,e); 136 } 137 if (log.isDebugEnabled()) { 138 log.debug(msg + ":" + e); 139 } 140 } 141 } 142 } 143 144 public boolean isFullyStarted() { 145 boolean fullyStarted = true; 146 147 try { 148 AbstractNameQuery query = new AbstractNameQuery(PersistentConfigurationList.class.getName()); 149 Set result = listGBeans(query); 150 Iterator iter = result.iterator(); 151 while (iter.hasNext()) { 152 AbstractName name = (AbstractName)iter.next(); 153 boolean started = getBooleanAttribute(name, "kernelFullyStarted"); 154 if (!started) { 155 fullyStarted = false; 156 break; 157 } 158 } 159 } 160 catch (IOException e) { 161 String msg = "Connection failure; ignoring"; 162 if (log.isTraceEnabled()) { 163 log.trace(msg, e); 164 } 165 else if (log.isDebugEnabled()) { 166 log.debug(msg + ": " + e); 167 } 168 169 fullyStarted = false; 170 lastError = e; 171 } 172 catch (Exception e) { 173 String msg = "Unable to determine if the server is fully started; ignoring"; 174 if (log.isTraceEnabled()) { 175 log.trace(msg, e); 176 } 177 else if (log.isDebugEnabled()) { 178 log.debug(msg + ": " + e); 179 } 180 181 fullyStarted = false; 182 lastError = e; 183 } 184 185 return fullyStarted; 186 } 187 188 public String getGeronimoHome() { 189 String home = null; 190 191 try { 192 ObjectName systemInfoQuery = new ObjectName("*:name=ServerInfo,j2eeType=GBean,*"); 193 194 getConnection(); 195 196 Set set = mbeanConnection.queryNames(systemInfoQuery, null); 197 198 if (set.size() > 0) { 199 ObjectName found = (ObjectName)set.iterator().next(); 200 home = (String)mbeanConnection.getAttribute(found, "currentBaseDirectory"); 201 } 202 } 203 catch (IOException e) { 204 String msg = "Connection failure; ignoring"; 205 if (log.isTraceEnabled()) { 206 log.trace(msg, e); 207 } 208 else if (log.isDebugEnabled()) { 209 log.debug(msg + ": " + e); 210 } 211 212 lastError = e; 213 } 214 catch (Exception e) { 215 String msg = "Unable to determine if the server is fully started; ignoring"; 216 if (log.isTraceEnabled()) { 217 log.trace(msg, e); 218 } 219 else if (log.isDebugEnabled()) { 220 log.debug(msg + ": " + e); 221 } 222 223 lastError = e; 224 } 225 226 return home; 227 } 228 229 public Throwable getLastError() { 230 return lastError; 231 } 232 233 public void shutdown() throws Exception { 234 invoke("shutdown"); 235 } 236 237 // 238 // Kernel invocation helpers 239 // 240 241 private Object invoke(final String operation, final Object[] args, final String[] signature) throws Exception { 242 assert operation != null; 243 assert args != null; 244 assert signature != null; 245 246 return getConnection().invoke(Kernel.KERNEL, operation, args, signature); 247 } 248 249 private Object invoke(final String operation, final Object[] args) throws Exception { 250 assert args != null; 251 252 String[] signature = new String[args.length]; 253 for (int i=0; i<args.length; i++) { 254 signature[i] = args[i].getClass().getName(); 255 } 256 257 return invoke(operation, args, signature); 258 } 259 260 private Object invoke(final String operation) throws Exception { 261 return invoke(operation, new Object[0]); 262 } 263 264 private Set listGBeans(final AbstractNameQuery query) throws Exception { 265 return (Set)invoke("listGBeans", new Object[] { query }); 266 } 267 268 private Object getAttribute(final AbstractName name, final String attribute) throws Exception { 269 assert name != null; 270 assert attribute != null; 271 272 return invoke("getAttribute", new Object[] { name, attribute }); 273 } 274 275 private boolean getBooleanAttribute(final AbstractName name, final String attribute) throws Exception { 276 Object obj = getAttribute(name, attribute); 277 if (obj instanceof Boolean) { 278 return ((Boolean)obj).booleanValue(); 279 } 280 else { 281 throw new RuntimeException("Attribute is not of type Boolean: " + attribute); 282 } 283 } 284 }