001 /* 002 * Copyright 2005 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.depot; 020 021 import java.io.File; 022 import java.net.URL; 023 import java.net.URI; 024 import java.rmi.RMISecurityManager; 025 import java.util.ArrayList; 026 import java.util.Date; 027 import java.util.logging.LogManager; 028 029 import net.dpml.transit.Disposable; 030 import net.dpml.transit.Transit; 031 import net.dpml.transit.TransitError; 032 import net.dpml.transit.DefaultTransitModel; 033 import net.dpml.transit.model.TransitModel; 034 import net.dpml.transit.monitor.Adapter; 035 import net.dpml.transit.monitor.LoggingAdapter; 036 import net.dpml.transit.monitor.RepositoryMonitorAdapter; 037 import net.dpml.transit.monitor.CacheMonitorAdapter; 038 import net.dpml.transit.monitor.NetworkMonitorAdapter; 039 040 import net.dpml.lang.Enum; 041 import net.dpml.lang.PID; 042 import net.dpml.lang.Part; 043 044 import net.dpml.util.Logger; 045 import net.dpml.util.PropertyResolver; 046 047 /** 048 * CLI hander for the depot package. 049 * 050 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 051 * @version 1.2.0 052 */ 053 public final class Main //implements ShutdownHandler 054 { 055 private static Main m_MAIN; 056 private static final PID PROCESS_ID = new PID(); 057 058 private Object m_plugin; 059 private boolean m_debug = false; 060 private boolean m_trace = false; 061 062 /** 063 * Processes command line options to establish the command handler plugin to deploy. 064 * Command parameters recognixed by the console include the following: 065 * <ul> 066 * <li>-Ddpml.depot.addplication=transit|station|metro|build</li> 067 * <li>-debug</li> 068 * </ul> 069 * @param args the command line argument array 070 * @exception Exception if a error occurs 071 */ 072 public static void main( String[] args ) 073 throws Exception 074 { 075 if( null != m_MAIN ) 076 { 077 final String error = 078 "Console already established."; 079 throw new IllegalArgumentException( error ); 080 } 081 else 082 { 083 m_MAIN = new Main( args ); 084 } 085 } 086 087 private Main( String[] arguments ) 088 { 089 // 090 // check for debug and trace cli options 091 // 092 093 String[] args = arguments; 094 095 if( CLIHelper.isOptionPresent( args, "-trace" ) ) 096 { 097 args = CLIHelper.consolidate( args, "-trace" ); 098 System.setProperty( "dpml.trace", "true" ); 099 m_trace = true; 100 } 101 102 if( CLIHelper.isOptionPresent( args, "-debug" ) ) 103 { 104 args = CLIHelper.consolidate( args, "-debug" ); 105 System.setProperty( "dpml.debug", "true" ); 106 m_debug = true; 107 } 108 109 args = processSystemProperties( args ); 110 111 // 112 // handle cli sub-system establishment 113 // 114 115 Command command = getCommand( args ); 116 if( Command.STATION.equals( command ) ) 117 { 118 handleStation( args ); 119 } 120 else 121 { 122 if( null == System.getProperty( "dpml.logging.config" ) ) 123 { 124 if( m_trace ) 125 { 126 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/trace" ); 127 } 128 else if( m_debug ) 129 { 130 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/debug" ); 131 } 132 else 133 { 134 System.setProperty( "dpml.logging.config", "local:properties:dpml/transit/default" ); 135 } 136 } 137 138 if( m_debug || m_trace ) 139 { 140 for( int i=0; i<arguments.length; i++ ) 141 { 142 getLogger().debug( "arg[" + i + "]: " + arguments[i] ); 143 } 144 } 145 146 if( Command.BUILD.equals( command ) ) 147 { 148 handleBuild( args ); 149 } 150 else if( Command.TRANSIT.equals( command ) ) 151 { 152 handleTransit( args ); 153 } 154 else if( Command.METRO.equals( command ) ) 155 { 156 handleMetro( args ); 157 } 158 else 159 { 160 final String error = 161 "Missing application key '" + APPLICATION_KEY + "'."; 162 System.err.println( error ); 163 System.exit( 1 ); 164 } 165 } 166 } 167 168 private void handleBuild( String[] args ) 169 { 170 if( getLogger().isTraceEnabled() ) 171 { 172 getLogger().trace( "launching builder" ); 173 } 174 String name = "build"; 175 String spec = "link:part:dpml/depot/dpml-library-build"; 176 handlePlugin( name, spec, args, false ); 177 } 178 179 private void handleMetro( String[] args ) 180 { 181 if( getLogger().isTraceEnabled() ) 182 { 183 getLogger().trace( "launching metro" ); 184 } 185 String name = "exec"; 186 String spec = "link:part:dpml/station/dpml-station-exec"; 187 handlePlugin( name, spec, args, true ); 188 } 189 190 private void handleTransit( String[] args ) 191 { 192 String name = "transit"; 193 String spec = "link:part:dpml/transit/dpml-transit-console"; 194 handlePlugin( name, spec, args, false ); 195 } 196 197 private void handleStation( String[] args ) 198 { 199 new File( Transit.DPML_DATA, "logs/station" ).mkdirs(); 200 if( CLIHelper.isOptionPresent( args, "-server" ) ) 201 { 202 if( m_trace ) 203 { 204 System.setProperty( "dpml.logging.level", "FINEST" ); 205 } 206 else if( m_debug ) 207 { 208 System.setProperty( "dpml.logging.level", "FINE" ); 209 } 210 if( getLogger().isTraceEnabled() ) 211 { 212 getLogger().trace( "launching station in server mode" ); 213 } 214 String name = "station"; 215 args = CLIHelper.consolidate( args, "-server" ); 216 String spec = "link:part:dpml/station/dpml-station-server"; 217 handlePlugin( name, spec, args, true ); 218 } 219 else 220 { 221 if( getLogger().isTraceEnabled() ) 222 { 223 getLogger().trace( "launching station in client mode" ); 224 } 225 String name = "station"; 226 String spec = "link:part:dpml/station/dpml-station-console"; 227 handlePlugin( name, spec, args, false ); 228 } 229 } 230 231 private void handlePlugin( String name, String spec, String[] args, boolean wait ) 232 { 233 System.setSecurityManager( new RMISecurityManager() ); 234 TransitModel model = getTransitModel( args ); 235 boolean waitForCompletion = deployHandler( model, name, spec, args, wait ); 236 if( !waitForCompletion ) 237 { 238 if( m_plugin instanceof Disposable ) 239 { 240 Disposable disposable = (Disposable) m_plugin; 241 disposable.dispose(); 242 } 243 if( model instanceof DefaultTransitModel ) 244 { 245 DefaultTransitModel disposable = (DefaultTransitModel) model; 246 disposable.dispose(); 247 } 248 System.exit( 0 ); 249 } 250 } 251 252 private boolean deployHandler( 253 TransitModel model, String command, String path, String[] args, boolean waitFor ) 254 { 255 Logger logger = getLogger(); 256 if( logger.isDebugEnabled() ) 257 { 258 logger.debug( "date: " + new Date() ); 259 logger.debug( "system: " + command ); 260 logger.debug( "uri: " + path ); 261 logger.debug( "args: [" + toString( args ) + "]" ); 262 logger.debug( "system classloader: [" 263 + System.identityHashCode( ClassLoader.getSystemClassLoader() ) 264 + "]" ); 265 } 266 Logger log = resolveLogger( logger, command ); 267 try 268 { 269 URI uri = new URI( path ); 270 Transit transit = Transit.getInstance( model ); 271 setupMonitors( transit, (Adapter) logger ); 272 273 Part part = Part.load( uri, true ); 274 m_plugin = 275 part.instantiate( 276 new Object[] 277 { 278 model, 279 args, 280 log 281 } 282 ); 283 } 284 catch( GeneralException e ) 285 { 286 getLogger().error( e.getMessage() ); 287 System.exit( 1 ); 288 } 289 catch( Exception e ) 290 { 291 Throwable cause = e.getCause(); 292 if( ( null != cause ) && ( cause instanceof GeneralException ) ) 293 { 294 getLogger().error( cause.getMessage() ); 295 System.exit( 1 ); 296 } 297 else 298 { 299 getLogger().error( e.getMessage(), e.getCause() ); 300 System.exit( 1 ); 301 } 302 } 303 catch( Throwable e ) 304 { 305 final String error = 306 "Deloyment failure." 307 + "\nTarget: " + command 308 + "\n URI: " + path; 309 getLogger().error( error, e ); 310 System.exit( 1 ); 311 } 312 313 if( m_plugin instanceof Runnable ) 314 { 315 getLogger().debug( "starting " + m_plugin.getClass().getName() ); 316 Thread thread = new Thread( (Runnable) m_plugin ); 317 thread.start(); 318 setShutdownHook( thread ); 319 return true; 320 } 321 else 322 { 323 getLogger().debug( "deployed " + m_plugin.getClass().getName() ); 324 return waitFor; 325 } 326 } 327 328 private Logger resolveLogger( Logger logger, String command ) 329 { 330 String partition = System.getProperty( "dpml.station.partition", null ); 331 if( null != partition ) 332 { 333 return new LoggingAdapter( partition ); 334 } 335 else 336 { 337 return logger.getChildLogger( command ); 338 } 339 } 340 341 private TransitModel getTransitModel( String[] args ) 342 { 343 final String key = "dpml.transit.model"; 344 String property = null; 345 for( int i=0; i<args.length; i++ ) 346 { 347 String arg = args[i]; 348 if( arg.startsWith( "-D" + key + "=" ) ) 349 { 350 property = arg.substring( 21 ); 351 break; 352 } 353 } 354 355 if( null != property ) 356 { 357 if( property.startsWith( "registry:" ) ) 358 { 359 try 360 { 361 return (TransitModel) new URL( property ).getContent( 362 new Class[]{TransitModel.class} ); 363 } 364 catch( Exception e ) 365 { 366 final String error = 367 "Unable to resolve registry reference: " + property; 368 throw new TransitError( error, e ); 369 } 370 } 371 else 372 { 373 final String error = 374 "System property value for the key ': " 375 + key 376 + "' contains an unrecognized value: " 377 + property; 378 throw new TransitError( error ); 379 } 380 } 381 382 // 383 // otherwise let Transit handle model creation 384 // 385 386 try 387 { 388 Logger logger = getLogger().getChildLogger( "transit" ); 389 return DefaultTransitModel.getDefaultModel( logger ); 390 } 391 catch( Exception e ) 392 { 393 final String error = 394 "Transit model establishment failure."; 395 throw new TransitError( error, e ); 396 } 397 } 398 399 private static Logger getLogger() 400 { 401 if( null == m_LOGGER ) 402 { 403 try 404 { 405 LogManager.getLogManager().readConfiguration(); 406 } 407 catch( Throwable e ) 408 { 409 e.printStackTrace(); 410 } 411 String category = System.getProperty( "dpml.logging.category", "depot" ); 412 m_LOGGER = new LoggingAdapter( java.util.logging.Logger.getLogger( category ) ); 413 } 414 return m_LOGGER; 415 } 416 417 private String toString( String[] args ) 418 { 419 StringBuffer buffer = new StringBuffer(); 420 for( int i=0; i<args.length; i++ ) 421 { 422 if( i > 0 ) 423 { 424 buffer.append( ", " ); 425 } 426 buffer.append( args[i] ); 427 } 428 return buffer.toString(); 429 } 430 431 /** 432 * For all of the supplied command line arguments, if the 433 * argument is in the form -Dabc=def then extract the argument from 434 * the array and apply it as a system property. All non-system property 435 * arguments are included in the returned argument array. 436 * 437 * @param args the supplied commandline arguments including 438 * system property assignments 439 * @return the array of pure command line arguments (excluding 440 * and arg values recognized as system property declarations 441 */ 442 private String[] processSystemProperties( String[] args ) 443 { 444 ArrayList result = new ArrayList(); 445 for( int i=0; i < args.length; i++ ) 446 { 447 String arg = args[i]; 448 int index = arg.indexOf( "=" ); 449 if( index > -1 && arg.startsWith( "-D" ) ) 450 { 451 String name = arg.substring( 2, index ); 452 String raw = arg.substring( index + 1 ); 453 String value = PropertyResolver.resolve( raw ); 454 System.setProperty( name, value ); 455 } 456 else 457 { 458 result.add( arg ); 459 } 460 } 461 return (String[]) result.toArray( new String[0] ); 462 } 463 464 //-------------------------------------------------------------------------- 465 // static utilities for setup of logging manager and root prefs 466 //-------------------------------------------------------------------------- 467 468 /** 469 * Setup the monitors. 470 */ 471 private static void setupMonitors( Transit instance, Adapter adapter ) throws Exception 472 { 473 instance.getRepositoryMonitorRouter().addMonitor( 474 new RepositoryMonitorAdapter( adapter ) ); 475 instance.getCacheMonitorRouter().addMonitor( 476 new CacheMonitorAdapter( adapter ) ); 477 instance.getNetworkMonitorRouter().addMonitor( 478 new NetworkMonitorAdapter( adapter ) ); 479 } 480 481 /** 482 * Create a shutdown hook that will trigger shutdown of the supplied plugin. 483 * @param thread the application thread 484 */ 485 public static void setShutdownHook( final Thread thread ) 486 { 487 // 488 // Create a shutdown hook to trigger clean disposal of the 489 // controller 490 // 491 492 Runtime.getRuntime().addShutdownHook( 493 new Thread() 494 { 495 public void run() 496 { 497 try 498 { 499 thread.interrupt(); 500 } 501 catch( Throwable e ) 502 { 503 boolean ignorable = true; 504 } 505 System.runFinalization(); 506 } 507 } 508 ); 509 } 510 511 /** 512 * DPML build key. 513 */ 514 private static final String BUILD_KEY = "dpml.build"; 515 516 /** 517 * The Depot system version. 518 */ 519 private static final String BUILD_ID = "1.2.0"; 520 521 static 522 { 523 setSystemProperty( "java.protocol.handler.pkgs", "net.dpml.transit" ); 524 setSystemProperty( "java.util.logging.config.class", "net.dpml.util.ConfigurationHandler" ); 525 setSystemProperty( "java.rmi.server.RMIClassLoaderSpi", "net.dpml.depot.DepotRMIClassLoaderSpi" ); 526 setSystemProperty( Transit.SYSTEM_KEY, Transit.DPML_SYSTEM.getAbsolutePath() ); 527 setSystemProperty( Transit.HOME_KEY, Transit.DPML_HOME.getAbsolutePath() ); 528 setSystemProperty( Transit.DATA_KEY, Transit.DPML_DATA.getAbsolutePath() ); 529 setSystemProperty( Transit.PREFS_KEY, Transit.DPML_PREFS.getAbsolutePath() ); 530 setSystemProperty( BUILD_KEY, BUILD_ID ); 531 } 532 533 private static void setSystemProperty( String key, String value ) 534 { 535 if( null == System.getProperty( key ) ) 536 { 537 System.setProperty( key, value ); 538 } 539 } 540 541 private static Logger m_LOGGER = null; 542 543 private Command getCommand( String[] args ) 544 { 545 String ref = getApplicationReference( args ); 546 String app = System.getProperty( APPLICATION_KEY, ref ); 547 return Command.parse( app ); 548 } 549 550 private String getApplicationReference( String[] args ) 551 { 552 String key = "-D" + APPLICATION_KEY + "="; 553 for( int i=0; i<args.length; i++ ) 554 { 555 String arg = args[i]; 556 if( arg.startsWith( key ) ) 557 { 558 return arg.substring( 25 ); 559 } 560 } 561 return null; 562 } 563 564 /** 565 * Application selection key. 566 */ 567 public static final String APPLICATION_KEY = "dpml.depot.application"; 568 569 /** 570 * Application identifier enumeration. 571 */ 572 private static final class Command extends Enum 573 { 574 static final long serialVersionUID = 1L; 575 576 /** 577 * Transit command id. 578 */ 579 public static final Command TRANSIT = new Command( "dpml.transit" ); 580 581 /** 582 * Metro command id. 583 */ 584 public static final Command METRO = new Command( "dpml.metro" ); 585 586 /** 587 * Station command id. 588 */ 589 public static final Command STATION = new Command( "dpml.station" ); 590 591 /** 592 * Builder command id. 593 */ 594 public static final Command BUILD = new Command( "dpml.builder" ); 595 596 /** 597 * Internal constructor. 598 * @param label the enumeration label. 599 */ 600 private Command( String label ) 601 { 602 super( label ); 603 } 604 605 /** 606 * Create a now mode using a supplied mode name. 607 * @param value the mode name 608 * @return the mode 609 * @exception NullPointerException if the supplied value is null 610 * @exception IllegalArgumentException if the supplied value is not recognized 611 */ 612 public static Command parse( String value ) throws NullPointerException, IllegalArgumentException 613 { 614 if( null == value ) 615 { 616 final String error = 617 "Undefined sub-system identifier." 618 + "\nThe depot cli handler must be supplied with an -D" 619 + APPLICATION_KEY + "=[id] where id is one of the value 'dpml.metro', " 620 + "'dpml.transit', 'dpml.station' or 'dpml.build'."; 621 throw new NullPointerException( error ); 622 } 623 if( value.equalsIgnoreCase( "dpml.metro" ) ) 624 { 625 return METRO; 626 } 627 else if( value.equalsIgnoreCase( "dpml.transit" ) ) 628 { 629 return TRANSIT; 630 } 631 else if( value.equalsIgnoreCase( "dpml.station" ) ) 632 { 633 return STATION; 634 } 635 else if( value.equalsIgnoreCase( "dpml.builder" ) ) 636 { 637 return BUILD; 638 } 639 else 640 { 641 final String error = 642 "Unrecognized application id [" + value + "]"; 643 throw new IllegalArgumentException( error ); 644 } 645 } 646 } 647 } 648