1 | /* |
2 | * Copyright 2006-2010 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.configuration.support; |
17 | |
18 | import java.util.ArrayList; |
19 | import java.util.Collection; |
20 | import java.util.HashSet; |
21 | import java.util.Map; |
22 | import java.util.concurrent.ConcurrentHashMap; |
23 | |
24 | import org.apache.commons.logging.Log; |
25 | import org.apache.commons.logging.LogFactory; |
26 | import org.springframework.batch.core.Job; |
27 | import org.springframework.batch.core.configuration.DuplicateJobException; |
28 | import org.springframework.batch.core.configuration.JobFactory; |
29 | import org.springframework.batch.core.configuration.JobRegistry; |
30 | import org.springframework.batch.core.launch.NoSuchJobException; |
31 | import org.springframework.context.ConfigurableApplicationContext; |
32 | |
33 | /** |
34 | * Default implementation of {@link JobLoader}. Uses a {@link JobRegistry} to |
35 | * manage a population of loaded jobs and clears them up when asked. |
36 | * |
37 | * @author Dave Syer |
38 | * |
39 | */ |
40 | public class DefaultJobLoader implements JobLoader { |
41 | |
42 | private static Log logger = LogFactory.getLog(DefaultJobLoader.class); |
43 | |
44 | private JobRegistry jobRegistry; |
45 | |
46 | private Map<ApplicationContextFactory, ConfigurableApplicationContext> contexts = new ConcurrentHashMap<ApplicationContextFactory, ConfigurableApplicationContext>(); |
47 | |
48 | private Map<ConfigurableApplicationContext, Collection<String>> contextToJobNames = new ConcurrentHashMap<ConfigurableApplicationContext, Collection<String>>(); |
49 | |
50 | /** |
51 | * Default constructor useful for declarative configuration. |
52 | */ |
53 | public DefaultJobLoader() { |
54 | this(null); |
55 | } |
56 | |
57 | /** |
58 | * Create a job loader with the job registry provided. |
59 | * @param jobRegistry a {@link JobRegistry} |
60 | */ |
61 | public DefaultJobLoader(JobRegistry jobRegistry) { |
62 | this.jobRegistry = jobRegistry; |
63 | } |
64 | |
65 | /** |
66 | * The {@link JobRegistry} to use for jobs created. |
67 | * |
68 | * @param jobRegistry |
69 | */ |
70 | public void setJobRegistry(JobRegistry jobRegistry) { |
71 | this.jobRegistry = jobRegistry; |
72 | } |
73 | |
74 | /** |
75 | * Unregister all the jobs and close all the contexts created by this |
76 | * loader. |
77 | * |
78 | * @see JobLoader#clear() |
79 | */ |
80 | public void clear() { |
81 | for (ConfigurableApplicationContext context : contexts.values()) { |
82 | if (context.isActive()) { |
83 | context.close(); |
84 | } |
85 | } |
86 | for (String jobName : jobRegistry.getJobNames()) { |
87 | jobRegistry.unregister(jobName); |
88 | } |
89 | contexts.clear(); |
90 | } |
91 | |
92 | public Collection<Job> reload(ApplicationContextFactory factory) { |
93 | |
94 | // If the same factory is loaded twice the context can be closed |
95 | if (contexts.containsKey(factory)) { |
96 | ConfigurableApplicationContext context = contexts.get(factory); |
97 | for (String name : contextToJobNames.get(context)) { |
98 | logger.debug("Unregistering job: " + name + " from context: " + context.getDisplayName()); |
99 | jobRegistry.unregister(name); |
100 | } |
101 | context.close(); |
102 | } |
103 | |
104 | try { |
105 | return doLoad(factory, true); |
106 | } |
107 | catch (DuplicateJobException e) { |
108 | throw new IllegalStateException("Found duplicte job in reload (it should have been unregistered " |
109 | + "if it was previously registered in this loader)", e); |
110 | } |
111 | } |
112 | |
113 | public Collection<Job> load(ApplicationContextFactory factory) throws DuplicateJobException { |
114 | return doLoad(factory, false); |
115 | } |
116 | |
117 | private Collection<Job> doLoad(ApplicationContextFactory factory, boolean unregister) throws DuplicateJobException { |
118 | |
119 | Collection<String> jobNamesBefore = jobRegistry.getJobNames(); |
120 | ConfigurableApplicationContext context = factory.createApplicationContext(); |
121 | Collection<String> jobNamesAfter = jobRegistry.getJobNames(); |
122 | // Try to detect auto-registration (e.g. through a bean post processor) |
123 | boolean autoRegistrationDetected = jobNamesAfter.size() > jobNamesBefore.size(); |
124 | |
125 | Collection<String> jobsRegistered = new HashSet<String>(); |
126 | if (autoRegistrationDetected) { |
127 | for (String name : jobNamesAfter) { |
128 | if (!jobNamesBefore.contains(name)) { |
129 | jobsRegistered.add(name); |
130 | } |
131 | } |
132 | } |
133 | |
134 | contexts.put(factory, context); |
135 | String[] names = context.getBeanNamesForType(Job.class); |
136 | |
137 | for (String name : names) { |
138 | |
139 | if (!autoRegistrationDetected) { |
140 | |
141 | Job job = (Job) context.getBean(name); |
142 | String jobName = job.getName(); |
143 | |
144 | // On reload try to unregister first |
145 | if (unregister) { |
146 | logger.debug("Unregistering job: " + jobName + " from context: " + context.getDisplayName()); |
147 | jobRegistry.unregister(jobName); |
148 | } |
149 | |
150 | logger.debug("Registering job: " + jobName + " from context: " + context.getDisplayName()); |
151 | JobFactory jobFactory = new ReferenceJobFactory(job); |
152 | jobRegistry.register(jobFactory); |
153 | jobsRegistered.add(jobName); |
154 | |
155 | } |
156 | |
157 | } |
158 | |
159 | Collection<Job> result = new ArrayList<Job>(); |
160 | for (String name : jobsRegistered) { |
161 | try { |
162 | result.add(jobRegistry.getJob(name)); |
163 | } |
164 | catch (NoSuchJobException e) { |
165 | // should not happen; |
166 | throw new IllegalStateException("Could not retrieve job that was should have been registered", e); |
167 | } |
168 | |
169 | } |
170 | |
171 | contextToJobNames.put(context, jobsRegistered); |
172 | |
173 | return result; |
174 | |
175 | } |
176 | |
177 | } |