/
Singleton.cfc
172 lines (153 loc) · 4.71 KB
/
Singleton.cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
* Copyright Since 2005 ColdBox Framework by Luis Majano and Ortus Solutions, Corp
* www.ortussolutions.com
* ---
* Tracking of single instance objects: Singletons
*
* @see coldbox.system.ioc.scopes.IScope
**/
component accessors="true" {
/**
* Injector linkage
*/
property name="injector";
/**
* Track singletons as a concurrent hash map
*/
property name="singletons";
/**
* Log Reference
*/
property name="log";
/**
* These are keys we use internally in ColdBox, so we don't want to clear them when doing clearAppOnly() calls
*/
variables.RESERVED_KEYS = [
"@coldbox",
"interceptor-",
"cbscheduler",
"@coreDelegates",
"@cbdelegates"
];
/**
* Configure the scope for operation and returns itself
*
* @injector The linked WireBox injector
* @injector.doc_generic coldbox.system.ioc.Injector
*
* @return coldbox.system.ioc.scopes.IScope
*/
function init( required injector ){
variables.injector = arguments.injector;
variables.singletons = createObject( "java", "java.util.concurrent.ConcurrentHashMap" ).init();
variables.log = arguments.injector.getLogBox().getLogger( this );
return this;
}
/**
* Retrieve an object from scope or create it if not found in scope
*
* @mapping The linked WireBox injector
* @mapping.doc_generic coldbox.system.ioc.config.Mapping
* @initArguments The constructor struct of arguments to passthrough to initialization
*/
function getFromScope( required mapping, struct initArguments ){
var cacheKey = lCase( arguments.mapping.getName() );
// Verify in Singleton Cache
if ( NOT variables.singletons.containsKey( cacheKey ) ) {
// Lock it
lock
name ="WireBox.#variables.injector.getInjectorID()#.Singleton.#cacheKey#"
type ="exclusive"
timeout ="30"
throwontimeout="true" {
// double lock it
if ( NOT variables.singletons.containsKey( cacheKey ) ) {
// some nice debug info.
if ( variables.log.canDebug() ) {
variables.log.debug(
"Object: (#cacheKey#) not found in singleton cache, beginning construction by (#variables.injector.getName()#) injector"
);
}
// construct the singleton object
var tmpSingleton = variables.injector.buildInstance(
arguments.mapping,
arguments.initArguments
);
// If not in wiring thread safety, store in singleton cache to satisfy circular dependencies
if ( NOT arguments.mapping.getThreadSafe() ) {
variables.singletons.put( cacheKey, tmpSingleton );
}
try {
// wire up dependencies on the singleton object
variables.injector.autowire( target = tmpSingleton, mapping = arguments.mapping );
} catch ( any e ) {
variables.singletons.remove( cacheKey );
rethrow;
}
// If thread safe, then now store it in the singleton cache, as all dependencies are now safely wired
if ( arguments.mapping.getThreadSafe() ) {
variables.singletons.put( cacheKey, tmpSingleton );
}
// log it
if ( variables.log.canDebug() ) {
variables.log.debug(
"Object: (#cacheKey#) constructed and stored in singleton cache. ThreadSafe=#arguments.mapping.getThreadSafe()# by (#variables.injector.getName()#) injector"
);
}
// return it
return variables.singletons.get( cacheKey );
}
}
// end lock
}
// return singleton
return variables.singletons.get( cacheKey );
}
/**
* Indicates whether an object exists in scope
*
* @mapping The linked WireBox injector
* @mapping.doc_generic coldbox.system.ioc.config.Mapping
*
* @return True if the mapping exists in the singleton cache
*/
boolean function exists( required mapping ){
return variables.singletons.containsKey( lCase( arguments.mapping.getName() ) );
}
/**
* Clear the singletons scopes
*
* @key If passed, we will try to remove that key from the singleton cache instead of every key
*/
Singleton function clear( string key ){
if ( !isNull( arguments.key ) ) {
variables.singletons.remove( lCase( arguments.key ) );
} else {
variables.singletons.clear();
}
return this;
}
/**
* Clear application only singletons
*/
function clearAppOnly(){
var keys = variables.singletons.keySet().toArray();
for ( var key in keys ) {
// They key must NOT match any pattern in our reserved keys, so we can clear it
if ( !inReservedKeys( key ) ) {
variables.singletons.remove( key );
}
}
return this;
}
/**
* Discover if a key is in our reserved keys
*
* @key The key to check
*
* @return boolean
*/
private boolean function inReservedKeys( String key ){
return variables.RESERVED_KEYS.some( ( reservedKey ) => findNoCase( reservedKey, key ) );
}
}