001/* 002 * $RCSfile: Client.java,v $ 003 * $Revision: 1.1.2.5.14.2 $ $Date: 2015/05/07 23:56:02 $ 004 * 005 * Copyright 2009 Kabira Technologies, Inc. All rights reserved. 006 */ 007package com.kabira.test.management; 008 009import java.io.BufferedReader; 010import java.io.IOException; 011import java.io.InputStream; 012import java.io.InputStreamReader; 013import java.net.InetAddress; 014import java.net.MalformedURLException; 015import java.net.URL; 016import java.net.UnknownHostException; 017import java.util.Map; 018import java.util.HashMap; 019import java.util.Map.Entry; 020import java.lang.Throwable; 021import java.util.logging.Level; 022import java.util.logging.Logger; 023import javax.management.AttributeList; 024import javax.management.InstanceNotFoundException; 025import javax.management.MBeanException; 026import javax.management.MBeanServerConnection; 027import javax.management.MalformedObjectNameException; 028import javax.management.ObjectName; 029import javax.management.ReflectionException; 030import javax.management.RuntimeErrorException; 031import javax.management.RuntimeMBeanException; 032import javax.management.remote.JMXConnector; 033import javax.management.remote.JMXConnectorFactory; 034import javax.management.remote.JMXServiceURL; 035import com.kabira.platform.Transaction; 036import com.kabira.platform.Transaction.InvalidTransactionState; 037 038/** 039 * Provides a mechanism for calling administration targets from test code. 040 */ 041public class Client 042{ 043 /** 044 * Create a new Client instance using the given userName and password. 045 * @param userName a configured principal on the node 046 * @param password the text credential for the principal 047 */ 048 public Client(String userName, String password) 049 { 050 m_userName = userName; 051 m_password = password; 052 } 053 054 /** 055 * Execute an administration command. 056 * All administration targets and commands are supported 057 * except "load configuration", which is supported by the 058 * nested Configuration class, and "loadactivate configuration", which is 059 * not supported. 060 * The administration target will always run on the same node as 061 * the application calling runCommand. 062 * 063 * @param command the command name 064 * @param target the target name 065 * @param parameters a map containing parameter names and values 066 * @return The output of the command 067 * @throws CommandFailed an unsupported command was specified or the 068 * invocation of the command failed. 069 * @throws InvalidTransactionState called from a transactional context. 070 */ 071 public String[] runCommand( 072 final String command, 073 final String target, 074 final HashMap<String, String> parameters) 075 throws CommandFailed, InvalidTransactionState 076 { 077 if (target.equals("configuration")) 078 { 079 if (command.equals("load")) 080 { 081 throw new CommandFailed("load configuration is not supported " + 082 "by runCommand. Use loadConfigurationData() or " + 083 "loadConfigurationResource() instead."); 084 } 085 else if (command.equals("loadactivate")) 086 { 087 throw new CommandFailed( 088 "loadactivate configuration is not supported"); 089 } 090 } 091 092 jmxConnect(); 093 094 String[] params = null; 095 096 if (parameters != null) 097 { 098 params = new String[parameters.size()]; 099 int i = 0; 100 101 for (Entry entry : parameters.entrySet()) 102 { 103 params[i++] = (String) entry.getKey() + "=" + (String) entry. 104 getValue(); 105 } 106 } 107 108 return invoke(command, target, params); 109 } 110 /** 111 * performs cleanup action. 112 * closes jmx connection, if not done already 113 * this is generally called by finalize method which is called by garbage 114 * collector to do cleanup; 115 * this is made public so that calling class can do cleanup to release 116 * resources without having to wait for garbage collector. 117 */ 118 public void tearDown() 119 { 120 try 121 { 122 if(m_jmxConnector != null) 123 { 124 m_jmxConnector.close(); 125 } 126 } 127 catch (IOException ex) 128 { 129 Logger l = Logger.getLogger(""); 130 l.log(Level.WARNING, 131 "Failed to close jmx connector file", ex); 132 } 133 } 134 @Override 135 protected void finalize() throws Throwable 136 { 137 super.finalize(); 138 tearDown(); 139 } 140 /** 141 * Provides a mechanism for loading and managing configuration data. 142 */ 143 public final class Configuration 144 { 145 /** 146 * Create a new Configuration instance to manage the supplied 147 * configuration data 148 * @param url the URL of the configuration data. 149 */ 150 public Configuration(final URL url) 151 { 152 m_url = url; 153 } 154 155 /** 156 * Load configuration data. 157 * @throws CommandFailed the URL could not be opened, or the 158 * configuration failed audit. 159 * @throws InvalidTransactionState called from a transactional context. 160 */ 161 public void load() throws CommandFailed, InvalidTransactionState 162 { 163 jmxConnect(); 164 165 InputStream is; 166 167 try 168 { 169 is = m_url.openStream(); 170 } 171 catch (IOException ex) 172 { 173 throw new CommandFailed("failed to open URL " + 174 m_url.toString() + ": " + ex.getMessage()); 175 } 176 177 String data = readInputStream(is); 178 179 Object[] params = new Object[] 180 { 181 m_url.toString(), 182 data, 183 new Boolean(false), 184 new Boolean(false), 185 }; 186 187 String[] sig = new String[] 188 { 189 "java.lang.String", 190 "java.lang.String", 191 "java.lang.Boolean", 192 "java.lang.Boolean", 193 }; 194 195 String[] results = doInvoke(m_configurationName, 196 "loadNamedResource", params, sig); 197 198 m_versionInfo = new HashMap<String, String>(); 199 200 for (String r : results) 201 { 202 String[]nv = r.replaceAll("\\s", "").split("="); 203 m_versionInfo.put(nv[0], nv[1]); 204 } 205 206 assert(m_versionInfo.get("type") != null); 207 assert(m_versionInfo.get("name") != null); 208 assert(m_versionInfo.get("version") != null); 209 210 m_parameters = new String[] 211 { 212 "type=" + m_versionInfo.get("type"), 213 "name=" + m_versionInfo.get("name"), 214 "version=" + m_versionInfo.get("version") 215 }; 216 } 217 218 /** 219 * Activate the current configuration. Configuration must have been 220 * loaded by this instance of Configuration. 221 * @throws CommandFailed the configuration was not loaded by this 222 * Configuration instance or the configuration is already active. 223 * @throws InvalidTransactionState called from a transactional context. 224 */ 225 public void activate() throws CommandFailed, InvalidTransactionState 226 { 227 checkLoaded(); 228 invoke("activate", "configuration", m_parameters); 229 } 230 231 /** 232 * Deactivate the current configuration. Configuration must have been 233 * loaded by this instance of Configuration. 234 * @throws CommandFailed the configuration was not loaded by this 235 * Configuration instance or the configuration is not active. 236 * @throws InvalidTransactionState called from a transactional context. 237 */ 238 public void deactivate() throws CommandFailed, InvalidTransactionState 239 { 240 checkLoaded(); 241 invoke("deactivate", "configuration", m_parameters); 242 } 243 244 /** 245 * Remove the current configuration. Configuration must have been 246 * loaded by this instance of Configuration. 247 * @throws CommandFailed the configuration was not loaded by this 248 * Configuration instance or the configuration has already been 249 * removed. 250 * @throws InvalidTransactionState called from a transactional context. 251 */ 252 public void remove() throws CommandFailed, InvalidTransactionState 253 { 254 checkLoaded(); 255 invoke("remove", "configuration", m_parameters); 256 } 257 258 /** 259 * 260 * Display the current Configuration. Configuration must have been 261 * loaded by this instance of Configuration. 262 * @return All available information about the configuration. 263 * @throws CommandFailed the configuration was not loaded by this 264 * Configuration instance. 265 * @throws InvalidTransactionState called from a transactional context. 266 */ 267 public String[] display() throws CommandFailed, InvalidTransactionState 268 { 269 checkLoaded(); 270 return invoke("display", "configuration", m_parameters); 271 } 272 273 /** 274 * Export the current configuration. Configuration must have been 275 * loaded by this instance of Configuration. 276 * @return The configuration data formatted as a kcs file. 277 * @throws CommandFailed the configuration was not loaded by this 278 * Configuration instance. 279 * @throws InvalidTransactionState called from a transactional context. 280 */ 281 public String[] export() throws CommandFailed, InvalidTransactionState 282 { 283 checkLoaded(); 284 return invoke("export", "configuration", m_parameters); 285 } 286 287 /** 288 * Get the type of the currently-loaded configuration. 289 * @return the type of the currently-loaded configuration. 290 * @throws CommandFailed the configuration was not loaded by this 291 * Configuration instance. 292 */ 293 public String getType() throws CommandFailed 294 { 295 checkLoaded(); 296 return m_versionInfo.get("type"); 297 } 298 299 /** 300 * Get the name of the currently-loaded configuration. 301 * @return the name of the currently-loaded configuration 302 * @throws CommandFailed the configuration was not loaded by this 303 * Configuration instance. 304 */ 305 public String getName() throws CommandFailed 306 { 307 checkLoaded(); 308 return m_versionInfo.get("name"); 309 } 310 311 /** 312 * Get the version of the currently-loaded configuration. 313 * @return the version of the currently-loaded configuration 314 * @throws CommandFailed the configuration was not loaded by this 315 * Configuration instance. 316 */ 317 public String getVersion() throws CommandFailed 318 { 319 checkLoaded(); 320 return m_versionInfo.get("version"); 321 } 322 323 /** 324 * Get the state of the currently-loaded configuration. 325 * @return the state of the currently-loaded configuration 326 * @throws CommandFailed the configuration was not loaded by this 327 * Configuration instance. 328 * @throws InvalidTransactionState called from a transactional context. 329 */ 330 public ConfigurationState getState() 331 throws CommandFailed, InvalidTransactionState 332 { 333 checkLoaded(); 334 335 assert(m_parameters.length == 3); 336 337 String[] parameters = new String[] 338 { 339 m_parameters[0], 340 m_parameters[1], 341 m_parameters[2], 342 "delimiter=|" 343 }; 344 345 String[] display = invoke("display", "configuration", parameters); 346 347 assert(display.length == 2); 348 349 String[] names = display[0].split("\\|"); 350 String[] values = display[1].split("\\|"); 351 352 assert(names.length == values.length); 353 354 ConfigurationState state = null; 355 356 for (int i = 0; i < names.length; i++) 357 { 358 if (names[i].equals("State")) 359 { 360 if (values[i].equals("Active")) 361 { 362 state = ConfigurationState.Active; 363 } 364 else 365 { 366 state = ConfigurationState.Inactive; 367 } 368 break; 369 } 370 } 371 372 return state; 373 } 374 375 private void checkLoaded() throws CommandFailed 376 { 377 if (m_parameters == null) 378 { 379 throw new CommandFailed("Configuration is not loaded"); 380 } 381 } 382 383 private URL m_url; 384 private String[] m_parameters = null; 385 private HashMap<String, String> m_versionInfo; 386 } 387 388 private String[] invoke(String command, String target, 389 String[] parameters) 390 throws CommandFailed, InvalidTransactionState 391 { 392 int numArgs = 2; 393 394 if (parameters != null) 395 { 396 numArgs += 1; 397 } 398 399 Object result = null; 400 Object[] params = new Object[numArgs]; 401 String[] sig = new String[numArgs]; 402 403 params[0] = new String(command); 404 params[1] = new String(target); 405 406 if (parameters != null) 407 { 408 String allargs = new String(); 409 410 for (String p : parameters) 411 { 412 if (!allargs.isEmpty()) 413 { 414 allargs += " "; 415 } 416 allargs += p; 417 } 418 params[2] = allargs; 419 } 420 421 for (int i = 0; i < numArgs; i++) 422 { 423 sig[i] = new String("java.lang.String"); 424 } 425 426 return doInvoke(m_pluginServiceName, "execute", params, sig); 427 428 } 429 430 private String[] doInvoke(ObjectName name, 431 String command, Object[] args, String[] sig) 432 throws CommandFailed, InvalidTransactionState 433 { 434 // 435 // fail if there is an active transaction 436 // 437 checkInTransaction(); 438 439 try 440 { 441 return (String[]) m_mbsc.invoke(name, command, args, sig); 442 } 443 catch (Exception ex) 444 { 445 throw new CommandFailed(ex); 446 } 447 } 448 449 private String readInputStream(InputStream stream) throws CommandFailed 450 { 451 BufferedReader reader = new BufferedReader( 452 new InputStreamReader(stream)); 453 454 StringBuffer stringBuffer = new StringBuffer(); 455 456 char[] buffer = new char[1024]; 457 int bytesRead; 458 CommandFailed commandFailed = null; 459 460 try 461 { 462 while ((bytesRead = reader.read(buffer, 0, buffer.length)) != -1) 463 { 464 stringBuffer.append(buffer, 0, bytesRead); 465 } 466 } 467 catch (IOException ex) 468 { 469 commandFailed = new CommandFailed(ex); 470 } 471 472 try 473 { 474 stream.close(); 475 } 476 catch (IOException ex) 477 { 478 if (commandFailed == null) 479 { 480 commandFailed = new CommandFailed(ex); 481 } 482 } 483 484 if (commandFailed != null) 485 { 486 throw commandFailed; 487 } 488 489 return stringBuffer.toString(); 490 } 491 492 private void jmxConnect() throws CommandFailed 493 { 494 if (m_mbsc != null) 495 { 496 return; 497 } 498 499 String host; 500 String port = System.getProperty( 501 "com.kabira.management.jmxregistryport"); 502 503 if (port == null) 504 { 505 throw new CommandFailed("System property " + 506 "com.kabira.management.jmxregistryport is not available " + 507 "in this engine - is the adminservice component " + 508 "installed?"); 509 } 510 511 try 512 { 513 host = InetAddress.getLocalHost().getHostName(); 514 } 515 catch (UnknownHostException ex) 516 { 517 throw new CommandFailed(ex); 518 } 519 520 String addr = "service:jmx:rmi:///jndi/rmi://" + 521 host + ":" + port + "/jmxrmi"; 522 523 JMXServiceURL url = null; 524 try 525 { 526 url = new JMXServiceURL(addr); 527 } 528 catch (MalformedURLException ex) 529 { 530 throw new CommandFailed(ex); 531 } 532 533 HashMap<String, String[]> env = new HashMap<String, String[]>(); 534 535 env.put("jmx.remote.credentials", 536 new String[] { m_userName, m_password }); 537 538 try 539 { 540 m_jmxConnector = JMXConnectorFactory.connect(url, env); 541 m_mbsc = m_jmxConnector.getMBeanServerConnection(); 542 } 543 catch (java.io.IOException ex) 544 { 545 throw new CommandFailed(ex); 546 } 547 548 try 549 { 550 m_pluginServiceName = new ObjectName( 551 "com.kabira.platform.management", "type", "Management"); 552 m_configurationName = new ObjectName( 553 "com.kabira.platform.management", "type", "Configuration"); 554 } 555 catch (MalformedObjectNameException ex) 556 { 557 throw new CommandFailed(ex); 558 } 559 catch (NullPointerException ex) 560 { 561 throw new CommandFailed(ex); 562 } 563 } 564 565 private void checkInTransaction() throws InvalidTransactionState 566 { 567 new Transaction() 568 { 569 public void run() throws Transaction.Rollback 570 { 571 } 572 }.execute(); 573 } 574 575 private String m_userName; 576 private String m_password; 577 private MBeanServerConnection m_mbsc = null; 578 private ObjectName m_pluginServiceName = null; 579 private ObjectName m_configurationName = null; 580 private JMXConnector m_jmxConnector = null; 581} 582