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.transit; 020 021 import java.io.File; 022 import java.net.URL; 023 import java.net.URI; 024 import java.rmi.RemoteException; 025 import java.rmi.NoSuchObjectException; 026 import java.rmi.server.UnicastRemoteObject; 027 import java.util.EventObject; 028 import java.util.EventListener; 029 030 import net.dpml.transit.info.CacheDirective; 031 import net.dpml.transit.info.ProxyDirective; 032 import net.dpml.transit.info.TransitDirective; 033 import net.dpml.transit.model.CacheModel; 034 import net.dpml.transit.model.ProxyModel; 035 import net.dpml.transit.model.TransitModel; 036 import net.dpml.transit.model.DisposalEvent; 037 import net.dpml.transit.model.DisposalListener; 038 import net.dpml.transit.monitor.LoggingAdapter; 039 040 import net.dpml.util.EventQueue; 041 import net.dpml.util.Logger; 042 043 /** 044 * The DefaultTransitModel class maintains an active configuration of the 045 * Transit system. 046 * 047 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 048 * @version 1.0.3 049 */ 050 public class DefaultTransitModel extends DefaultModel implements TransitModel 051 { 052 // ------------------------------------------------------------------------ 053 // static 054 // ------------------------------------------------------------------------ 055 056 /** 057 * Default configuration url path. 058 */ 059 public static final String DEFAULT_PROFILE_PATH = "local:xml:dpml/transit/standard"; 060 061 /** 062 * Default configuration url path. 063 */ 064 public static final URI DEFAULT_PROFILE_URI = createStaticURI( DEFAULT_PROFILE_PATH ); 065 066 /** 067 * System property key used to hold an overriding configuration url. 068 */ 069 public static final String PROFILE_KEY = "dpml.transit.profile"; 070 071 /** 072 * Return a model that is restricted to the secure local environment with 073 * no proxy setting or external hosts. 074 * @param logger the logging channel to assign to the model 075 * @return the transit model 076 */ 077 public static DefaultTransitModel getSecureModel( Logger logger ) 078 { 079 try 080 { 081 TransitDirective directive = new TransitDirective( null, new CacheDirective() ); 082 if( logger.isTraceEnabled() ) 083 { 084 ClassLoader system = ClassLoader.getSystemClassLoader(); 085 int id = System.identityHashCode( system ); 086 logger.trace( "system classloader id: " + id ); 087 } 088 EventQueue queue = new EventQueue( logger, "DPML Transit Event Queue" ); 089 return new DefaultTransitModel( queue, logger, directive ); 090 } 091 catch( Exception e ) 092 { 093 final String error = 094 "Unexpected error while constructing static secure model."; 095 throw new RuntimeException( error, e ); 096 } 097 } 098 099 /** 100 * Resolve the transit configuration using the default resource path 101 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 102 * default scenario will be returned. 103 * 104 * @return the transit model 105 * @exception Exception if an error occurs during model construction 106 */ 107 public static DefaultTransitModel getDefaultModel() throws Exception 108 { 109 return getDefaultModel( "transit" ); 110 } 111 112 /** 113 * Resolve the transit configuration using the default resource path 114 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 115 * default scenario will be returned. 116 * 117 * @param category the logging channel category name 118 * @return the transit model 119 * @exception Exception if an error occurs during model construction 120 */ 121 public static DefaultTransitModel getDefaultModel( String category ) throws Exception 122 { 123 LoggingAdapter adapter = new LoggingAdapter( category ); 124 return getDefaultModel( adapter ); 125 } 126 127 /** 128 * Resolve the transit configuration using the default resource path 129 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 130 * default scenario will be returned. 131 * 132 * @param logger the logging channel 133 * @return the transit model 134 * @exception Exception if an error occurs during model construction 135 */ 136 public static DefaultTransitModel getDefaultModel( Logger logger ) throws Exception 137 { 138 String path = System.getProperty( PROFILE_KEY ); 139 if( logger.isTraceEnabled() ) 140 { 141 ClassLoader system = ClassLoader.getSystemClassLoader(); 142 int id = System.identityHashCode( system ); 143 logger.trace( "system classloader id: " + id ); 144 } 145 if( null != path ) 146 { 147 URI uri = Artifact.createArtifact( path ).toURI(); 148 URL url = uri.toURL(); 149 TransitBuilder builder = new TransitBuilder( logger ); 150 TransitDirective directive = builder.load( url ); 151 return new DefaultTransitModel( logger, directive ); 152 } 153 else 154 { 155 File prefs = Transit.DPML_PREFS; 156 File config = new File( prefs, "dpml/transit/xmls/standard.xml" ); 157 if( config.exists() ) 158 { 159 URI uri = config.toURI(); 160 URL url = uri.toURL(); 161 TransitBuilder builder = new TransitBuilder( logger ); 162 TransitDirective directive = builder.load( url ); 163 return new DefaultTransitModel( logger, directive ); 164 } 165 else 166 { 167 return getClassicModel( logger ); 168 } 169 } 170 } 171 172 // ------------------------------------------------------------------------ 173 // state 174 // ------------------------------------------------------------------------ 175 176 private final DefaultProxyModel m_proxy; 177 private final DefaultCacheModel m_cache; 178 179 // ------------------------------------------------------------------------ 180 // constructor 181 // ------------------------------------------------------------------------ 182 183 /** 184 * Creation of a new TransitModel using a supplied configuration 185 * and logging channel. The implementation will construct a proxy 186 * model, layout registry model, cache model, and repository codebase 187 * model using the supplied configuration. 188 * 189 * @param logger the assigned loging channel 190 * @param directive the transit configuration 191 * @exception NullPointerException if the logger or directive arguments are null 192 * @exception RemoteException if a remote exception occurs 193 */ 194 public DefaultTransitModel( Logger logger, TransitDirective directive ) 195 throws RemoteException, NullPointerException 196 { 197 this( 198 new EventQueue( logger, "DPML Transit Event Queue" ), 199 logger, directive ); 200 } 201 202 /** 203 * Creation of a new TransitModel using a supplied configuration 204 * and logging channel. The implementation will construct a proxy 205 * model, layout registry model, cache model, and repository codebase 206 * model using the supplied configuration. 207 * 208 * @param queue the event queue 209 * @param logger the assigned logging channel 210 * @param directive the transit configuration 211 * @exception NullPointerException if the logger or directive arguments are null 212 * @exception RemoteException if a remote exception occurs 213 */ 214 public DefaultTransitModel( EventQueue queue, Logger logger, TransitDirective directive ) 215 throws RemoteException, NullPointerException 216 { 217 super( queue, logger ); 218 219 if( null == directive ) 220 { 221 throw new NullPointerException( "directive" ); 222 } 223 224 m_proxy = createProxyModel( queue, directive ); 225 m_cache = createCacheModel( queue, directive ); 226 } 227 228 // ------------------------------------------------------------------------ 229 // TransitModel 230 // ------------------------------------------------------------------------ 231 232 /** 233 * Return the proxy configuration model. 234 * @return the proxy model (null if no proxy config defined). 235 */ 236 public ProxyModel getProxyModel() 237 { 238 return m_proxy; 239 } 240 241 /** 242 * Return the cache model. 243 * @return the cache model 244 */ 245 public CacheModel getCacheModel() 246 { 247 return m_cache; 248 } 249 250 /** 251 * Add a disposal listener to the model. 252 * @param listener the listener to add 253 */ 254 public void addDisposalListener( DisposalListener listener ) 255 { 256 super.addListener( listener ); 257 } 258 259 /** 260 * Remove a disposal listener from the model. 261 * @param listener the listener to remove 262 */ 263 public void removeDisposalListener( DisposalListener listener ) 264 { 265 super.removeListener( listener ); 266 } 267 268 /** 269 * Internal event handler. 270 * @param eventObject the event to handle 271 */ 272 public void processEvent( EventObject eventObject ) 273 { 274 if( eventObject instanceof DisposalEvent ) 275 { 276 DisposalEvent event = (DisposalEvent) eventObject; 277 processDisposalEvent( event ); 278 } 279 } 280 281 private void processDisposalEvent( DisposalEvent event ) 282 { 283 EventListener[] listeners = super.getEventListeners(); 284 for( int i=0; i < listeners.length; i++ ) 285 { 286 EventListener listener = listeners[i]; 287 if( listener instanceof DisposalListener ) 288 { 289 DisposalListener pl = (DisposalListener) listener; 290 try 291 { 292 pl.notifyDisposal( event ); 293 } 294 catch( Throwable e ) 295 { 296 final String error = 297 "Disposal notification error."; 298 getLogger().error( error, e ); 299 } 300 } 301 } 302 } 303 304 // ------------------------------------------------------------------------ 305 // impl 306 // ------------------------------------------------------------------------ 307 308 Logger getLoggingChannel() 309 { 310 return getLogger(); 311 } 312 313 /** 314 * Trigger disposal of the transit model. 315 */ 316 public synchronized void dispose() 317 { 318 DisposalEvent event = new DisposalEvent( this ); 319 enqueueEvent( event, true ); 320 disposeCacheModel(); 321 disposeProxyModel(); 322 super.dispose(); 323 getEventQueue().terminateDispatchThread(); 324 Thread thread = new Terminator( this ); 325 thread.start(); 326 } 327 328 /** 329 * Internal model terminator. 330 */ 331 private class Terminator extends Thread 332 { 333 private final DefaultTransitModel m_model; 334 Terminator( DefaultTransitModel model ) 335 { 336 m_model = model; 337 } 338 339 /** 340 * Initiate model retraction from the RMI. 341 */ 342 public void run() 343 { 344 try 345 { 346 UnicastRemoteObject.unexportObject( m_model, true ); 347 } 348 catch( NoSuchObjectException e ) 349 { 350 // ignore 351 } 352 catch( RemoteException e ) 353 { 354 e.printStackTrace(); 355 } 356 } 357 } 358 359 private synchronized void disposeProxyModel() 360 { 361 if( null == m_proxy ) 362 { 363 return; 364 } 365 else 366 { 367 m_proxy.dispose(); 368 try 369 { 370 UnicastRemoteObject.unexportObject( m_proxy, true ); 371 } 372 catch( NoSuchObjectException e ) 373 { 374 // ignore 375 } 376 catch( RemoteException e ) 377 { 378 getLogger().warn( "Remote error during proxy reference removal.", e ); 379 } 380 } 381 } 382 383 private synchronized void disposeCacheModel() 384 { 385 m_cache.dispose(); 386 try 387 { 388 UnicastRemoteObject.unexportObject( m_cache, true ); 389 } 390 catch( NoSuchObjectException e ) 391 { 392 // ignore 393 } 394 catch( RemoteException e ) 395 { 396 getLogger().warn( "Remote error during cache reference removal.", e ); 397 } 398 } 399 400 private DefaultProxyModel createProxyModel( final EventQueue queue, final TransitDirective directive ) 401 { 402 try 403 { 404 ProxyDirective config = directive.getProxyDirective(); 405 if( null == config ) 406 { 407 return null; 408 } 409 else 410 { 411 Logger logger = getLogger().getChildLogger( "proxy" ); 412 return new DefaultProxyModel( queue, logger, config ); 413 } 414 } 415 catch( Throwable e ) 416 { 417 final String error = 418 "An error occured during construction of the proxy model."; 419 throw new TransitError( error, e ); 420 } 421 } 422 423 private DefaultCacheModel createCacheModel( final EventQueue queue, final TransitDirective directive ) 424 { 425 try 426 { 427 Logger logger = getLogger().getChildLogger( "cache" ); 428 CacheDirective config = directive.getCacheDirective(); 429 return new DefaultCacheModel( queue, logger, config ); 430 } 431 catch( Throwable e ) 432 { 433 final String error = 434 "An error occured during construction of the cache model."; 435 throw new TransitError( error, e ); 436 } 437 } 438 439 static DefaultTransitModel getBootstrapModel() throws Exception 440 { 441 Logger logger = new LoggingAdapter( "transit" ); 442 return getSecureModel( logger ); 443 } 444 445 static DefaultTransitModel getClassicModel( Logger logger ) throws Exception 446 { 447 TransitDirective directive = TransitDirective.CLASSIC_PROFILE; 448 EventQueue queue = new EventQueue( logger, "DPML Transit Event Queue" ); 449 return new DefaultTransitModel( queue, logger, directive ); 450 } 451 452 private static URI createStaticURI( String path ) 453 { 454 try 455 { 456 return Artifact.createArtifact( path ).toURI(); 457 } 458 catch( Exception e ) 459 { 460 return null; 461 } 462 } 463 464 } 465