Browse files

Adds support for automated destruction of singletons on unmapping, wh…

…ere destruction means invocation of [PreDestroy] methods, nulling out of the singleton instance and throwing an error upon consecutive attempts at using the provider
  • Loading branch information...
1 parent b284e85 commit aa79289d5b5ca85d857a2a77f9b55be0e96ce5f0 @tschneidereit committed May 31, 2012
View
13 src/org/swiftsuspenders/injection/Injector.as
@@ -246,6 +246,7 @@ package org.swiftsuspenders.injection
throw new InjectorError('Error while removing an injector mapping: ' +
'No mapping defined for dependency ' + mappingId);
}
+ mapping.getProvider().destroy();
delete _mappings[mappingId];
delete providerMappings[mappingId];
hasEventListener(MappingEvent.POST_MAPPING_REMOVE) && dispatchEvent(
@@ -361,6 +362,18 @@ package org.swiftsuspenders.injection
}
/**
+ * Returns a description of the given type containing its constructor, injection points
+ * and post construct and pre destroy hooks
+ *
+ * @param type The type to describe
+ * @return The TypeDescription containing all information the injector has about the type
+ */
+ public function getTypeDescription(type : Class) : TypeDescription
+ {
+ return _reflector.describeInjections(type);
+ }
+
+ /**
* Creates a new <code>Injector</code> and sets itself as that new <code>Injector</code>'s
* <code>parentInjector</code>.
*
View
4 src/org/swiftsuspenders/injection/dependencyproviders/ClassProvider.as
@@ -35,5 +35,9 @@ package org.swiftsuspenders.injection.dependencyproviders
{
return activeInjector.SsInternal::instantiateUnmapped(_responseType);
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
8 src/org/swiftsuspenders/injection/dependencyproviders/DependencyProvider.as
@@ -24,5 +24,13 @@ package org.swiftsuspenders.injection.dependencyproviders
*/
function apply(
targetType : Class, activeInjector : Injector, injectParameters : Dictionary) : Object;
+
+ /**
+ * Cleans up any internal state the provider might keep as preparation for the Injector
+ * dropping it
+ *
+ * Used in the SingletonProvider to invoke <code>[PreDestroy]</code> methods on unmapping
+ */
+ function destroy() : void;
}
}
View
4 src/org/swiftsuspenders/injection/dependencyproviders/FactoryProvider.as
@@ -28,5 +28,9 @@ package org.swiftsuspenders.injection.dependencyproviders
return DependencyProvider(activeInjector.getInstance(_factoryClass))
.apply(targetType, activeInjector, injectParameters);
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
5 src/org/swiftsuspenders/injection/dependencyproviders/ForwardingProvider.as
@@ -27,5 +27,10 @@ package org.swiftsuspenders.injection.dependencyproviders
{
return provider.apply(targetType, activeInjector, injectParameters);
}
+
+ public function destroy() : void
+ {
+ provider.destroy();
+ }
}
}
View
4 src/org/swiftsuspenders/injection/dependencyproviders/OtherMappingProvider.as
@@ -34,5 +34,9 @@ package org.swiftsuspenders.injection.dependencyproviders
{
return _mapping.getProvider().apply(targetType, activeInjector, injectParameters);
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
27 src/org/swiftsuspenders/injection/dependencyproviders/SingletonProvider.as
@@ -8,8 +8,12 @@
package org.swiftsuspenders.injection.dependencyproviders
{
import flash.utils.Dictionary;
+ import flash.utils.getQualifiedClassName;
import org.swiftsuspenders.injection.Injector;
+ import org.swiftsuspenders.injection.InjectorError;
+ import org.swiftsuspenders.typedescriptions.PreDestroyInjectionPoint;
+ import org.swiftsuspenders.typedescriptions.TypeDescription;
import org.swiftsuspenders.utils.SsInternal;
public class SingletonProvider implements DependencyProvider
@@ -18,6 +22,7 @@ package org.swiftsuspenders.injection.dependencyproviders
private var _responseType : Class;
private var _creatingInjector : Injector;
private var _response : Object;
+ private var _destroyed : Boolean;
//---------------------- Public Methods ----------------------//
/**
@@ -49,7 +54,29 @@ package org.swiftsuspenders.injection.dependencyproviders
//---------------------- Private / Protected Methods ----------------------//
private function createResponse(injector : Injector) : Object
{
+ if (_destroyed)
+ {
+ throw new InjectorError("Forbidden usage of unmapped singleton provider for type "
+ + getQualifiedClassName(_responseType));
+ }
return injector.SsInternal::instantiateUnmapped(_responseType);
}
+
+ public function destroy() : void
+ {
+ _destroyed = true;
+ if (!_response)
+ {
+ return;
+ }
+ const typeDescription : TypeDescription =
+ _creatingInjector.getTypeDescription(_responseType);
+ for (var preDestroyHook : PreDestroyInjectionPoint = typeDescription.preDestroyMethods;
+ preDestroyHook; preDestroyHook = PreDestroyInjectionPoint(preDestroyHook.next))
+ {
+ preDestroyHook.applyInjection(_response, _responseType, _creatingInjector);
+ }
+ _response = null;
+ }
}
}
View
5 src/org/swiftsuspenders/injection/dependencyproviders/ValueProvider.as
@@ -32,5 +32,10 @@ package org.swiftsuspenders.injection.dependencyproviders
{
return _value;
}
+
+ public function destroy() : void
+ {
+ //TODO: figure out whether to invoke pre destroy methods here. And how.
+ }
}
}
View
22 test/org/swiftsuspenders/DependencyProviderTests.as
@@ -11,6 +11,10 @@ package org.swiftsuspenders
import flexunit.framework.Assert;
+ import org.hamcrest.assertThat;
+ import org.hamcrest.object.hasPropertyWithValue;
+ import org.swiftsuspenders.injection.InjectorError;
+
import org.swiftsuspenders.injection.dependencyproviders.ClassProvider;
import org.swiftsuspenders.injection.dependencyproviders.OtherMappingProvider;
import org.swiftsuspenders.injection.dependencyproviders.SingletonProvider;
@@ -91,7 +95,23 @@ package org.swiftsuspenders
}
[Test]
- public function injectionTypeOtherMappingReturnsOtherMappingsResponse():void
+ public function destroyingSingletonProviderInvokesPreDestroyMethodsOnSingleton() : void
+ {
+ const provider : SingletonProvider = new SingletonProvider(Clazz, injector);
+ const singleton : Clazz = Clazz(provider.apply(null, injector, null));
+ provider.destroy();
+ assertThat(singleton, hasPropertyWithValue("preDestroyCalled", true));
+ }
+ [Test(expects="org.swiftsuspenders.injection.InjectorError")]
+ public function usingDestroyedSingletonProviderThrows() : void
+ {
+ const provider : SingletonProvider = new SingletonProvider(Clazz, injector);
+ provider.destroy();
+ const singleton : Clazz = Clazz(provider.apply(null, injector, null));
+ }
+
+ [Test]
+ public function mappingToProviderUsesProvidersResponse():void
{
var otherConfig : InjectionMapping = new InjectionMapping(injector, ClazzExtension,
'', '');
View
13 test/org/swiftsuspenders/InjectorTests.as
@@ -36,7 +36,6 @@ package org.swiftsuspenders
import org.swiftsuspenders.support.injectees.MultipleSingletonsOfSameClassInjectee;
import org.swiftsuspenders.support.injectees.NamedArrayCollectionInjectee;
import org.swiftsuspenders.support.injectees.NamedClassInjectee;
- import org.swiftsuspenders.support.injectees.NamedClassInjectee;
import org.swiftsuspenders.support.injectees.NamedInterfaceInjectee;
import org.swiftsuspenders.support.injectees.OneNamedParameterConstructorInjectee;
import org.swiftsuspenders.support.injectees.OneNamedParameterMethodInjectee;
@@ -63,8 +62,6 @@ package org.swiftsuspenders
import org.swiftsuspenders.support.types.ComplexClazz;
import org.swiftsuspenders.support.types.Interface;
import org.swiftsuspenders.support.types.Interface2;
- import org.swiftsuspenders.typedescriptions.NoParamsConstructorInjectionPoint;
- import org.swiftsuspenders.typedescriptions.PropertyInjectionPoint;
import org.swiftsuspenders.typedescriptions.TypeDescription;
import org.swiftsuspenders.utils.SsInternal;
@@ -906,5 +903,15 @@ package org.swiftsuspenders
injector.getInstance(PostConstructInjectedVarInjectee);
assertThat(injectee.property, isA(Clazz));
}
+
+ [Test]
+ public function unmappingSingletonProviderInvokesPreDestroyMethodsOnSingleton() : void
+ {
+ injector.map(Clazz).asSingleton();
+ const singleton : Clazz = injector.getInstance(Clazz);
+ assertThat(singleton, hasPropertyWithValue("preDestroyCalled", false));
+ injector.unmap(Clazz);
+ assertThat(singleton, hasPropertyWithValue("preDestroyCalled", true));
+ }
}
}
View
4 test/org/swiftsuspenders/support/injectees/childinjectors/ChildInjectorCreatingProvider.as
@@ -20,5 +20,9 @@ package org.swiftsuspenders.support.injectees.childinjectors
{
return activeInjector.createChildInjector();
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
4 test/org/swiftsuspenders/support/providers/ClassNameStoringProvider.as
@@ -26,5 +26,9 @@ package org.swiftsuspenders.support.providers
lastTargetClassName = getQualifiedClassName(targetType);
return new Clazz();
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
5 test/org/swiftsuspenders/support/providers/UnknownParametersUsingProvider.as
@@ -8,7 +8,6 @@
package org.swiftsuspenders.support.providers
{
import flash.utils.Dictionary;
- import flash.utils.getQualifiedClassName;
import org.swiftsuspenders.injection.Injector;
import org.swiftsuspenders.injection.dependencyproviders.DependencyProvider;
@@ -26,5 +25,9 @@ package org.swiftsuspenders.support.providers
parameterValue = injectParameters.param;
return new Clazz();
}
+
+ public function destroy() : void
+ {
+ }
}
}
View
33 test/org/swiftsuspenders/support/types/Clazz.as
@@ -1,31 +1,24 @@
/*
-* Copyright (c) 2009 the original author or authors
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documentation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to whom the Software is
-* furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in
-* all copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-* THE SOFTWARE.
-*/
+ * Copyright (c) 2012 the original author or authors
+ *
+ * Permission is hereby granted to use, modify, and distribute this file
+ * in accordance with the terms of the license agreement accompanying it.
+ */
package org.swiftsuspenders.support.types
{
public class Clazz implements Interface, Interface2
{
+ public var preDestroyCalled : Boolean;
+
public function Clazz()
{
}
+
+ [PreDestroy]
+ public function preDestroy() : void
+ {
+ preDestroyCalled = true;
+ }
}
}

0 comments on commit aa79289

Please sign in to comment.