001 package org.apache.fulcrum.yaafi.cli; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import java.io.File; 023 024 import org.apache.avalon.framework.activity.Disposable; 025 import org.apache.avalon.framework.logger.ConsoleLogger; 026 import org.apache.avalon.framework.logger.Logger; 027 import org.apache.avalon.framework.service.ServiceManager; 028 import org.apache.fulcrum.yaafi.framework.container.ServiceContainer; 029 import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerConfiguration; 030 import org.apache.fulcrum.yaafi.framework.factory.ServiceContainerFactory; 031 032 033 /** 034 * An example of the embedding of a YAAFI kernel inside an 035 * arbitrary application. 036 */ 037 038 public class Main implements Runnable, Disposable 039 { 040 /** parameter for the application name */ 041 public static final String APPLICATION_NAME = "yaafi.cli.applicationName"; 042 043 /** parameter for the application home directory */ 044 public static final String APPLICATION_HOME = "yaafi.cli.applicationHome"; 045 046 /** parameter for the application temporary directory */ 047 public static final String APPLICATION_TEMP = "yaafi.cli.tempHome"; 048 049 /** parameter for the application container configuration file */ 050 public static final String APPLICATION_CONFIG = "yaafi.cli.config"; 051 052 /** parameter for setting a shutdown hook */ 053 public static final String APPLICATION_HASSHUTDOWNHOOK = "yaafi.cli.hasShutdownHook"; 054 055 /** parameter for blocking the main thread in Main.run() */ 056 public static final String APPLICATION_ISBLOCKING = "yaafi.cli.isBlocking"; 057 058 /** the interval to check for termination */ 059 private static final int SLEEP_TIME = 100; 060 061 /** the timeout for joing the shutdown thread */ 062 private static final int JOIN_TIME = 1000; 063 064 /** The service manager */ 065 private ServiceContainer container; 066 067 /** The location of the container configuration */ 068 private String containerConfigValue; 069 070 /** Thread for processing the shutdown notification of the JVM */ 071 private Thread shutdownThread; 072 073 /** Do we block the invoking thread until the JVM terminates ?! */ 074 private boolean isBlocking; 075 076 /** Do we install a shutdown hook for the JVM ?! */ 077 private boolean hasShutdownHook; 078 079 /** The logger being used */ 080 private Logger logger; 081 082 /** the name of the application */ 083 private String applicationName; 084 085 /** the working directory */ 086 private String applicationHome; 087 088 /** the temp directory */ 089 private String tempHome; 090 091 /** the command line arguments */ 092 private String[] args; 093 094 /** is the instance properly initialized */ 095 private volatile boolean isInitialized; 096 097 098 /** 099 * Constructor 100 */ 101 public Main() 102 { 103 // default initialization 104 105 this.containerConfigValue = "./conf/containerConfiguration.xml"; 106 this.logger = new ConsoleLogger(); 107 this.applicationHome = "."; 108 this.tempHome = System.getProperty("java.io.tmpdir","."); 109 this.applicationName = "main"; 110 this.args = ( args != null ? args : new String[0] ); 111 this.isBlocking = false; 112 this.hasShutdownHook = true; 113 this.isInitialized = false; 114 115 // query the system properties 116 117 this.containerConfigValue = System.getProperty( 118 APPLICATION_CONFIG, 119 this.containerConfigValue 120 ); 121 122 this.applicationName = System.getProperty( 123 APPLICATION_NAME, 124 this.applicationName 125 ); 126 127 this.applicationHome = System.getProperty( 128 APPLICATION_HOME, 129 this.applicationHome 130 ); 131 132 this.tempHome = System.getProperty( 133 APPLICATION_TEMP, 134 this.tempHome 135 ); 136 } 137 138 /** 139 * Constructor 140 * 141 * The following command line parameters are supported 142 * <ul> 143 * <li>--yaafi.cli.applicationName name</li> 144 * <li>--yaafi.cli.applicationHome dir</li> 145 * <li>--yaafi.cli.tempHome dir</li> 146 * <li>--yaafi.cli.isBlocking [true|false]</li> 147 * <li>--yaafi.cli.hasShutdownHook [true|false]</li> 148 * <li>--yaafi.cli.config file</li> 149 * </ul> 150 * 151 * @param args the command line arguments 152 */ 153 public Main( String[] args ) 154 { 155 this(); 156 157 this.args = args; 158 159 // parse the command line 160 161 Getopt getopt = new Getopt(this.args); 162 163 this.setApplicationName( 164 getopt.getStringValue( APPLICATION_NAME, this.getApplicationName() ) 165 ); 166 167 this.setApplicationHome( 168 getopt.getStringValue( APPLICATION_HOME, this.getApplicationHome() ) 169 ); 170 171 this.setTempHome( 172 getopt.getStringValue( APPLICATION_TEMP, this.getTempHome() ) 173 ); 174 175 this.setContainerConfigValue( 176 getopt.getStringValue( APPLICATION_CONFIG, this.getContainerConfigValue() ) 177 ); 178 179 this.setIsBlocking( 180 getopt.getBooleanValue( APPLICATION_ISBLOCKING, this.isBlocking ) 181 ); 182 183 this.setHasShutdownHook( 184 getopt.getBooleanValue( APPLICATION_HASSHUTDOWNHOOK, this.hasShutdownHook ) 185 ); 186 } 187 188 /** 189 * The main method. 190 * 191 * @param args Command line arguments 192 * @throws Exception the execution failed 193 */ 194 public static void main( String[] args ) throws Exception 195 { 196 int exitCode = 0; 197 198 Main impl = new Main(args); 199 200 try 201 { 202 impl.run(); 203 } 204 catch (Throwable t) 205 { 206 exitCode = 1; 207 } 208 209 System.exit(exitCode); 210 } 211 212 /** 213 * Determines the file location of the given name. If the name denotes 214 * a relative file location it will be rsolved using the application 215 * home directory. 216 * 217 * @param baseDir the base directory 218 * @param name the filename 219 * @return the file 220 */ 221 public static File makeAbsoluteFile( File baseDir, String name ) 222 { 223 File result = new File(name); 224 225 if( !result.isAbsolute() ) 226 { 227 result = new File( baseDir, name ); 228 } 229 230 return result; 231 } 232 233 /** 234 * Dispose the YAAFI container 235 */ 236 237 public synchronized void dispose() 238 { 239 this.shutdown(); 240 } 241 242 /** 243 * Runs the instance by initializing it and potentially blocking 244 * the invoking thread depending on the configuration. 245 * 246 * @see java.lang.Runnable#run() 247 */ 248 public void run() 249 { 250 try 251 { 252 this.initialize(); 253 this.onWait(); 254 } 255 catch (Throwable t) 256 { 257 String msg = "Failed to run " + this.getClass().getName(); 258 this.getLogger().error(msg,t); 259 throw new RuntimeException(t.getMessage()); 260 } 261 } 262 263 /** 264 * Depending on the configuration this method might block 265 * the calling thread or return immediatly. We currently 266 * poll a volatile variable which is not the most elegant 267 * solution. 268 */ 269 public void onWait() 270 { 271 while( this.isBlocking() && this.isInitialized() ) 272 { 273 try 274 { 275 Thread.sleep(Main.SLEEP_TIME); 276 } 277 catch (InterruptedException e) 278 { 279 // ignore 280 } 281 } 282 } 283 284 /** 285 * Locates the file for the given file name. 286 * @param fileName the filename 287 * @return an absolute file 288 */ 289 public File makeAbsoluteFile( String fileName ) 290 { 291 return Main.makeAbsoluteFile( 292 new File(this.getApplicationHome()), 293 fileName 294 ); 295 } 296 297 /** 298 * Locates the file for the given file name. 299 * @param fileName the filename 300 * @return an absolute path 301 */ 302 public String makeAbsolutePath( String fileName ) 303 { 304 return Main.makeAbsoluteFile( 305 new File(this.getApplicationHome()), 306 fileName 307 ).getAbsolutePath(); 308 } 309 310 ///////////////////////////////////////////////////////////////////////// 311 // Generated getters & setters 312 ///////////////////////////////////////////////////////////////////////// 313 314 /** 315 * @return Returns the ServiceContainer interface 316 */ 317 public ServiceContainer getServiceContainer() 318 { 319 return this.container; 320 } 321 322 /** 323 * @return Returns the ServiceManager interface 324 */ 325 public ServiceManager getServiceManager() 326 { 327 return this.container; 328 } 329 330 /** 331 * @return Returns the applicationHome. 332 */ 333 public String getApplicationHome() 334 { 335 return this.applicationHome; 336 } 337 338 /** 339 * @param applicationHome The applicationHome to set. 340 */ 341 public void setApplicationHome(String applicationHome) 342 { 343 this.applicationHome = applicationHome; 344 } 345 346 /** 347 * @return Returns the containerConfigValue. 348 */ 349 public String getContainerConfigValue() 350 { 351 return containerConfigValue; 352 } 353 354 /** 355 * @param containerConfigValue The containerConfigValue to set. 356 */ 357 public void setContainerConfigValue(String containerConfigValue) 358 { 359 this.containerConfigValue = containerConfigValue; 360 } 361 362 /** 363 * @return Returns the isBlocking. 364 */ 365 public boolean isBlocking() 366 { 367 return isBlocking; 368 } 369 370 /** 371 * @param isBlocking The isBlocking to set. 372 */ 373 public void setIsBlocking(boolean isBlocking) 374 { 375 this.isBlocking = isBlocking; 376 } 377 378 /** 379 * @param isBlocking The isBlocking to set. 380 */ 381 public void setIsBlocking(Boolean isBlocking) 382 { 383 this.isBlocking = isBlocking.booleanValue(); 384 } 385 386 /** 387 * @param isBlocking The isBlocking to set. 388 */ 389 public void setIsBlocking(String isBlocking) 390 { 391 this.isBlocking = Boolean.valueOf(isBlocking).booleanValue(); 392 } 393 394 /** 395 * @return Returns the tempHome. 396 */ 397 public String getTempHome() 398 { 399 return this.tempHome; 400 } 401 402 /** 403 * @param tempHome The tempHome to set. 404 */ 405 public void setTempHome(String tempHome) 406 { 407 this.tempHome = tempHome; 408 } 409 410 /** 411 * @return Returns the logger. 412 */ 413 public Logger getLogger() 414 { 415 return this.logger; 416 } 417 418 /** 419 * @param logger The logger to set. 420 */ 421 public void setLogger(Logger logger) 422 { 423 this.logger = logger; 424 } 425 426 /** 427 * @return Returns the applicationName. 428 */ 429 public String getApplicationName() 430 { 431 return applicationName; 432 } 433 434 /** 435 * @param applicationName The applicationName to set. 436 */ 437 public void setApplicationName(String applicationName) 438 { 439 this.applicationName = applicationName; 440 } 441 442 /** 443 * @return Returns the args. 444 */ 445 public String [] getArgs() 446 { 447 return args; 448 } 449 /** 450 * @param args The args to set. 451 */ 452 public void setArgs(String [] args) 453 { 454 this.args = args; 455 } 456 457 /** 458 * @return Returns the hasShutdownHook. 459 */ 460 public boolean hasShutdownHook() 461 { 462 return hasShutdownHook; 463 } 464 465 /** 466 * @param hasShutdownHook The hasShutdownHook to set. 467 */ 468 public void setHasShutdownHook(boolean hasShutdownHook) 469 { 470 this.hasShutdownHook = hasShutdownHook; 471 } 472 473 /** 474 * @param hasShutdownHook The hasShutdownHook to set. 475 */ 476 public void setHasShutdownHook(Boolean hasShutdownHook) 477 { 478 this.hasShutdownHook = hasShutdownHook.booleanValue(); 479 } 480 481 /** 482 * @param hasShutdownHook The hasShutdownHook to set. 483 */ 484 public void setHasShutdownHook(String hasShutdownHook) 485 { 486 this.hasShutdownHook = Boolean.valueOf(hasShutdownHook).booleanValue(); 487 } 488 489 /** 490 * @see java.lang.Object#toString() 491 */ 492 public String toString() 493 { 494 StringBuffer result = new StringBuffer(); 495 StringBuffer argsLine = new StringBuffer(); 496 497 result.append(getClass().getName() + "@" + Integer.toHexString(hashCode())); 498 499 result.append('['); 500 result.append("workingDir=" + new File("").getAbsolutePath()); 501 result.append(','); 502 503 result.append("args="); 504 505 for( int i=0; i<this.getArgs().length; i++ ) 506 { 507 argsLine.append( this.getArgs()[i] ); 508 509 if( (i+1) < this.getArgs().length ) 510 { 511 argsLine.append( " " ); 512 } 513 } 514 515 result.append( argsLine.toString() ); 516 result.append(','); 517 518 result.append("applicationName=" + this.getApplicationName()); 519 result.append(','); 520 result.append("applicationHome=" + this.getApplicationHome()); 521 result.append(','); 522 result.append("tempHome=" + this.getTempHome()); 523 result.append(','); 524 result.append("logger=" + this.getLogger().getClass().getName()); 525 result.append(','); 526 result.append("isBlocking=" + this.isBlocking); 527 result.append(','); 528 result.append("hasShutdownHook=" + this.hasShutdownHook()); 529 result.append(','); 530 result.append("containerConfigValue=" + this.getContainerConfigValue()); 531 result.append(']'); 532 533 return result.toString(); 534 } 535 536 /** 537 * @return Returns the isInitialized. 538 */ 539 public boolean isInitialized() 540 { 541 return isInitialized; 542 } 543 544 ///////////////////////////////////////////////////////////////////////// 545 // Implementation 546 ///////////////////////////////////////////////////////////////////////// 547 548 /** 549 * @param isInitialized The isInitialized to set. 550 */ 551 protected void setInitialized(boolean isInitialized) 552 { 553 this.isInitialized = isInitialized; 554 } 555 556 /** 557 * Initialize the instance 558 * 559 * @throws Exception the initialization failed 560 */ 561 public void initialize() throws Exception 562 { 563 this.getLogger().debug( "Initializing " + this.getClass().getName() ); 564 565 ServiceContainerConfiguration config = new ServiceContainerConfiguration(); 566 567 // intialize the Avalon container 568 569 config.setLogger( this.getLogger() ); 570 config.setApplicationRootDir( this.getApplicationHome() ); 571 config.setTempRootDir( this.getTempHome() ); 572 config.loadContainerConfiguration( this.getContainerConfigValue(), "auto" ); 573 574 this.container = ServiceContainerFactory.create( config ); 575 576 // initialize shutdown hook of JVM for a server application 577 578 if( this.hasShutdownHook() ) 579 { 580 this.getLogger().debug( "Registering shutdown hook" ); 581 Shutdown shutdown = new Shutdown( this ); 582 this.shutdownThread = new Thread( shutdown, "ShutdownThread" ); 583 Runtime.getRuntime().addShutdownHook( this.shutdownThread ); 584 } 585 586 this.setInitialized(true); 587 } 588 589 /** 590 * Terminates the instance 591 */ 592 protected void shutdown() 593 { 594 if( !this.isInitialized()) 595 { 596 return; 597 } 598 599 this.getLogger().debug( "Terminating " + this.getClass().getName() ); 600 601 try 602 { 603 // wait for the shutdown thread 604 605 if( this.shutdownThread != null ) 606 { 607 try 608 { 609 this.getLogger().debug( "Waiting for shutdown handler thread to terminate" ); 610 this.shutdownThread.join(JOIN_TIME); 611 this.shutdownThread = null; 612 this.getLogger().debug( "Shutdown handler thread is terminated" ); 613 } 614 catch (InterruptedException e) 615 { 616 // nothing to do 617 } 618 } 619 620 // dispose the service container 621 622 if( this.getServiceContainer() != null ) 623 { 624 this.getServiceContainer().dispose(); 625 this.container = null; 626 } 627 628 this.setInitialized(false); 629 } 630 631 catch (Exception e) 632 { 633 String msg = "Failed to terminate " + this.getClass().getName(); 634 this.getLogger().error(msg,e); 635 } 636 } 637 }