diff --git a/source/Kepler-System-Tests/FixedCustomerManagementSystem.class.st b/source/Kepler-System-Tests/FixedCustomerManagementSystem.class.st new file mode 100644 index 0000000..3ade312 --- /dev/null +++ b/source/Kepler-System-Tests/FixedCustomerManagementSystem.class.st @@ -0,0 +1,32 @@ +" +I'm a system implementation failing when trying to add a customer. Used for testing purposes. +" +Class { + #name : #FixedCustomerManagementSystem, + #superclass : #SubsystemImplementation, + #category : #'Kepler-System-Tests' +} + +{ #category : #API } +FixedCustomerManagementSystem >> addCustomer: aCustomer [ + + SystemCommandExecutionError signal: 'Cannot add customer' +] + +{ #category : #installing } +FixedCustomerManagementSystem >> dependencies [ + + ^ #() +] + +{ #category : #installing } +FixedCustomerManagementSystem >> implementedInterfaces [ + + ^ #(#CustomerManagementSystem) +] + +{ #category : #installing } +FixedCustomerManagementSystem >> name [ + + ^ 'CMS' +] diff --git a/source/Kepler-System-Tests/SystemHotSwapperTest.class.st b/source/Kepler-System-Tests/SystemHotSwapperTest.class.st index 00402f6..6d8e220 100644 --- a/source/Kepler-System-Tests/SystemHotSwapperTest.class.st +++ b/source/Kepler-System-Tests/SystemHotSwapperTest.class.st @@ -4,24 +4,53 @@ SystemHotSwapper test case Class { #name : #SystemHotSwapperTest, #superclass : #TestCase, + #instVars : [ + 'composite' + ], #category : #'Kepler-System-Tests' } -{ #category : #tests } -SystemHotSwapperTest >> testCantSwapWhenTheInterfaceIsNotImplemented [ +{ #category : #private } +SystemHotSwapperTest >> assert: aSubsystem implements: anInterfaceKey [ - | composite cms newCMS | + self assert: ( self is: anInterfaceKey implementedBy: aSubsystem ) +] - cms := SampleCustomerSystem new. +{ #category : #private } +SystemHotSwapperTest >> deny: aSubsystem implements: anInterfaceKey [ + + self deny: ( self is: anInterfaceKey implementedBy: aSubsystem ) +] + +{ #category : #private } +SystemHotSwapperTest >> is: anInterfaceKey implementedBy: aSubsystem [ + + ^ aSubsystem implements: ( composite interfaceAt: anInterfaceKey ) +] + +{ #category : #running } +SystemHotSwapperTest >> setUp [ + + super setUp. composite := CompositeSystem new - register: cms; - yourself. - composite startUp. +] - newCMS := FixedCustomerSystem new. +{ #category : #running } +SystemHotSwapperTest >> tearDown [ + + composite shutDown. + super tearDown +] + +{ #category : #tests } +SystemHotSwapperTest >> testCantSwapWhenTheInterfaceIsNotImplemented [ + + composite + register: SampleCustomerSystem new; + startUp. self - should: [ (SystemHotSwapper swapSystemImplementing: #CustomerManagementSystem with: newCMS) applyTo: composite ] + should: [ SystemHotSwapper swapSystemImplementing: #CustomerManagementSystem with: FixedCustomerSystem new ] raise: SystemControlError withMessageText: 'CMS is not implementing Customer Management' ] @@ -29,48 +58,109 @@ SystemHotSwapperTest >> testCantSwapWhenTheInterfaceIsNotImplemented [ { #category : #tests } SystemHotSwapperTest >> testSwapping [ - | composite cms newCMS | + | cms newCMS | cms := SampleCustomerSystem new. - composite := CompositeSystem new + composite register: cms; - yourself. - composite startUp. + startUp. cms addCustomer: 'John'. newCMS := FixedCustomerSystem new. - self deny: cms = newCMS. - self assert: composite >> #CustomerQueryingSystem equals: cms; - assert: (composite >> #CustomerQueryingSystem) customers size equals: 1. + assert: ( composite >> #CustomerQueryingSystem ) customers size equals: 1. - (SystemHotSwapper swapSystemImplementing: #CustomerQueryingSystem with: newCMS) applyTo: composite. + ( SystemHotSwapper swapSystemImplementing: #CustomerQueryingSystem with: newCMS ) + applyTo: composite. self assert: composite >> #CustomerQueryingSystem equals: newCMS; - assert: (composite >> #CustomerQueryingSystem) customers size equals: 2 + assert: ( composite >> #CustomerQueryingSystem ) customers size equals: 2 +] + +{ #category : #tests } +SystemHotSwapperTest >> testSwappingFailedWhenReplacementsAreMissingSomeInterfaces [ + + | cms pms swapper | + + cms := SampleCustomerSystem new. + pms := SampleProjectSystem new. + composite + register: cms; + register: pms; + startUp. + + self + assert: composite >> #CustomerManagementSystem equals: cms; + assert: pms >> #CustomerQueryingSystem equals: cms. + + swapper := SystemHotSwapper + swapSystemImplementing: #CustomerManagementSystem + with: FixedCustomerManagementSystem new. + + self + should: [ swapper applyTo: composite ] + raise: SystemControlError + withMessageText: 'System implementing "Customer Querying" not found.' +] + +{ #category : #tests } +SystemHotSwapperTest >> testSwappingOneImplementationWithTwoOthers [ + + "This test checks that a system implementing more than one interface can be replaced + by two or more systems implementing the required interfaces" + + | cms managementSystem queryingSystem | + + cms := SampleCustomerSystem new. + composite + register: cms; + startUp. + + self + assert: cms implements: #CustomerQueryingSystem; + assert: cms implements: #CustomerManagementSystem. + + managementSystem := FixedCustomerManagementSystem new. + self + assert: managementSystem implements: #CustomerManagementSystem; + deny: managementSystem implements: #CustomerQueryingSystem. + + queryingSystem := FixedCustomerSystem new. + self + assert: queryingSystem implements: #CustomerQueryingSystem; + deny: queryingSystem implements: #CustomerManagementSystem. + + self + assert: composite >> #CustomerQueryingSystem equals: cms; + assert: composite >> #CustomerManagementSystem equals: cms. + + ( SystemHotSwapper + swapSystemImplementingAll: #(#CustomerQueryingSystem #CustomerManagementSystem) + with: ( Array with: queryingSystem with: managementSystem ) ) applyTo: composite. + + self + assert: composite >> #CustomerQueryingSystem equals: queryingSystem; + assert: composite >> #CustomerManagementSystem equals: managementSystem ] { #category : #tests } SystemHotSwapperTest >> testSwappingSystemWithDependents [ - | composite cms newCMS pms | + | cms newCMS pms | cms := SampleCustomerSystem new. pms := SampleProjectSystem new. - composite := CompositeSystem new + composite register: cms; register: pms; - yourself. - composite startUp. + startUp. newCMS := FixedCustomerSystem new. - self deny: cms = newCMS. - self assert: composite >> #CustomerQueryingSystem equals: cms; assert: pms >> #CustomerQueryingSystem equals: cms. diff --git a/source/Kepler-System/SystemHotInstaller.class.st b/source/Kepler-System/SystemHotInstaller.class.st index f20bc76..9ee6dcc 100644 --- a/source/Kepler-System/SystemHotInstaller.class.st +++ b/source/Kepler-System/SystemHotInstaller.class.st @@ -5,7 +5,7 @@ Class { #name : #SystemHotInstaller, #superclass : #Object, #instVars : [ - 'subsystem' + 'subsystems' ], #category : #'Kepler-System' } @@ -13,19 +13,28 @@ Class { { #category : #'instance creation' } SystemHotInstaller class >> installing: aSubsystem [ - ^self new initializeInstalling: aSubsystem + ^ self installingAll: {aSubsystem} +] + +{ #category : #'instance creation' } +SystemHotInstaller class >> installingAll: aSubsystemCollection [ + + ^self new initializeInstallingAll: aSubsystemCollection ] { #category : #applying } SystemHotInstaller >> applyTo: aCompositeSystem [ - aCompositeSystem register: subsystem. - subsystem startUp. + subsystems + do: [ :subsystem | + aCompositeSystem register: subsystem. + subsystem startUp + ]. aCompositeSystem resolveSubsystemDependencies ] { #category : #initialization } -SystemHotInstaller >> initializeInstalling: aSubsystem [ +SystemHotInstaller >> initializeInstallingAll: aSubsystemCollection [ - subsystem := aSubsystem + subsystems := aSubsystemCollection ] diff --git a/source/Kepler-System/SystemHotSwapper.class.st b/source/Kepler-System/SystemHotSwapper.class.st index b40a796..352d204 100644 --- a/source/Kepler-System/SystemHotSwapper.class.st +++ b/source/Kepler-System/SystemHotSwapper.class.st @@ -5,8 +5,8 @@ Class { #name : #SystemHotSwapper, #superclass : #Object, #instVars : [ - 'subsystem', - 'interfaceKey' + 'interfaceKey', + 'subsystems' ], #pools : [ 'Kepler' @@ -15,17 +15,37 @@ Class { } { #category : #'private - preconditions' } -SystemHotSwapper class >> assert: aSubsystem implements: anInterfaceKey [ +SystemHotSwapper class >> assert: aSubsystem implementsAnyIn: anInterfaceKeyCollection [ - (SystemInterfaces >> anInterfaceKey isImplementedBy: aSubsystem) - ifFalse: [ SystemControlError signal: ('<1p> is not implementing <2p>' expandMacrosWith: aSubsystem with: SystemInterfaces >> anInterfaceKey) ] + anInterfaceKeyCollection + detect: [ :anInterfaceKey | SystemInterfaces >> anInterfaceKey isImplementedBy: aSubsystem ] + ifNone: [ SystemControlError + signal: + ( '<1p> is not implementing <2s>' + expandMacrosWith: aSubsystem + with: + ( ( CollectionFormatter + separatingWith: $, + applyingToEach: [ :anInterfaceKey | ( SystemInterfaces >> anInterfaceKey ) printString ] ) + format: anInterfaceKeyCollection ) ) + ] ] { #category : #'instance creation' } SystemHotSwapper class >> swapSystemImplementing: anInterfaceKey with: aSubsystem [ - self assert: aSubsystem implements: anInterfaceKey. - ^self new initializeSwapSystemImplementing: anInterfaceKey with: aSubsystem + ^ self swapSystemImplementingAll: {anInterfaceKey} with: {aSubsystem} +] + +{ #category : #'instance creation' } +SystemHotSwapper class >> swapSystemImplementingAll: anInterfaceKeyCollection with: aSubsystemCollection [ + + aSubsystemCollection + do: [ :aSubsystem | self assert: aSubsystem implementsAnyIn: anInterfaceKeyCollection ]. + + ^ self new + initializeSwapSystemImplementing: anInterfaceKeyCollection anyOne + withAll: aSubsystemCollection ] { #category : #applying } @@ -36,12 +56,12 @@ SystemHotSwapper >> applyTo: aCompositeSystem [ subsystemToSwap := aCompositeSystem >> interfaceKey. subsystemToSwap shutDown. aCompositeSystem unregister: subsystemToSwap. - (SystemHotInstaller installing: subsystem) applyTo: aCompositeSystem + ( SystemHotInstaller installingAll: subsystems ) applyTo: aCompositeSystem ] { #category : #initialization } -SystemHotSwapper >> initializeSwapSystemImplementing: anInterfaceKey with: aSubsystem [ +SystemHotSwapper >> initializeSwapSystemImplementing: anInterfaceKey withAll: aSubsystemCollection [ interfaceKey := anInterfaceKey. - subsystem := aSubsystem + subsystems := aSubsystemCollection ]