View Javadoc
1 /* ====================================================================
2 * License:
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * 3. The end-user documentation included with the redistribution,
17 * if any, must include the following acknowledgment:
18 * "This product includes software developed by
19 * Robert Half International (http://www.rhi.com/)."
20 * Alternately, this acknowledgment may appear in the software itself,
21 * if and wherever such third-party acknowledgments normally appear.
22 *
23 * 4. The names "Parc", "RHI", and "Robert Half International" must
24 * not be used to endorse or promote products derived from this
25 * software without prior written permission. For written
26 * permission, please contact pete.mckinstry@rhi.com.
27 *
28 * 5. Products derived from this software may not be called "PARC",
29 * nor may "PARC" appear in their name, without prior written
30 * permission of Robert Half International.
31 *
32 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
33 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
36 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
38 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
39 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
40 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
41 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
42 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * SUCH DAMAGE.
44 * ====================================================================
45 *
46 */
47 package com.rhi.architecture.batch;
48
49 import com.rhi.architecture.lang.CompositeProperties;
50 import com.rhi.architecture.resource.InitializationException;
51 import com.rhi.architecture.resource.ResourceContext;
52
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.util.Properties;
56
57 /***
58 * A BatchApplication adds audit and configuration capabilities
59 * to the standard application. It requires the following
60 * properties to be set:
61 * <br/>
62 * AuditAgent - This should be assigned to a
63 * concrete AuditAgent class that should be used to perform auditing of the
64 * batch process. ConfigMixin# - Any number of ConfigMixin's can be
65 * loaded by the BatchApplication. However they need to follow these guidelines:
66 * 1) implement the ConfigMixin interface, 2) be listed in the order required,
67 * where ConfigMixin0 is loaded first, and each increasing number is loaded in
68 * order until a number is missing at which point all Mixin's are assumed to be
69 * loaded, and the initialization process continues.
70 *
71 * @author Pete McKinstry
72 * @copyright 2002, Robert Half Int'l., Inc. All rights reserved.
73 *
74 * @since 1.0
75 */
76 public abstract class BatchApplication extends AbstractApplication {
77
78 // property settings for this application.
79 private Properties resources;
80 // the audit agent to use
81 private AuditAgent auditAgent;
82
83 /***
84 * default constructor
85 *
86 * @since 1.0
87 */
88 public BatchApplication() {
89 resources = new CompositeProperties();
90 }
91
92 /***
93 * Return the audit agent to be used to perform audits.
94 * Note: this method is declared final because the audit
95 * agent is created dynamically based on a property file
96 * setting, thereby making it possible to override the
97 * type w/o modifying this method. (Also the reason that
98 * there is no setAuditAgent() method.
99 *
100 * @return AuditAgent concrete instance, potentially specific
101 * to this application.
102 *
103 * @since 1.0
104 */
105 public final AuditAgent getAuditAgent() {
106 return auditAgent;
107 }
108
109 /***
110 * Perform BatchApplication initialization.
111 * 1. Loads configuration information.
112 * a) command line arguments are loaded w/ property
113 * names of arg0, arg1, arg2, etc...
114 * 2. Initializes audit agent
115 * 3. Calls doInit() to allow sub-classes to perform
116 * any required initialization _after_ this method is
117 * complete. Note: sub-classes can override properties
118 * by loading an already existing property name. Last
119 * defined value wins.
120 *
121 * @param args the commandline arguments. usually used for configuration
122 * options.
123 * @throws InitializationException thrown when their is a problem configuring
124 * the application
125 *
126 * @since 1.0
127 */
128 protected final void init(String args[])
129 throws InitializationException {
130 try {
131 // 1. Bootstrap the application
132 // Checks for properties file & configMixin on the
133 // commandline. If found, loads them. Other mixins
134 // will be found & loaded after bootstrap.
135 bootstrap(args);
136
137 // 2. Mixin classes can be used to append configuration
138 // information from disparate sources w/o affecting the
139 // core batch architecture. This facility allows the
140 // interface developer to add any number of optional mixin
141 // classes. They will be loaded and initialized @ runtime
142 // prior to initializing any framework objects.
143 int count = 1;
144 while (true) {
145 // instead of using propertyNames() enumeration to find
146 // config mixins, i chose this method because it forces
147 // a certain well understood loading order.
148 String mixinClassName =
149 resources.getProperty("ConfigMixin" + count, "none");
150 if (mixinClassName.equals("none")) {
151 break;
152 }
153 loadConfigMixin(mixinClassName);
154 count++;
155 }
156
157 // 3. Initialize the ResourceContext.
158 // so it's available to the audit agent.
159 ResourceContext ctx = new ResourceContext();
160 ctx.init(resources);
161
162 if (isAuditEnabled()) {
163 // 4. create & initialize audit agent
164 String agentType = resources.getProperty("AuditAgent",
165 "com.rhi.architecture.batch.NullAuditAgent");
166 auditAgent = AuditAgent.getInstance(agentType);
167 auditAgent.init(resources);
168 }
169
170 // 5. sub-classes get initialized last.
171 doInit();
172
173 }
174 catch (NullPointerException npe) {
175 log().debug("null pointer exception while running init().");
176 log().debug("probably config error.");
177 throw new InitializationException(
178 "null pointer exception in BatchApplication.init()",
179 npe);
180 }
181
182 }
183
184 /***
185 * Bootstrap the application configuration. Normally this is
186 * done by loading the properties file autoSys passes via the
187 * commandline & then loading whatever data is found in the
188 * database referenced by those db connection properties. However
189 * many options exist for utilizing this startup logic.
190 * 1. If a properties file named <app-class-name>.properties
191 * exists, load it's name/value pairs.
192 * 2. Try loading System properties of
193 * a) bootstrap-mixin
194 * b) properties-file
195 * 3. Load commandline arguments.
196 * a) If -p|-properties-file found as commandline argument,
197 * assume the next argument is a properties filename
198 * that should override the System property.
199 * b) If -b|-bootstrap-mixin found as commandline argument,
200 * assume the next argument is a configMixin & load it
201 * accordingly.
202 * 4. If step 2 or 3 (3 overrides 2) found non-null values for
203 * either the bootstrap mixin or the properties filename,
204 * load them properties file first.
205 *
206 * @param args the commandline arguments.
207 *
208 * @exception InitializationException thrown if a problem is detected
209 * during the bootstrap process.
210 *
211 * @since 1.0
212 */
213 private void bootstrap(String[] args) throws InitializationException {
214
215 // check for system properties
216 String propertiesFile = System.getProperty("properties-file", "");
217 ;
218 String bootstrapMixin = System.getProperty("bootstrap-mixin", "");
219
220 // load command line arguments
221 for (int i = 0; i < args.length; ++i) {
222 resources.setProperty("arg" + i, args[i]);
223
224 if ((args[i].equals("-p") == true)
225 || (args[i].equals("-properties-file") == true)) {
226 propertiesFile = args[i + 1];
227 }
228
229 if ((args[i].equals("-b") == true)
230 || (args[i].equals("-bootstrap-mixin") == true)) {
231 bootstrapMixin = args[i + 1];
232 }
233 }
234 // properties first.
235 if (propertiesFile.equals("") == false) {
236 loadProperties(propertiesFile);
237 }
238 else {
239 log().info("no bootstrap properties file found.");
240 }
241 // mixin can now take advantage of commandline properties.
242 if (bootstrapMixin.equals("") == false) {
243 loadConfigMixin(bootstrapMixin);
244 // for process based application, you should set the
245 // bootstrap configmixin so that it will be able to
246 // reload data correctly.
247 resources.setProperty("ConfigMixin0", bootstrapMixin);
248 }
249 else {
250 log().info("no bootstrap config mixin found.");
251 }
252 }
253
254 /***
255 * Instantiate a Configuration Mixin class & load it's property
256 * settings.
257 *
258 * @param mixinClassName the class name of the mixin class to be used.
259 * @throws InitializationException thrown if a problem occurs loading a
260 * certain config mixin class.
261 *
262 * @since 1.0
263 */
264 private void loadConfigMixin(String mixinClassName)
265 throws InitializationException {
266 try {
267 Class mixinClass = Class.forName(mixinClassName);
268 ConfigMixin config = (ConfigMixin) mixinClass.newInstance();
269 config.loadConfiguration(resources);
270 }
271 catch (ClassNotFoundException cnfe) {
272 throw new InitializationException(
273 "ClassNotFoundException = " + cnfe.toString(),
274 cnfe);
275 }
276 catch (InstantiationException ie) {
277 throw new InitializationException(
278 "InstantiationException = " + ie.toString(),
279 ie);
280 }
281 catch (IllegalAccessException iae) {
282 throw new InitializationException(
283 "IllegalAccessException = " + iae.toString(),
284 iae);
285 }
286 }
287
288 /***
289 * Load settings from property file. Called as part of initialization
290 * of the BatchApplication object.
291 *
292 * @param filename the properties file
293 * @throws InitializationException thrown if there is an error loading
294 * the properties.
295 *
296 * @since 1.0
297 */
298 private void loadProperties(String filename)
299 throws InitializationException {
300 try {
301 // getClass() method returns the runtime type of the application
302 // class. We take advantage of this by loading the property
303 // setting according to the concrete classname thereby providing a
304 // unique name based on the real interface application class and
305 // removing the burden of configuration loading from the interface
306 // designer.
307 InputStream is =
308 getClass().getClassLoader().getResourceAsStream(filename);
309 if (is == null) {
310 log().error("<" + filename + "> not found.");
311 throw new InitializationException(
312 "config file missing = " + filename);
313 }
314 resources.load(is);
315 }
316 catch (IOException io) {
317 throw new InitializationException("IOException = " + io, io);
318 }
319 }
320
321 /***
322 * Return the configuration information for this application as a
323 * Properties object. Sub-classes will use this method to retrieve
324 * the configuration information.
325 *
326 * @return Properties - the configuration data for this interface.
327 *
328 * @since 1.0
329 */
330 protected Properties getProperties() {
331 return resources;
332 }
333
334 /***
335 * Perform any cleanup required by the application framework.
336 * This method will also call the <code>doCleanup()</code>
337 * abstract method to allow concrete application classes to
338 * shutdown gracefully.
339 *
340 * @since 1.0
341 */
342 public final void cleanup() {
343 // sub-classes get to cleanup resources first.
344 doCleanup();
345 // cleanup this classes resources.
346 resources.clear();
347
348 // shutdown any configured resources via ResourceContext.
349 ResourceContext ctx = new ResourceContext();
350 ctx.cleanup();
351 }
352
353 /***
354 * The doInit() method is expected to be overriden by
355 * concrete application classes to perform any required
356 * initialization.
357 *
358 * @throws InitializationException thrown if a problem occuring in the init
359 * method of a sub-class.
360 * @since 1.0
361 */
362 protected abstract void doInit() throws InitializationException;
363
364 /***
365 * The doCleanup() method is expected to be overriden by
366 * concrete application classes to perform any required
367 * cleanup facilitating a graceful application shutdown.
368 *
369 * @since 1.0
370 */
371 protected abstract void doCleanup();
372
373 /***
374 * returns true if the batch application should initialize the audit
375 * agent class. Otherwise returns false. To maintain backward
376 * compatibility, it always returns true unless overridden by a
377 * sub-class.
378 *
379 * @return boolean
380 * @since 2.0
381 */
382 protected boolean isAuditEnabled() {
383 return true;
384 }
385
386 }
This page was automatically generated by Maven