1
+ /*
2
+ * Copyright 2006-2007 the original author or authors.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ package org .springframework .batch .core .scope ;
17
+
18
+ import org .apache .commons .logging .Log ;
19
+ import org .apache .commons .logging .LogFactory ;
20
+ import org .springframework .batch .core .scope .context .JobContext ;
21
+ import org .springframework .batch .core .scope .context .JobSynchronizationManager ;
22
+ import org .springframework .batch .core .scope .util .ContextFactory ;
23
+ import org .springframework .batch .core .scope .util .JobContextFactory ;
24
+ import org .springframework .beans .BeanWrapper ;
25
+ import org .springframework .beans .BeanWrapperImpl ;
26
+ import org .springframework .beans .factory .ObjectFactory ;
27
+ import org .springframework .beans .factory .config .Scope ;
28
+
29
+ /**
30
+ * Scope for job context. Objects in this scope use the Spring container as an
31
+ * object factory, so there is only one instance of such a bean per executing
32
+ * job. All objects in this scope are <aop:scoped-proxy/> (no need to
33
+ * decorate the bean definitions).<br/>
34
+ * <br/>
35
+ *
36
+ * In addition, support is provided for late binding of references accessible
37
+ * from the {@link JobContext} using #{..} placeholders. Using this feature,
38
+ * bean properties can be pulled from the job or job execution context and the
39
+ * job parameters. E.g.
40
+ *
41
+ * <pre>
42
+ * <bean id="..." class="..." scope="job">
43
+ * <property name="name" value="#{jobParameters[input]}" />
44
+ * </bean>
45
+ *
46
+ * <bean id="..." class="..." scope="job">
47
+ * <property name="name" value="#{jobExecutionContext['input.stem']}.txt" />
48
+ * </bean>
49
+ * </pre>
50
+ *
51
+ * The {@link JobContext} is referenced using standard bean property paths (as
52
+ * per {@link BeanWrapper}). The examples above all show the use of the Map
53
+ * accessors provided as a convenience for job attributes.
54
+ *
55
+ * @author Dave Syer
56
+ * @author Jimmy Praet (create JobScope based on {@link StepScope})
57
+ * @since 2.0
58
+ */
59
+ public class JobScope extends ScopeSupport {
60
+
61
+ private Log logger = LogFactory .getLog (getClass ());
62
+
63
+ private final Object mutex = new Object ();
64
+
65
+ /**
66
+ * Context key for clients to use for conversation identifier.
67
+ */
68
+ public static final String ID_KEY = "JOB_IDENTIFIER" ;
69
+
70
+ /**
71
+ * The ContextFactory.
72
+ */
73
+ private static final ContextFactory CONTEXT_FACTORY = new JobContextFactory ();
74
+
75
+ public JobScope () {
76
+ super ("job" , CONTEXT_FACTORY );
77
+ }
78
+
79
+ /**
80
+ * If Spring 3.0 is available, this will be used to resolve expressions in
81
+ * job-scoped beans. This method is part of the Scope SPI in Spring 3.0,
82
+ * but should just be ignored by earlier versions of Spring.
83
+ */
84
+ public Object resolveContextualObject (String key ) {
85
+ JobContext context = getContext ();
86
+ // TODO: support for attributes as well maybe (setters not exposed yet
87
+ // so not urgent).
88
+ return new BeanWrapperImpl (context ).getPropertyValue (key );
89
+ }
90
+
91
+ /**
92
+ * @see Scope#get(String, ObjectFactory)
93
+ */
94
+ public Object get (String name , ObjectFactory objectFactory ) {
95
+
96
+ JobContext context = getContext ();
97
+ Object scopedObject = context .getAttribute (name );
98
+
99
+ if (scopedObject == null ) {
100
+
101
+ synchronized (mutex ) {
102
+ scopedObject = context .getAttribute (name );
103
+ if (scopedObject == null ) {
104
+
105
+ logger .debug (String .format ("Creating object in scope=%s, name=%s" , this .getName (), name ));
106
+
107
+ scopedObject = objectFactory .getObject ();
108
+ context .setAttribute (name , scopedObject );
109
+
110
+ }
111
+
112
+ }
113
+
114
+ }
115
+ return scopedObject ;
116
+ }
117
+
118
+ /**
119
+ * @see Scope#getConversationId()
120
+ */
121
+ public String getConversationId () {
122
+ JobContext context = getContext ();
123
+ return context .getId ();
124
+ }
125
+
126
+ /**
127
+ * @see Scope#registerDestructionCallback(String, Runnable)
128
+ */
129
+ public void registerDestructionCallback (String name , Runnable callback ) {
130
+ JobContext context = getContext ();
131
+ logger .debug (String .format ("Registered destruction callback in scope=%s, name=%s" , this .getName (), name ));
132
+ context .registerDestructionCallback (name , callback );
133
+ }
134
+
135
+ /**
136
+ * @see Scope#remove(String)
137
+ */
138
+ public Object remove (String name ) {
139
+ JobContext context = getContext ();
140
+ logger .debug (String .format ("Removing from scope=%s, name=%s" , this .getName (), name ));
141
+ return context .removeAttribute (name );
142
+ }
143
+
144
+ /**
145
+ * Get an attribute accessor in the form of a {@link JobContext} that can
146
+ * be used to store scoped bean instances.
147
+ *
148
+ * @return the current job context which we can use as a scope storage
149
+ * medium
150
+ */
151
+ private JobContext getContext () {
152
+ JobContext context = JobSynchronizationManager .getContext ();
153
+ if (context == null ) {
154
+ throw new IllegalStateException ("No context holder available for job scope" );
155
+ }
156
+ return context ;
157
+ }
158
+
159
+ }
0 commit comments