Components injected by abstract base, with timing and logging aspects
The complete source of dependencyInjection_components_and_aspects.ek9, compiled by the EK9 compiler. Shown on dependencyInjection.html, structure.html.
#!ek9 defines module site.dependencyInjection.componentsAndAspects defines component BaseConfiguration abstract getFileStoreName() abstract <- rtn as String? default operator ? Solution1 is BaseConfiguration override getFileStoreName() as <- rtn String: "MainStore" Solution2 extends BaseConfiguration override getFileStoreName() as <- rtn as String: "SecondaryStore" Solution3 extends BaseConfiguration storeName as String: "DefaultStore" override getFileStoreName() as <- rtn as String: storeName default operator ? defines class ConfigHandler //This component will get injected config as BaseConfiguration! showConfigDetails() stdout <- Stdout() stdout.println(config.getFileStoreName()) default operator ? defines function checkConfigHandler() stdout <- Stdout() stdout.println("Will check config handler to see if file store name is available") configHandler <- ConfigHandler() configHandler.showConfigDetails() defines application noAspectApp register Solution1() as BaseConfiguration firstApp register FileLogger() as ILogger register Solution1() as BaseConfiguration with aspect of TimerAspect(SystemClock()), LoggingAspect("DEBUG") secondApp register FileLogger() as ILogger register Solution2() as BaseConfiguration with aspect of LoggingAspect("WARN") thirdApp register FileLogger() as ILogger register Solution3() as BaseConfiguration with aspect of TimerAspect(SystemClock()) defines program Program0 with application of noAspectApp stdout <- Stdout() stdout.println("Program0 functionality") checkConfigHandler() Program1 with application of firstApp stdout <- Stdout() stdout.println("Program1 functionality") checkConfigHandler() Program2 with application of secondApp stdout <- Stdout() stdout.println("Program2 functionality") checkConfigHandler() Program3 with application of thirdApp stdout <- Stdout() stdout.println("Program3 functionality") checkConfigHandler() defines component ILogger as abstract log() as abstract -> level as String content as String default operator ? FileLogger extends ILogger stdout as Stdout: Stdout() override log() as -> level as String content as String //Just use Stdout for logging for this example. stdout.println(`${level}: ${content}`) default operator ? defines class LoggingAspect extends Aspect loggingLevel as String? LoggingAspect() as pure this("WARN") LoggingAspect() as pure -> level as String this.loggingLevel :=? level override beforeAdvice() -> joinPoint as JoinPoint <- rtn as PreparedMetaData: PreparedMetaData(joinPoint) //Will be injected logger as ILogger! logger.log(loggingLevel, `Before ${joinPoint.componentName()} ${joinPoint.methodName()}`) override afterAdvice() -> preparedMetaData as PreparedMetaData joinPoint <- preparedMetaData.joinPoint() //Will be injected logger as ILogger! logger.log(loggingLevel, `After ${joinPoint.componentName()} ${joinPoint.methodName()}`) default operator ? TimerData extends PreparedMetaData beforeOperation as Millisecond? default private TimerData() TimerData() -> millis as Millisecond joinPoint as JoinPoint super(joinPoint) beforeOperation: millis before() <- rtn as Millisecond: beforeOperation default operator ? default operator $ default operator :=: default operator <=> TimerAspect extends Aspect clock as Clock? default private TimerAspect() TimerAspect() -> clock as Clock this.clock: clock override beforeAdvice() -> joinPoint as JoinPoint <- rtn as TimerData: TimerData(clock.millisecond(), joinPoint) //A handler for Aspect's 'afterAdvice() as dispatcher': it takes a TimerData, so it is a new overload, not an //override (front-end rationalisation N06), and EK9 dispatches a TimerData to it. afterAdvice() -> timerData as TimerData millisecondsTaken <- clock.millisecond() - timerData.before() joinPoint <- timerData.joinPoint() //Will be injected logger as ILogger! logger.log("INFO", `${millisecondsTaken} Milliseconds for ${joinPoint.componentName()} ${joinPoint.methodName()}`) default operator ?
Download dependencyInjection_components_and_aspects.ek9
Output
Program0
Program0 functionality Will check config handler to see if file store name is available MainStore
Program1
Program1 functionality
Will check config handler to see if file store name is available
DEBUG: Before site.dependencyInjection.componentsAndAspects::Solution1 getFileStoreName
DEBUG: After site.dependencyInjection.componentsAndAspects::Solution1 getFileStoreName
INFO: {{Millisecond}} Milliseconds for site.dependencyInjection.componentsAndAspects::Solution1 getFileStoreName
MainStore
Program2
Program2 functionality Will check config handler to see if file store name is available WARN: Before site.dependencyInjection.componentsAndAspects::Solution2 getFileStoreName WARN: After site.dependencyInjection.componentsAndAspects::Solution2 getFileStoreName SecondaryStore
Program3
Program3 functionality
Will check config handler to see if file store name is available
INFO: {{Millisecond}} Milliseconds for site.dependencyInjection.componentsAndAspects::Solution3 getFileStoreName
DefaultStore