001: /*
002: * Copyright 2005 JBoss Inc
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.drools;
018:
019: import java.io.Serializable;
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.List;
023: import java.util.Map;
024: import java.util.Properties;
025:
026: import org.drools.common.AgendaGroupFactory;
027: import org.drools.common.ArrayAgendaGroupFactory;
028: import org.drools.common.PriorityQueueAgendaGroupFactory;
029: import org.drools.concurrent.ExecutorService;
030: import org.drools.spi.ConflictResolver;
031: import org.drools.spi.ConsequenceExceptionHandler;
032: import org.drools.util.ChainedProperties;
033:
034: /**
035: * RuleBaseConfiguration
036: *
037: * A class to store RuleBase related configuration. It must be used at rule base instantiation time
038: * or not used at all.
039: * This class will automatically load default values from system properties, so if you want to set
040: * a default configuration value for all your new rule bases, you can simply set the property as
041: * a System property.
042: *
043: * After RuleBase is created, it makes the configuration immutable and there is no way to make it
044: * mutable again. This is to avoid inconsistent behavior inside rulebase.
045: *
046: * NOTE: This API is under review and may change in the future.
047: */
048:
049: /**
050: * drools.shadowproxy = <true|false>
051: * drools.shadowproxy.exclude = org.domainy.* org.domainx.ClassZ
052: * drools.sequential = <true|false>
053: * drools.sequential.agenda = <sequential|dynamic>
054: * drools.removeIdentities = <true|false>
055: * drools.shareAlphaNodes = <true|false>
056: * drools.shareBetaNodes = <true|false>
057: * drools.alphaMemory <true/false>
058: * drools.alphaNodeHashingThreshold = <1...n>
059: * drools.compositeKeyDepth =<1..3>
060: * drools.indexLeftBetaMemory = <true/false>
061: * drools.indexRightBetaMemory = <true/false>
062: * drools.assertBehaviour = <identity|equality>
063: * drools.logicalOverride = <discard|preserve>
064: * drools.executorService = <qualified class name>
065: * drools.conflictResolver = <qualified class name>
066: * drools.conflictExceptionHandler = <qualified class name>
067: *
068: */
069: public class RuleBaseConfiguration implements Serializable {
070: private static final long serialVersionUID = 400L;
071:
072: private ChainedProperties chainedProperties;
073:
074: private boolean immutable;
075:
076: private boolean sequential;
077: private SequentialAgenda sequentialAgenda;
078:
079: private boolean maintainTms;
080: private boolean removeIdentities;
081: private boolean shareAlphaNodes;
082: private boolean shareBetaNodes;
083: private boolean alphaMemory;
084: private int alphaNodeHashingThreshold;
085: private int compositeKeyDepth;
086: private boolean indexLeftBetaMemory;
087: private boolean indexRightBetaMemory;
088: private AssertBehaviour assertBehaviour;
089: private LogicalOverride logicalOverride;
090: private ExecutorService executorService;
091: private ConsequenceExceptionHandler consequenceExceptionHandler;
092:
093: private ConflictResolver conflictResolver;
094:
095: private boolean shadowProxy;
096: private Map shadowProxyExcludes;
097: private static final String STAR = "*";
098:
099: public RuleBaseConfiguration(Properties properties) {
100: init(properties);
101: }
102:
103: public RuleBaseConfiguration() {
104: init(null);
105: }
106:
107: private void init(Properties properties) {
108: this .immutable = false;
109:
110: this .chainedProperties = new ChainedProperties("rulebase.conf");
111:
112: if (properties != null) {
113: this .chainedProperties.addProperties(properties);
114: }
115:
116: setSequentialAgenda(SequentialAgenda
117: .determineSequentialAgenda(this .chainedProperties
118: .getProperty("drools.sequential.agenda",
119: "sequential")));
120:
121: setSequential(Boolean.valueOf(
122: this .chainedProperties.getProperty("drools.sequential",
123: "false")).booleanValue());
124:
125: setMaintainTms(Boolean.valueOf(
126: this .chainedProperties.getProperty(
127: "drools.maintainTms", "true")).booleanValue());
128:
129: setRemoveIdentities(Boolean.valueOf(
130: this .chainedProperties.getProperty(
131: "drools.removeIdentities", "false"))
132: .booleanValue());
133:
134: setAlphaMemory(Boolean.valueOf(
135: this .chainedProperties.getProperty(
136: "drools.alphaMemory", "false")).booleanValue());
137:
138: setShareAlphaNodes(Boolean.valueOf(
139: this .chainedProperties.getProperty(
140: "drools.shareAlphaNodes", "true"))
141: .booleanValue());
142:
143: setShareBetaNodes(Boolean.valueOf(
144: this .chainedProperties.getProperty(
145: "drools.shareBetaNodes", "true"))
146: .booleanValue());
147:
148: setAlphaNodeHashingThreshold(Integer
149: .parseInt(this .chainedProperties.getProperty(
150: "drools.alphaNodeHashingThreshold", "3")));
151:
152: setCompositeKeyDepth(Integer.parseInt(this .chainedProperties
153: .getProperty("drools.compositeKeyDepth", "3")));
154:
155: setIndexLeftBetaMemory(Boolean.valueOf(
156: this .chainedProperties.getProperty(
157: "drools.indexLeftBetaMemory", "true"))
158: .booleanValue());
159: setIndexRightBetaMemory(Boolean.valueOf(
160: this .chainedProperties.getProperty(
161: "drools.indexRightBetaMemory", "true"))
162: .booleanValue());
163:
164: setAssertBehaviour(AssertBehaviour
165: .determineAssertBehaviour(this .chainedProperties
166: .getProperty("drools.assertBehaviour",
167: "identity")));
168: setLogicalOverride(LogicalOverride
169: .determineLogicalOverride(this .chainedProperties
170: .getProperty("drools.logicalOverride",
171: "discard")));
172:
173: setExecutorService(RuleBaseConfiguration
174: .determineExecutorService(this .chainedProperties
175: .getProperty("drools.executorService",
176: "org.drools.concurrent.DefaultExecutorService")));
177:
178: setConsequenceExceptionHandler(RuleBaseConfiguration
179: .determineConsequenceExceptionHandler(this .chainedProperties
180: .getProperty(
181: "drools.consequenceExceptionHandler",
182: "org.drools.base.DefaultConsequenceExceptionHandler")));
183:
184: setConflictResolver(RuleBaseConfiguration
185: .determineConflictResolver(this .chainedProperties
186: .getProperty("drools.conflictResolver",
187: "org.drools.conflict.DepthConflictResolver")));
188:
189: setShadowProxy(determineShadowProxy(this .chainedProperties
190: .getProperty("drools.shadowproxy", null)));
191:
192: setShadowProxyExcludes(this .chainedProperties.getProperty(
193: "drools.shadowProxyExcludes", ""));
194: }
195:
196: /**
197: * Makes the configuration object immutable. Once it becomes immutable,
198: * there is no way to make it mutable again.
199: * This is done to keep consistency.
200: */
201: public void makeImmutable() {
202: this .immutable = true;
203: }
204:
205: /**
206: * Returns true if this configuration object is immutable or false otherwise.
207: * @return
208: */
209: public boolean isImmutable() {
210: return this .immutable;
211: }
212:
213: private void checkCanChange() {
214: if (this .immutable) {
215: throw new UnsupportedOperationException(
216: "Can't set a property after configuration becomes immutable");
217: }
218: }
219:
220: public void setSequential(boolean sequential) {
221: this .sequential = sequential;
222: }
223:
224: public boolean isSequential() {
225: return this .sequential;
226: }
227:
228: public boolean isMaintainTms() {
229: return this .maintainTms;
230: }
231:
232: public void setMaintainTms(final boolean maintainTms) {
233: checkCanChange(); // throws an exception if a change isn't possible;
234: this .maintainTms = maintainTms;
235: }
236:
237: public boolean isRemoveIdentities() {
238: return this .removeIdentities;
239: }
240:
241: public void setRemoveIdentities(final boolean removeIdentities) {
242: checkCanChange(); // throws an exception if a change isn't possible;
243: this .removeIdentities = removeIdentities;
244: }
245:
246: public boolean isAlphaMemory() {
247: return this .alphaMemory;
248: }
249:
250: public void setAlphaMemory(final boolean alphaMemory) {
251: checkCanChange(); // throws an exception if a change isn't possible;
252: this .alphaMemory = alphaMemory;
253: }
254:
255: public boolean isShareAlphaNodes() {
256: return this .shareAlphaNodes;
257: }
258:
259: public void setShareAlphaNodes(final boolean shareAlphaNodes) {
260: checkCanChange(); // throws an exception if a change isn't possible;
261: this .shareAlphaNodes = shareAlphaNodes;
262: }
263:
264: public boolean isShareBetaNodes() {
265: return this .shareBetaNodes;
266: }
267:
268: public void setShareBetaNodes(final boolean shareBetaNodes) {
269: checkCanChange(); // throws an exception if a change isn't possible;
270: this .shareBetaNodes = shareBetaNodes;
271: }
272:
273: public int getAlphaNodeHashingThreshold() {
274: return this .alphaNodeHashingThreshold;
275: }
276:
277: public void setAlphaNodeHashingThreshold(
278: final int alphaNodeHashingThreshold) {
279: checkCanChange(); // throws an exception if a change isn't possible;
280: this .alphaNodeHashingThreshold = alphaNodeHashingThreshold;
281: }
282:
283: public AssertBehaviour getAssertBehaviour() {
284: return this .assertBehaviour;
285: }
286:
287: public void setAssertBehaviour(final AssertBehaviour assertBehaviour) {
288: checkCanChange(); // throws an exception if a change isn't possible;
289: this .assertBehaviour = assertBehaviour;
290: }
291:
292: public int getCompositeKeyDepth() {
293: return this .compositeKeyDepth;
294: }
295:
296: public void setCompositeKeyDepth(final int compositeKeyDepth) {
297: if (!this .immutable) {
298: if (compositeKeyDepth > 3) {
299: throw new UnsupportedOperationException(
300: "compositeKeyDepth cannot be greater than 3");
301: }
302: this .compositeKeyDepth = compositeKeyDepth;
303: } else {
304: throw new UnsupportedOperationException(
305: "Can't set a property after configuration becomes immutable");
306: }
307: }
308:
309: public boolean isIndexLeftBetaMemory() {
310: return this .indexLeftBetaMemory;
311: }
312:
313: public void setIndexLeftBetaMemory(final boolean indexLeftBetaMemory) {
314: checkCanChange(); // throws an exception if a change isn't possible;
315: this .indexLeftBetaMemory = indexLeftBetaMemory;
316: }
317:
318: public boolean isIndexRightBetaMemory() {
319: return this .indexRightBetaMemory;
320: }
321:
322: public void setIndexRightBetaMemory(
323: final boolean indexRightBetaMemory) {
324: checkCanChange(); // throws an exception if a change isn't possible;
325: this .indexRightBetaMemory = indexRightBetaMemory;
326: }
327:
328: public LogicalOverride getLogicalOverride() {
329: return this .logicalOverride;
330: }
331:
332: public void setLogicalOverride(final LogicalOverride logicalOverride) {
333: checkCanChange(); // throws an exception if a change isn't possible;
334: this .logicalOverride = logicalOverride;
335: }
336:
337: public ExecutorService getExecutorService() {
338: return executorService;
339: }
340:
341: public void setExecutorService(ExecutorService executorService) {
342: checkCanChange(); // throws an exception if a change isn't possible;
343: this .executorService = executorService;
344: }
345:
346: public ConsequenceExceptionHandler getConsequenceExceptionHandler() {
347: return consequenceExceptionHandler;
348: }
349:
350: public void setConsequenceExceptionHandler(
351: ConsequenceExceptionHandler consequenceExceptionHandler) {
352: checkCanChange(); // throws an exception if a change isn't possible;
353: this .consequenceExceptionHandler = consequenceExceptionHandler;
354: }
355:
356: public AgendaGroupFactory getAgendaGroupFactory() {
357: if (isSequential()) {
358: if (this .sequentialAgenda == SequentialAgenda.SEQUENTIAL) {
359: return ArrayAgendaGroupFactory.getInstance();
360: } else {
361: return PriorityQueueAgendaGroupFactory.getInstance();
362: }
363: } else {
364: return PriorityQueueAgendaGroupFactory.getInstance();
365: }
366: }
367:
368: public SequentialAgenda getSequentialAgenda() {
369: return this .sequentialAgenda;
370: }
371:
372: public void setSequentialAgenda(
373: final SequentialAgenda sequentialAgenda) {
374: checkCanChange(); // throws an exception if a change isn't possible;
375: this .sequentialAgenda = sequentialAgenda;
376: }
377:
378: private boolean determineShadowProxy(String userValue) {
379: if (userValue != null) {
380: return Boolean.valueOf(userValue).booleanValue();
381: } else {
382: if (this .isSequential()) {
383: return false;
384: } else {
385: return true;
386: }
387: }
388: }
389:
390: private static ConflictResolver determineConflictResolver(
391: String className) {
392: Class clazz = null;
393: try {
394: clazz = Thread.currentThread().getContextClassLoader()
395: .loadClass(className);
396: } catch (ClassNotFoundException e) {
397: }
398:
399: if (clazz == null) {
400: try {
401: clazz = RuleBaseConfiguration.class.getClassLoader()
402: .loadClass(className);
403: } catch (ClassNotFoundException e) {
404: }
405: }
406:
407: if (clazz != null) {
408: try {
409: return (ConflictResolver) clazz.getMethod(
410: "getInstance", null).invoke(null, null);
411: } catch (Exception e) {
412: throw new IllegalArgumentException(
413: "Unable to Conflict Resolver '" + className
414: + "'");
415: }
416: } else {
417: throw new IllegalArgumentException("conflict Resolver '"
418: + className + "' not found");
419: }
420: }
421:
422: public void setConflictResolver(ConflictResolver conflictResolver) {
423: checkCanChange(); // throws an exception if a change isn't possible;
424: this .conflictResolver = conflictResolver;
425: }
426:
427: public ConflictResolver getConflictResolver() {
428: return this .conflictResolver;
429: }
430:
431: public void setShadowProxy(boolean shadowProxy) {
432: checkCanChange(); // throws an exception if a change isn't possible;
433: this .shadowProxy = shadowProxy;
434: }
435:
436: public boolean isShadowProxy() {
437: return this .shadowProxy;
438: }
439:
440: public void setShadowProxyExcludes(String excludes) {
441: checkCanChange(); // throws an exception if a change isn't possible;
442: if (excludes == null || "".equals(excludes.trim())) {
443: return;
444: }
445:
446: if (this .shadowProxyExcludes == null) {
447: this .shadowProxyExcludes = new HashMap();
448: }
449:
450: String[] items = excludes.split(" ");
451: for (int i = 0; i < items.length; i++) {
452: String qualifiedNamespace = items[i].substring(0,
453: items[i].lastIndexOf('.')).trim();
454: String name = items[i].substring(
455: items[i].lastIndexOf('.') + 1).trim();
456: Object object = this .shadowProxyExcludes
457: .get(qualifiedNamespace);
458: if (object == null) {
459: if (STAR.equals(name)) {
460: this .shadowProxyExcludes.put(qualifiedNamespace,
461: STAR);
462: } else {
463: // create a new list and add it
464: List list = new ArrayList();
465: list.add(name);
466: this .shadowProxyExcludes.put(qualifiedNamespace,
467: list);
468: }
469: } else if (name.equals(STAR)) {
470: // if its a STAR now add it anyway, we don't care if it was a STAR or a List before
471: this .shadowProxyExcludes.put(qualifiedNamespace, STAR);
472: } else {
473: // its a list so add it if it doesn't already exist
474: List list = (List) object;
475: if (!list.contains(object)) {
476: list.add(name);
477: }
478: }
479: }
480: }
481:
482: public boolean isShadowed(String className) {
483: if (this .shadowProxyExcludes == null) {
484: return true;
485: }
486:
487: String qualifiedNamespace = className.substring(0,
488: className.lastIndexOf('.')).trim();
489: String name = className.substring(
490: className.lastIndexOf('.') + 1).trim();
491: Object object = this .shadowProxyExcludes
492: .get(qualifiedNamespace);
493: if (object == null) {
494: return true;
495: } else if (STAR.equals(object)) {
496: return false;
497: } else {
498: List list = (List) object;
499: return !list.contains(name);
500: }
501: }
502:
503: private static ExecutorService determineExecutorService(
504: String className) {
505: return (ExecutorService) instantiateClass("ExecutorService",
506: className);
507: }
508:
509: private static ConsequenceExceptionHandler determineConsequenceExceptionHandler(
510: String className) {
511: return (ConsequenceExceptionHandler) instantiateClass(
512: "ConsequenceExceptionHandler", className);
513: }
514:
515: private static Object instantiateClass(String type, String className) {
516: Class clazz = null;
517: try {
518: clazz = Thread.currentThread().getContextClassLoader()
519: .loadClass(className);
520: } catch (ClassNotFoundException e) {
521: }
522:
523: if (clazz == null) {
524: try {
525: clazz = RuleBaseConfiguration.class.getClassLoader()
526: .loadClass(className);
527: } catch (ClassNotFoundException e) {
528: }
529: }
530:
531: if (clazz != null) {
532: try {
533: return clazz.newInstance();
534: } catch (Exception e) {
535: throw new IllegalArgumentException(
536: "Unable to instantiate " + type + " '"
537: + className + "'");
538: }
539: } else {
540: throw new IllegalArgumentException(type + " '" + className
541: + "' not found");
542: }
543: }
544:
545: public static class AssertBehaviour implements Serializable {
546: private static final long serialVersionUID = 400L;
547:
548: public static final AssertBehaviour IDENTITY = new AssertBehaviour(
549: 0);
550: public static final AssertBehaviour EQUALITY = new AssertBehaviour(
551: 1);
552:
553: private int value;
554:
555: private AssertBehaviour(final int value) {
556: this .value = value;
557: }
558:
559: public static AssertBehaviour determineAssertBehaviour(
560: final String value) {
561: if ("IDENTITY".equalsIgnoreCase(value)) {
562: return IDENTITY;
563: } else if ("EQUALITY".equalsIgnoreCase(value)) {
564: return EQUALITY;
565: } else {
566: throw new IllegalArgumentException(
567: "Illegal enum value '" + value
568: + "' for AssertBehaviour");
569: }
570: }
571:
572: private Object readResolve()
573: throws java.io.ObjectStreamException {
574: switch (this .value) {
575: case 0:
576: return IDENTITY;
577: case 1:
578: return EQUALITY;
579: default:
580: throw new IllegalArgumentException(
581: "Illegal enum value '" + this .value
582: + "' for AssertBehaviour");
583: }
584: }
585:
586: public String toString() {
587: return "AssertBehaviour : "
588: + ((this .value == 0) ? "identity" : "equality");
589: }
590: }
591:
592: public static class LogicalOverride implements Serializable {
593: private static final long serialVersionUID = 400L;
594:
595: public static final LogicalOverride PRESERVE = new LogicalOverride(
596: 0);
597: public static final LogicalOverride DISCARD = new LogicalOverride(
598: 1);
599:
600: private int value;
601:
602: private LogicalOverride(final int value) {
603: this .value = value;
604: }
605:
606: public static LogicalOverride determineLogicalOverride(
607: final String value) {
608: if ("PRESERVE".equalsIgnoreCase(value)) {
609: return PRESERVE;
610: } else if ("DISCARD".equalsIgnoreCase(value)) {
611: return DISCARD;
612: } else {
613: throw new IllegalArgumentException(
614: "Illegal enum value '" + value
615: + "' for LogicalOverride");
616: }
617: }
618:
619: private Object readResolve()
620: throws java.io.ObjectStreamException {
621: switch (this .value) {
622: case 0:
623: return PRESERVE;
624: case 1:
625: return DISCARD;
626: default:
627: throw new IllegalArgumentException(
628: "Illegal enum value '" + this .value
629: + "' for LogicalOverride");
630: }
631: }
632:
633: public String toString() {
634: return "LogicalOverride : "
635: + ((this .value == 0) ? "preserve" : "discard");
636: }
637: }
638:
639: public static class SequentialAgenda implements Serializable {
640: private static final long serialVersionUID = 400L;
641:
642: public static final SequentialAgenda SEQUENTIAL = new SequentialAgenda(
643: 0);
644: public static final SequentialAgenda DYNAMIC = new SequentialAgenda(
645: 1);
646:
647: private int value;
648:
649: private SequentialAgenda(final int value) {
650: this .value = value;
651: }
652:
653: public static SequentialAgenda determineSequentialAgenda(
654: final String value) {
655: if ("sequential".equalsIgnoreCase(value)) {
656: return SEQUENTIAL;
657: } else if ("dynamic".equalsIgnoreCase(value)) {
658: return DYNAMIC;
659: } else {
660: throw new IllegalArgumentException(
661: "Illegal enum value '" + value
662: + "' for SequentialAgenda");
663: }
664: }
665:
666: private Object readResolve()
667: throws java.io.ObjectStreamException {
668: switch (this .value) {
669: case 0:
670: return SEQUENTIAL;
671: case 1:
672: return DYNAMIC;
673: default:
674: throw new IllegalArgumentException(
675: "Illegal enum value '" + this .value
676: + "' for SequentialAgenda");
677: }
678: }
679:
680: public String toString() {
681: return "SequentialAgenda : "
682: + ((this .value == 0) ? "sequential" : "dynamic");
683: }
684: }
685: }
|