001 /* 002 * Copyright 2004-2005 Stephen McConnell. 003 * Copyright 2004-2005 Niclas Hedhman. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. 015 * 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 package net.dpml.transit; 021 022 import java.io.File; 023 import java.io.FileOutputStream; 024 import java.io.IOException; 025 import java.io.OutputStreamWriter; 026 import java.io.PrintWriter; 027 028 import net.dpml.transit.link.LinkManager; 029 import net.dpml.transit.monitor.LoggingAdapter; 030 import net.dpml.transit.monitor.RepositoryMonitorRouter; 031 import net.dpml.transit.monitor.CacheMonitorRouter; 032 import net.dpml.transit.monitor.NetworkMonitorRouter; 033 import net.dpml.transit.model.TransitModel; 034 035 import net.dpml.lang.UnknownKeyException; 036 import net.dpml.util.Logger; 037 038 /** 039 * The Transit class manages the establishment of a singleton transit instance 040 * together with a service supporting the deployment of a application plugin and 041 * access to transit monitor routers. 042 * 043 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 044 * @version 1.1.0 045 */ 046 public final class Transit 047 { 048 //------------------------------------------------------------------ 049 // static 050 //------------------------------------------------------------------ 051 052 /** 053 * DPML home key. 054 */ 055 public static final String HOME_KEY = "dpml.home"; 056 057 /** 058 * DPML data key. 059 */ 060 public static final String DATA_KEY = "dpml.data"; 061 062 /** 063 * DPML prefs key. 064 */ 065 public static final String PREFS_KEY = "dpml.prefs"; 066 067 /** 068 * Transit system key. 069 */ 070 public static final String SYSTEM_KEY = "dpml.system"; 071 072 /** 073 * Transit share key (alias to dpml.system). 074 */ 075 public static final String SHARE_KEY = "dpml.share"; 076 077 /** 078 * DPML environment variable string. 079 */ 080 public static final String HOME_SYMBOL = "DPML_HOME"; 081 082 /** 083 * DPML environment variable string. 084 */ 085 public static final String SYSTEM_SYMBOL = "DPML_SYSTEM"; 086 087 /** 088 * The DPML home directory established via assesment of the the ${dpml.home} 089 * system property and the DPML_HOME environment variable. 090 */ 091 public static final File DPML_HOME; 092 093 /** 094 * If a system property named "dpml.system" is defined then the value 095 * is assigned otherwise the implementation will look for an environment 096 * variable named "DPML_SYSTEM". 097 */ 098 public static final File DPML_SYSTEM; 099 100 /** 101 * The Transit personal data directory. The location of this diectory is system 102 * dependent. 103 */ 104 public static final File DPML_DATA; 105 106 /** 107 * The Transit personal preferences directory. The location of this diectory is system 108 * dependent. 109 */ 110 public static final File DPML_PREFS; 111 112 /** 113 * The Transit system version. 114 */ 115 public static final String VERSION = "1.1.0"; 116 117 static 118 { 119 System.setProperty( "java.protocol.handler.pkgs", "net.dpml.transit" ); 120 System.setProperty( "dpml.transit.version", VERSION ); 121 122 DPML_HOME = resolveHomeDirectory(); 123 DPML_SYSTEM = resolveSystemDirectory( DPML_HOME ); 124 DPML_DATA = resolveDataDirectory( DPML_HOME ); 125 DPML_PREFS = resolvePreferencesDirectory( DPML_HOME ); 126 127 System.setProperty( SYSTEM_KEY, DPML_SYSTEM.getAbsolutePath() ); 128 System.setProperty( SHARE_KEY, DPML_SYSTEM.getAbsolutePath() ); 129 System.setProperty( HOME_KEY, DPML_HOME.getAbsolutePath() ); 130 System.setProperty( DATA_KEY, DPML_DATA.getAbsolutePath() ); 131 System.setProperty( PREFS_KEY, DPML_PREFS.getAbsolutePath() ); 132 } 133 134 /** 135 * Returns the singleton instance of the transit system. If Transit 136 * has not been initialized a the transit model will be resolved 137 * using the System property <tt>dpml.transit.profile</tt>. 138 * @return the singleton transit instance 139 * @exception TransitError if an error occurs during establishment 140 * @see DefaultTransitModel#getDefaultModel 141 */ 142 public static Transit getInstance() throws TransitError 143 { 144 synchronized( Transit.class ) 145 { 146 if( m_INSTANCE == null ) 147 { 148 try 149 { 150 Logger logger = new LoggingAdapter( "dpml.transit" ); 151 TransitModel model = DefaultTransitModel.getDefaultModel( logger ); 152 return getInstance( model ); 153 } 154 catch( Throwable e ) 155 { 156 String message = e.getMessage(); 157 Throwable cause = e.getCause(); 158 throw new TransitError( message, cause ); 159 } 160 } 161 else 162 { 163 return m_INSTANCE; 164 } 165 } 166 } 167 168 /** 169 * Returns the singleton instance of the transit system. If this method 170 * has already been invoked the server and monitor argument will be ignored. 171 * 172 * @param model the activate transit model 173 * @return the singleton transit instance 174 * @exception IOException if an error occurs during establishment 175 * @exception TransitAlreadyInitializedException if Transit is already initialized 176 */ 177 public static Transit getInstance( TransitModel model ) 178 throws IOException, TransitAlreadyInitializedException 179 { 180 synchronized( Transit.class ) 181 { 182 if( m_INSTANCE == null ) 183 { 184 m_INSTANCE = new Transit( model ); 185 186 // before returning from this method we need to give the transit 187 // subsystems a chance to complete initialization actions that 188 // are themselves dependent on an establish Transit instance 189 190 m_INSTANCE.getTransitContext().initialize(); 191 return m_INSTANCE; 192 } 193 else 194 { 195 final String error = 196 "Transit has already been initialized."; 197 throw new TransitAlreadyInitializedException( error ); 198 } 199 } 200 } 201 202 //------------------------------------------------------------------ 203 // state 204 //------------------------------------------------------------------ 205 206 /** 207 * Singleton repository monitor router. 208 */ 209 private RepositoryMonitorRouter m_repositoryMonitor; 210 211 /** 212 * Singleton cache monitor router. 213 */ 214 private CacheMonitorRouter m_cacheMonitor; 215 216 /** 217 * Singleton network monitor router. 218 */ 219 private NetworkMonitorRouter m_networkMonitor; 220 221 /** 222 * PrintWriter where operations troubleshooting messages 223 * can be written to. 224 */ 225 private PrintWriter m_logWriter; 226 227 private SecuredTransitContext m_context; 228 229 /** 230 * Return the singleton transit content. 231 * @return the context instance 232 * @exception IllegalStateException if transit has not been initialized 233 */ 234 SecuredTransitContext getTransitContext() throws IllegalStateException 235 { 236 if( null == m_context ) 237 { 238 final String error = 239 "Transit context has not been initialized."; 240 throw new IllegalStateException( error ); 241 } 242 else 243 { 244 return m_context; 245 } 246 } 247 248 /** 249 * Private constructor of a transit instance. 250 * @param model the active transit model 251 * @exception TransitException if an establishment error occurs 252 */ 253 private Transit( TransitModel model ) throws TransitException 254 { 255 // 256 // create the transit context 257 // 258 259 try 260 { 261 m_context = SecuredTransitContext.create( model ); 262 } 263 catch( TransitException e ) 264 { 265 throw e; 266 } 267 catch( Throwable e ) 268 { 269 final String error = "Unable to construct transit context."; 270 throw new TransitException( error, e ); 271 } 272 273 // 274 // setup the monitors 275 // 276 277 m_repositoryMonitor = new RepositoryMonitorRouter(); 278 m_cacheMonitor = new CacheMonitorRouter(); 279 m_networkMonitor = new NetworkMonitorRouter(); 280 281 try 282 { 283 // Setting up a temporary directory for Transit. 284 285 File temp = new File( DPML_DATA, "temp" ); 286 temp.mkdirs(); 287 288 // Setting up a permanent output troubleshooting resource 289 // for Transit. 290 File logs = new File( DPML_DATA, "logs" ); 291 File logDir = new File( logs, "transit" ); 292 logDir.mkdirs(); 293 File logFile = new File( logDir, "transit.log" ); 294 FileOutputStream fos = new FileOutputStream( logFile ); 295 OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF-8" ); 296 m_logWriter = new PrintWriter( osw, true ); 297 } 298 catch( Throwable e ) 299 { 300 final String error = "Unable to construct transit instance."; 301 throw new TransitException( error, e ); 302 } 303 } 304 305 /** 306 * Return the current cache directory. 307 * @return the cache directory. 308 */ 309 public File getCacheDirectory() 310 { 311 return getTransitContext().getCacheHandler().getCacheDirectory(); 312 } 313 314 /** 315 * Return the link manager. 316 * @return the link manager 317 */ 318 public LinkManager getLinkManager() 319 { 320 return getTransitContext().getLinkManager(); 321 } 322 323 /** 324 * Return the cache layout. 325 * @return the layout 326 */ 327 public Layout getCacheLayout() 328 { 329 return getTransitContext().getCacheLayout(); 330 } 331 332 /** 333 * Return a layout object matching the supplied identifier. 334 * @param id the layout identifier 335 * @return the layout object 336 * @exception UnknownKeyException if the supplied layout id is unknown 337 * @exception IOException if an IO error occurs 338 */ 339 public Layout getLayout( String id ) throws UnknownKeyException, IOException 340 { 341 return getTransitContext().getLayout( id ); 342 } 343 344 /** 345 * Return the Transit repository service. 346 * @return the repository service 347 * @exception IllegalStateException if Transit has not been initialized 348 */ 349 //public Repository getRepository() throws IllegalStateException 350 //{ 351 // return getTransitContext().getRepository(); 352 //} 353 354 /** 355 * Returns a reference to the repository monitor router. Client application 356 * may use the router to add, remove or replace existing monitors. 357 * @return the repository monitor router 358 */ 359 public RepositoryMonitorRouter getRepositoryMonitorRouter() 360 { 361 return m_repositoryMonitor; 362 } 363 364 /** 365 * Returns a reference to the cache monitor router. Client application 366 * may use the router to add, remove or replace existing monitors. 367 * @return the cache monitor router 368 */ 369 public CacheMonitorRouter getCacheMonitorRouter() 370 { 371 return m_cacheMonitor; 372 } 373 374 /** 375 * Returns a reference to the netowork monitor router. Client application 376 * may use the router to add, remove or replace existing monitors. 377 * @return the network monitor router 378 */ 379 public NetworkMonitorRouter getNetworkMonitorRouter() 380 { 381 return m_networkMonitor; 382 } 383 384 /** Returns the LogWriter for the Transit system. 385 * This writer should only be used to report information that 386 * should not be output to the user in the course of normal 387 * execution but can aid to determine what has gone wrong in 388 * Transit, such as configuration problems, network problems 389 * and security issues. 390 * @return a PrintWriter where troubleshooting information can 391 * be written to. 392 */ 393 public PrintWriter getLogWriter() 394 { 395 return m_logWriter; 396 } 397 398 /** 399 * Resolve the DPML home directory using assesment of the the ${dpml.home} 400 * system property, the DPML_HOME environment variable. If DPML_HOME is 401 * not declared, the behaviour is platform specific. If the os is Windows, 402 * the value returned is equivalent to $APPDATA\DPML whereas Unix environment 403 * will return ${user.home}/.dpml. The value returned may be overriden by 404 * setting a 'dpml.home' system property. 405 * 406 * @return the DPML home directory 407 */ 408 private static File resolveHomeDirectory() 409 { 410 String home = System.getProperty( HOME_KEY ); 411 if( null != home ) 412 { 413 return new File( home ); 414 } 415 home = Environment.getEnvVariable( HOME_SYMBOL ); 416 if( null != home ) 417 { 418 return new File( home ); 419 } 420 String os = System.getProperty( "os.name" ).toLowerCase(); 421 if( os.indexOf( "win" ) >= 0 ) 422 { 423 home = Environment.getEnvVariable( "APPDATA" ); 424 File data = new File( home ); 425 return new File( data, "DPML" ); 426 } 427 else 428 { 429 File user = new File( System.getProperty( "user.home" ) ); 430 return new File( user, ".dpml" ); 431 } 432 } 433 434 /** 435 * Resolve the DPML system home directory. If a system property 436 * named "dpml.system" is defined then the value as a file is 437 * returned otherwise the implementation will look for an environment 438 * variable named "DPML_SYSTEM" which if defined will be 439 * returned as a file otherwise a value equivalent to 440 * <tt>${dpml.home}/share</tt> will be returned. 441 * 442 * @param dpmlHomeDir the default DPML_HOME value 443 * @return the transit system directory 444 */ 445 private static File resolveSystemDirectory( File dpmlHomeDir ) 446 { 447 String home = System.getProperty( SYSTEM_KEY ); 448 if( null != home ) 449 { 450 return new File( home ); 451 } 452 home = Environment.getEnvVariable( SYSTEM_SYMBOL ); 453 if( null != home ) 454 { 455 return new File( home ); 456 } 457 else 458 { 459 return new File( dpmlHomeDir, "share" ); 460 } 461 } 462 463 /** 464 * Resolve the DPML data directory. The value 465 * returned may be overriden by setting a 'dpml.data' 466 * system property otherwise the default value returned 467 * will be equivalent to <tt>${dpml.home}/data</tt>. 468 * 469 * @param dir the default DPML_HOME value 470 * @return the transit personal data directory 471 */ 472 private static File resolveDataDirectory( File dir ) 473 { 474 String path = System.getProperty( DATA_KEY ); 475 if( null != path ) 476 { 477 return new File( path ); 478 } 479 else 480 { 481 return new File( dir, "data" ); 482 } 483 } 484 485 /** 486 * Resolve the DPML prefs directory. The value 487 * returned may be overriden by setting a 'dpml.prefs' 488 * system property otherwise the default value returned 489 * will be equivalent to <tt>${dpml.home}/prefs</tt>. 490 * 491 * @param dir the default DPML_HOME value 492 * @return the transit personal data directory 493 */ 494 private static File resolvePreferencesDirectory( File dir ) 495 { 496 String path = System.getProperty( PREFS_KEY ); 497 if( null != path ) 498 { 499 return new File( path ); 500 } 501 else 502 { 503 return new File( dir, "prefs" ); 504 } 505 } 506 507 //------------------------------------------------------------------ 508 // static internal 509 //------------------------------------------------------------------ 510 511 /** 512 * Singleton transit instance. 513 */ 514 private static Transit m_INSTANCE; 515 516 }