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 }