1 module glued.application.runtime; 2 3 import std.range; 4 import std.functional; 5 6 import glued.logging; 7 8 import glued.codescan; 9 10 import glued.application.scanlisteners; 11 import glued.application.logging; 12 import glued.adhesives; 13 14 import dejector; 15 16 interface ApplicationAction { 17 void execute(); 18 } 19 20 interface ShutdownHandler { 21 void onShutdown(); 22 } 23 24 enum RuntimeLifecycleStage { 25 PREPARED, 26 STARTED, 27 SHUT_DOWN 28 } 29 30 class GluedRuntime(alias scannables) { 31 mixin CreateLogger; 32 private Logger log; 33 34 private RuntimeLifecycleStage _stage = RuntimeLifecycleStage.PREPARED; 35 private Dejector _injector; 36 private LogSink _targetSink; 37 38 @property 39 RuntimeLifecycleStage currentStage(){ 40 return _stage; 41 } 42 43 @property 44 auto injector(){ 45 return _injector; //todo optional? 46 } 47 48 @property 49 auto targetSink(){ 50 return _targetSink; 51 } 52 53 //todo expose effective-/configuredSink? the deferred one, or only the filtering one? 54 55 @property 56 void targetSink(LogSink sink){ 57 assert(_stage == RuntimeLifecycleStage.PREPARED); //todo exception 58 _targetSink = sink; 59 } 60 61 void start(string[] cmdLineArgs=[]){ 62 assert(_stage == RuntimeLifecycleStage.PREPARED); //todo exception 63 DeferredLogSink sink = new DeferredLogSink; 64 log = Logger(sink); 65 log.debug_.emit("Initialized deferred log sink"); 66 log.debug_.emit("Setting up DI facilities"); 67 _injector = new Dejector; 68 _injector.bind!(LogSink)(new InstanceProvider(sink)); 69 auto scanner = new CodebaseScanner!(Dejector, GluedAppListeners)(_injector, sink); 70 log.debug_.emit("Scanning: ", scannables); 71 scanner.scan!scannables(); 72 log.debug_.emit("Scan finished, freezing application state"); 73 scanner.freeze(); //todo this will happen in different moment when we compose scannables from annotations 74 log.debug_.emit("Resolving log sink based on loaded configuration"); 75 resolveLogSink(sink); //todo if there is exception before this step, turn off any filtering (maybe keep buildLog.conf), flush to stderr, then let the failure propagate (so we can investigate, but with logs) 76 // instantiateComponents(); //todo do this once you index types by stereotypes 77 //interface StereotypedInstance(S) {prop S stereotype, prop Object instance} 78 //interface StereotypeDescriptor{ prop string stereotypeTypeName } 79 log.debug_.emit("Running application actions"); 80 runActions(); 81 log.info.emit("Runtime started"); 82 _stage = RuntimeLifecycleStage.STARTED; //todo on exception -> SHUT_DOWN 83 } 84 85 //todo test this by providing some small app with bunch of components; provide glued assets for their log levels, steer some with build time assets too; set targetSink manually, provide action that triggers these components methods (which do logging) and make sure that related events are prezent 86 private void resolveLogSink(DeferredLogSink sink){ 87 auto logFilteringTree = _injector 88 .get!Config 89 .view 90 .subtree("log.level") 91 .mapValues!Level(toDelegate((ConfigEntry v) => v.text.toLevel)); 92 if (_targetSink is null) 93 _targetSink = new StdoutSink; //todo build sink based on config (stdout/err, some storage, maybe composites?); what to do if _targetSink !is null? 94 //todo levelConfig can also be tweaked via config 95 auto filteringSink = new FilteringSink(_targetSink, levelConfig(logFilteringTree)); 96 sink.resolve(filteringSink); 97 } 98 99 private void runActions(){ 100 InterfaceResolver resolver = _injector.get!InterfaceResolver; 101 auto actions = resolver.getImplementations!(ApplicationAction)(); 102 if (!actions.empty) { 103 log.debug_.emit("Found ", actions.length, " actions to execute"); 104 foreach(a; actions){ //todo make optionally parallel via glued.app.actions.parallel 105 a.execute(); 106 } 107 } else { 108 log.debug_.emit("No actions found"); 109 } 110 } 111 112 void shutDown(){ 113 assert(_stage == RuntimeLifecycleStage.STARTED); //todo exception 114 log.debug_.emit("Shutdown started"); 115 InterfaceResolver resolver = _injector.get!InterfaceResolver; 116 117 ShutdownHandler[] handlers = resolver.getImplementations!ShutdownHandler; 118 if (!handlers.empty) { 119 log.debug_.emit("Found ", handlers.length, " shutdown handlers"); 120 foreach(h; handlers){ //todo ditto 121 h.onShutdown(); 122 } 123 } else { 124 log.debug_.emit("No shutdown handlers found"); 125 } 126 _injector = null; 127 log.info.emit("Runtime shut down"); 128 _stage = RuntimeLifecycleStage.SHUT_DOWN; 129 } 130 }