001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm;
023:
024: import java.io.ByteArrayInputStream;
025: import java.io.InputStream;
026: import java.io.Serializable;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.Map;
030: import java.util.Stack;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.jbpm.configuration.ObjectFactory;
035: import org.jbpm.configuration.ObjectFactoryImpl;
036: import org.jbpm.configuration.ObjectFactoryParser;
037: import org.jbpm.configuration.ObjectInfo;
038: import org.jbpm.configuration.ValueInfo;
039: import org.jbpm.job.executor.JobExecutor;
040: import org.jbpm.persistence.db.DbPersistenceServiceFactory;
041: import org.jbpm.persistence.db.StaleObjectLogConfigurer;
042: import org.jbpm.svc.ServiceFactory;
043: import org.jbpm.svc.Services;
044: import org.jbpm.util.ClassLoaderUtil;
045:
046: /**
047: * configuration of one jBPM instance.
048: *
049: * <p>During process execution, jBPM might need to use some services.
050: * A JbpmConfiguration contains the knowledge on how to create those services.
051: * </p>
052: *
053: * <p>A JbpmConfiguration is a thread safe object and serves as a factory for
054: * {@link org.jbpm.JbpmContext}s, which means one JbpmConfiguration
055: * can be used to create {@link org.jbpm.JbpmContext}s for all threads.
056: * The single JbpmConfiguration can be maintained in a static member or
057: * in the JNDI tree if that is available.
058: * </p>
059: *
060: * <p>A JbpmConfiguration can be obtained in following ways:
061: * <ul>
062: * <li>from a resource (by default <code>jbpm.cfg.xml</code> is used):
063: * <pre> JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
064: * </pre>
065: * or
066: * <pre> String myXmlResource = "...";
067: * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance(myXmlResource);</pre>
068: * </li>
069: * <li>from an XML string:
070: * <pre> JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString(
071: * "<jbpm-configuration>" +
072: * ...
073: * "</jbpm-configuration>"
074: * );
075: * </pre>
076: * </li>
077: * <li>By specifying a custom implementation of an object factory. This can be
078: * used to specify a JbpmConfiguration in other bean-style notations such as
079: * used by JBoss Microcontainer or Spring.
080: * <pre> ObjectFactory of = new <i>MyCustomObjectFactory</i>();
081: * JbpmConfiguration.Configs.setDefaultObjectFactory(of);
082: * JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
083: * </pre>
084: * </li>
085: * </ul>
086: * </p>
087: *
088: * <p>JbpmConfigurations can be configured using a spring-like XML notation
089: * (in relax ng compact notation):
090: * </p>
091: *
092: * <pre>
093: * datatypes xs = "http://www.w3.org/2001/XMLSchema-datatypes"
094: *
095: * start = element beans { element object* }
096: *
097: * object = {
098: * jbpm-context |
099: * bean |
100: * ref |
101: * map |
102: * list |
103: * string |
104: * int |
105: * long |
106: * float |
107: * double |
108: * char |
109: * bool |
110: * true |
111: * false |
112: * null
113: * }
114: *
115: * jbpm-context = element jbpm-context {
116: * ( attribute name {xsd:string},
117: * service*,
118: * save-operations?
119: * )
120: * }
121: *
122: * service = element service {
123: * ( attribute name {xsd:string},
124: * ( attribute factory {xsd:string} ) |
125: * ( factory )
126: * )
127: * }
128: *
129: * factory = element factory {
130: * ( bean |
131: * ref
132: * )
133: * }
134: *
135: * save-operations = element save-operations {
136: * ( save-operation* )
137: * }
138: *
139: * save-operation = element save-operation {
140: * ( ( attribute class {xsd:string} ) |
141: * ( bean |
142: * ref
143: * )
144: * )
145: * }
146: *
147: * bean = element bean {
148: * ( attribute ref-name {xsd:string} ) |
149: * ( attribute name {xsd:string}?,
150: * attribute class {xsd:string}?,
151: * attribute singleton { "true" | "false" }?,
152: * constructor*,
153: * field*,
154: * property*
155: * )
156: * }
157: *
158: * ref = element ref {
159: * ( attribute bean (xsd:string) )
160: * }
161: *
162: * constructor = element constructor {
163: * attribute class {xsd:string}?,
164: * ( attribute factory {xsd:string},
165: * attribute method {xsd:string}
166: * )?,
167: * parameter*
168: * }
169: *
170: * parameter = element parameter {
171: * attribute class {xsd:string},
172: * object
173: * }
174: *
175: * field = element field {
176: * attribute name {xsd:string},
177: * object
178: * }
179: *
180: * property = element property {
181: * ( attribute name {xsd:string} |
182: * attribute setter {xsd:string}
183: * ),
184: * object
185: * }
186: *
187: * map = element map {
188: * entry*
189: * }
190: *
191: * entry = element entry {
192: * key,
193: * value
194: * }
195: *
196: * key = element key {
197: * object
198: * }
199: *
200: * value = element value {
201: * object
202: * }
203: *
204: * list = element list {
205: * object*
206: * }
207: *
208: * string = element string {xsd:string}
209: * int = element integer {xsd:integer}
210: * long = element long {xsd:long}
211: * float = element float {xsd:string}
212: * double = element string {xsd:double}
213: * char = element char {xsd:character}
214: * bool = element bool { "true" | "false" }
215: * true = element true {}
216: * false = element false {}
217: * null = element null {}
218: * </pre>
219: * </p>
220: *
221: * <p>
222: * Other configuration properties
223: * <table>
224: * <tr>
225: * <td>jbpm.files.dir</td><td></td>
226: * </tr>
227: * <tr>
228: * <td>jbpm.types</td><td></td>
229: * </tr>
230: * </table>
231: * </p>
232: */
233: public class JbpmConfiguration implements Serializable {
234:
235: private static final long serialVersionUID = 1L;
236:
237: static ObjectFactory defaultObjectFactory = null;
238: static Map instances = new HashMap();
239:
240: /**
241: * resets static members for test isolation.
242: */
243: static void reset() {
244: defaultObjectFactory = null;
245: instances = new HashMap();
246: }
247:
248: ObjectFactory objectFactory = null;
249: static ThreadLocal jbpmConfigurationsStacks = new ThreadLocal();
250: ThreadLocal jbpmContextStacks = new ThreadLocal();
251: JobExecutor jobExecutor = null;
252:
253: public JbpmConfiguration(ObjectFactory objectFactory) {
254: this .objectFactory = objectFactory;
255: }
256:
257: public static JbpmConfiguration getInstance() {
258: return getInstance(null);
259: }
260:
261: public static JbpmConfiguration getInstance(String resource) {
262:
263: JbpmConfiguration instance = null;
264:
265: synchronized (instances) {
266: if (resource == null) {
267: resource = "jbpm.cfg.xml";
268: }
269:
270: instance = (JbpmConfiguration) instances.get(resource);
271: if (instance == null) {
272:
273: if (defaultObjectFactory != null) {
274: log
275: .debug("creating jbpm configuration from given default object factory '"
276: + defaultObjectFactory + "'");
277: instance = new JbpmConfiguration(
278: defaultObjectFactory);
279:
280: } else {
281:
282: try {
283: log.info("using jbpm configuration resource '"
284: + resource + "'");
285: InputStream jbpmCfgXmlStream = ClassLoaderUtil
286: .getStream(resource);
287:
288: ObjectFactory objectFactory = parseObjectFactory(jbpmCfgXmlStream);
289: instance = createJbpmConfiguration(objectFactory);
290:
291: } catch (RuntimeException e) {
292: throw new JbpmException(
293: "couldn't parse jbpm configuration from resource '"
294: + resource + "'", e);
295: }
296: }
297:
298: instances.put(resource, instance);
299: }
300: }
301:
302: return instance;
303: }
304:
305: public static boolean hasInstance(String resource) {
306: boolean hasInstance = false;
307: if (resource == null) {
308: resource = "jbpm.cfg.xml";
309: }
310: if ((instances != null) && (instances.containsKey(resource))) {
311: hasInstance = true;
312: }
313: return hasInstance;
314: }
315:
316: protected static ObjectFactory parseObjectFactory(
317: InputStream inputStream) {
318: log.debug("loading defaults in jbpm configuration");
319: ObjectFactoryParser objectFactoryParser = new ObjectFactoryParser();
320: ObjectFactoryImpl objectFactoryImpl = new ObjectFactoryImpl();
321: objectFactoryParser.parseElementsFromResource(
322: "org/jbpm/default.jbpm.cfg.xml", objectFactoryImpl);
323:
324: if (inputStream != null) {
325: log.debug("loading specific configuration...");
326: objectFactoryParser.parseElementsStream(inputStream,
327: objectFactoryImpl);
328: }
329:
330: return objectFactoryImpl;
331: }
332:
333: /**
334: * create an ObjectFacotory from an XML string.
335: */
336: public static JbpmConfiguration parseXmlString(String xml) {
337: log.debug("creating jbpm configuration from xml string");
338: InputStream inputStream = null;
339: if (xml != null) {
340: inputStream = new ByteArrayInputStream(xml.getBytes());
341: }
342: ObjectFactory objectFactory = parseObjectFactory(inputStream);
343: return createJbpmConfiguration(objectFactory);
344: }
345:
346: protected static JbpmConfiguration createJbpmConfiguration(
347: ObjectFactory objectFactory) {
348: JbpmConfiguration jbpmConfiguration = new JbpmConfiguration(
349: objectFactory);
350:
351: // now we make the bean jbpm.configuration always availble
352: if (objectFactory instanceof ObjectFactoryImpl) {
353: ObjectFactoryImpl objectFactoryImpl = (ObjectFactoryImpl) objectFactory;
354: ObjectInfo jbpmConfigurationInfo = new ValueInfo(
355: "jbpmConfiguration", jbpmConfiguration);
356: objectFactoryImpl.addObjectInfo(jbpmConfigurationInfo);
357:
358: if (mustStaleObjectExceptionsBeHidden(objectFactory)) {
359: StaleObjectLogConfigurer.hideStaleObjectExceptions();
360: }
361: }
362:
363: return jbpmConfiguration;
364: }
365:
366: private static boolean mustStaleObjectExceptionsBeHidden(
367: ObjectFactory objectFactory) {
368: if (!objectFactory
369: .hasObject("jbpm.hide.stale.object.exceptions")) {
370: return true;
371: }
372: Object o = (Boolean) objectFactory
373: .createObject("jbpm.hide.stale.object.exceptions");
374: if ((o instanceof Boolean)
375: && (((Boolean) o).booleanValue() == false)) {
376: return false;
377: }
378: return true;
379: }
380:
381: public static JbpmConfiguration parseInputStream(
382: InputStream inputStream) {
383: ObjectFactory objectFactory = parseObjectFactory(inputStream);
384: log.debug("creating jbpm configuration from input stream");
385: return createJbpmConfiguration(objectFactory);
386: }
387:
388: public static JbpmConfiguration parseResource(String resource) {
389: InputStream inputStream = null;
390: log.debug("creating jbpm configuration from resource '"
391: + resource + "'");
392: if (resource != null) {
393: inputStream = ClassLoaderUtil.getStream(resource);
394: }
395: ObjectFactory objectFactory = parseObjectFactory(inputStream);
396: return createJbpmConfiguration(objectFactory);
397: }
398:
399: public JbpmContext createJbpmContext() {
400: return createJbpmContext(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
401: }
402:
403: public JbpmContext createJbpmContext(String name) {
404: JbpmContext jbpmContext = (JbpmContext) objectFactory
405: .createObject(name);
406: jbpmContext.jbpmConfiguration = this ;
407: jbpmContextCreated(jbpmContext);
408: return jbpmContext;
409: }
410:
411: public ServiceFactory getServiceFactory(String serviceName) {
412: return getServiceFactory(serviceName,
413: JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
414: }
415:
416: public ServiceFactory getServiceFactory(String serviceName,
417: String jbpmContextName) {
418: ServiceFactory serviceFactory = null;
419: JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
420: try {
421: serviceFactory = jbpmContext.getServices()
422: .getServiceFactory(serviceName);
423: } finally {
424: jbpmContext.close();
425: }
426: return serviceFactory;
427: }
428:
429: /**
430: * gives the jbpm domain model access to configuration information via the current JbpmContext.
431: */
432: public abstract static class Configs {
433: public static ObjectFactory getObjectFactory() {
434: ObjectFactory objectFactory = null;
435: JbpmContext jbpmContext = JbpmContext
436: .getCurrentJbpmContext();
437: if (jbpmContext != null) {
438: objectFactory = jbpmContext.objectFactory;
439: } else {
440: objectFactory = getInstance().objectFactory;
441: }
442: return objectFactory;
443: }
444:
445: public static void setDefaultObjectFactory(
446: ObjectFactory objectFactory) {
447: defaultObjectFactory = objectFactory;
448: }
449:
450: public static boolean hasObject(String name) {
451: ObjectFactory objectFactory = getObjectFactory();
452: return objectFactory.hasObject(name);
453: }
454:
455: public static synchronized Object getObject(String name) {
456: ObjectFactory objectFactory = getObjectFactory();
457: return objectFactory.createObject(name);
458: }
459:
460: public static String getString(String name) {
461: return (String) getObject(name);
462: }
463:
464: public static long getLong(String name) {
465: return ((Long) getObject(name)).longValue();
466: }
467:
468: public static int getInt(String name) {
469: return ((Integer) getObject(name)).intValue();
470: }
471:
472: public static boolean getBoolean(String name) {
473: return ((Boolean) getObject(name)).booleanValue();
474: }
475: }
476:
477: public void createSchema() {
478: createSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
479: }
480:
481: public void createSchema(String jbpmContextName) {
482: JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
483: try {
484: Services services = jbpmContext.getServices();
485: DbPersistenceServiceFactory persistenceServiceFactory = (DbPersistenceServiceFactory) services
486: .getServiceFactory(Services.SERVICENAME_PERSISTENCE);
487: persistenceServiceFactory.createSchema();
488: } finally {
489: jbpmContext.close();
490: }
491: }
492:
493: public void dropSchema() {
494: dropSchema(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
495: }
496:
497: public void dropSchema(String jbpmContextName) {
498: JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
499: try {
500: Services services = jbpmContext.getServices();
501: DbPersistenceServiceFactory persistenceServiceFactory = (DbPersistenceServiceFactory) services
502: .getServiceFactory(Services.SERVICENAME_PERSISTENCE);
503: persistenceServiceFactory.dropSchema();
504: } finally {
505: jbpmContext.close();
506: }
507: }
508:
509: public void close() {
510: close(JbpmContext.DEFAULT_JBPM_CONTEXT_NAME);
511: }
512:
513: public void close(String jbpmContextName) {
514: JbpmContext jbpmContext = createJbpmContext(jbpmContextName);
515: try {
516:
517: synchronized (instances) {
518: Iterator iter = instances.keySet().iterator();
519: while (iter.hasNext()) {
520: String resource = (String) iter.next();
521: if (this == instances.get(resource)) {
522: instances.remove(resource);
523: break;
524: }
525: }
526: }
527:
528: if (jobExecutor != null) {
529: jobExecutor.stop();
530: }
531:
532: Map serviceFactories = jbpmContext.getServices()
533: .getServiceFactories();
534: if (serviceFactories != null) {
535: Iterator iter = serviceFactories.values().iterator();
536: while (iter.hasNext()) {
537: ServiceFactory serviceFactory = (ServiceFactory) iter
538: .next();
539: serviceFactory.close();
540: }
541: }
542: } finally {
543: jbpmContext.close();
544: }
545: }
546:
547: static JbpmConfiguration getCurrentJbpmConfiguration() {
548: JbpmConfiguration currentJbpmConfiguration = null;
549: Stack stack = getJbpmConfigurationStack();
550: if (!stack.isEmpty()) {
551: currentJbpmConfiguration = (JbpmConfiguration) stack.peek();
552: }
553: return currentJbpmConfiguration;
554: }
555:
556: static synchronized Stack getJbpmConfigurationStack() {
557: Stack stack = (Stack) jbpmConfigurationsStacks.get();
558: if (stack == null) {
559: stack = new Stack();
560: jbpmConfigurationsStacks.set(stack);
561: }
562: return stack;
563: }
564:
565: synchronized void pushJbpmConfiguration() {
566: getJbpmConfigurationStack().push(this );
567: }
568:
569: synchronized void popJbpmConfiguration() {
570: getJbpmConfigurationStack().remove(this );
571: }
572:
573: public JbpmContext getCurrentJbpmContext() {
574: JbpmContext currentJbpmContext = null;
575: Stack stack = getJbpmContextStack();
576: if (!stack.isEmpty()) {
577: currentJbpmContext = (JbpmContext) stack.peek();
578: }
579: return currentJbpmContext;
580: }
581:
582: Stack getJbpmContextStack() {
583: Stack stack = (Stack) jbpmContextStacks.get();
584: if (stack == null) {
585: stack = new Stack();
586: jbpmContextStacks.set(stack);
587: }
588: return stack;
589: }
590:
591: void pushJbpmContext(JbpmContext jbpmContext) {
592: getJbpmContextStack().push(jbpmContext);
593: }
594:
595: void popJbpmContext(JbpmContext jbpmContext) {
596: Stack stack = getJbpmContextStack();
597: if (stack.isEmpty()) {
598: throw new JbpmException(
599: "closed JbpmContext more then once... check your try-finally's around JbpmContexts blocks");
600: }
601: JbpmContext popped = (JbpmContext) stack.pop();
602: if (jbpmContext != popped) {
603: throw new JbpmException(
604: "closed JbpmContext in different order then they were created... check your try-finally's around JbpmContexts blocks");
605: }
606: }
607:
608: void jbpmContextCreated(JbpmContext jbpmContext) {
609: pushJbpmConfiguration();
610: pushJbpmContext(jbpmContext);
611: }
612:
613: void jbpmContextClosed(JbpmContext jbpmContext) {
614: popJbpmConfiguration();
615: popJbpmContext(jbpmContext);
616: }
617:
618: public void startJobExecutor() {
619: getJobExecutor().start();
620: }
621:
622: public synchronized JobExecutor getJobExecutor() {
623: if (jobExecutor == null) {
624: try {
625: jobExecutor = (JobExecutor) this .objectFactory
626: .createObject("jbpm.job.executor");
627: } catch (ClassCastException e) {
628: throw new JbpmException(
629: "jbpm configuration object under key 'jbpm.job.executor' is not a "
630: + JobExecutor.class.getName(), e);
631: }
632: }
633: return jobExecutor;
634: }
635:
636: private static Log log = LogFactory.getLog(JbpmConfiguration.class);
637: }
|