2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.controlloop.tdjam;
23 import static org.onap.policy.drools.properties.DroolsPropertyConstants.PROPERTY_CONTROLLER_TYPE;
25 import java.io.ByteArrayOutputStream;
26 import java.io.PrintStream;
27 import java.nio.charset.StandardCharsets;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.List;
34 import java.util.Properties;
35 import java.util.UUID;
36 import java.util.concurrent.ConcurrentHashMap;
37 import org.onap.policy.common.endpoints.event.comm.Topic;
38 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
39 import org.onap.policy.common.endpoints.event.comm.TopicListener;
40 import org.onap.policy.common.endpoints.event.comm.TopicSource;
41 import org.onap.policy.controlloop.CanonicalOnset;
42 import org.onap.policy.controlloop.ControlLoopEvent;
43 import org.onap.policy.controlloop.ControlLoopException;
44 import org.onap.policy.controlloop.ControlLoopNotificationType;
45 import org.onap.policy.controlloop.ControlLoopResponse;
46 import org.onap.policy.controlloop.VirtualControlLoopEvent;
47 import org.onap.policy.controlloop.VirtualControlLoopNotification;
48 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
49 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
50 import org.onap.policy.controlloop.utils.ControlLoopUtils;
51 import org.onap.policy.drools.controller.DroolsController;
52 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
53 import org.onap.policy.drools.features.PolicyControllerFeatureApi;
54 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
55 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
56 import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset;
57 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
58 import org.onap.policy.drools.system.PolicyController;
59 import org.onap.policy.drools.system.PolicyEngineConstants;
60 import org.onap.policy.extension.system.NonDroolsPolicyController;
61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
66 * This replaces a Drools session with Java code. Although Drools memory
67 * is simulated when running the Junit tests, there is no actual use of
70 public class TdjamController extends NonDroolsPolicyController {
71 private static Logger logger = LoggerFactory.getLogger(TdjamController.class);
73 // the 'controller.type' property is set to this value
74 private static final String TDJAM_CONTROLLER_BUILDER_TAG = "tdjam";
76 // topic on which to publish event notifications
77 private static final String POLICY_CL_MGT = "POLICY-CL-MGT";
79 // additional data associated with session
80 private final String groupId;
81 private final String artifactId;
83 // top-level tosca policy table (first key = name, second key = version)
84 private final Map<String, Map<String, ToscaPolicy>> toscaPolicies = new HashMap<>();
86 // maps 'controlLoopControlName' to 'ControlLoopParams'
87 private final Map<String, ControlLoopParams> controlLoopParams = new HashMap<>();
89 // maps 'requestId' to 'ControlLoopEventManager'
90 private final Map<UUID, ControlLoopEventManager> eventManagers = new ConcurrentHashMap<>();
92 // maps onset to 'ControlLoopEventManager'
93 private final Map<VirtualControlLoopEvent, ControlLoopEventManager> onsetToEventManager = new ConcurrentHashMap<>();
95 // maps 'topic' to 'TopicData'
96 private final Map<String, TopicData> topicDataTable = new ConcurrentHashMap<>();
98 /* ============================================================ */
101 * Initialize a new 'TdjamController'.
103 * @param name the controller name
104 * @param properties properties defining the controller
106 public TdjamController(String name, Properties properties) {
107 super(name, properties);
109 this.groupId = getGroupId();
110 this.artifactId = getArtifactId();
115 private void init() {
116 // go through all of the incoming message decoders associated
117 // with this controller
118 for (ProtocolCoderToolset pct :
119 EventProtocolCoderConstants.getManager()
120 .getDecoders(groupId, artifactId)) {
121 // go through the 'CoderFilters' instances, and see if there are
122 // any that we are interested in
123 for (CoderFilters cf : pct.getCoders()) {
125 Class<?> clazz = Class.forName(cf.getCodedClass());
126 if (ControlLoopEvent.class.isAssignableFrom(clazz)) {
127 // this one is of interest
128 logger.debug("TdjamController using CoderFilters: {}", cf);
129 getTopicData(pct.getTopic());
131 } catch (ClassNotFoundException e) {
132 logger.error("CoderFilter refers to unknown class: {}",
133 cf.getCodedClass(), e);
138 // start all 'TopicData' instances
139 for (TopicData topicData : topicDataTable.values()) {
145 public <T> boolean offer(T object) {
146 if (object instanceof ToscaPolicy) {
147 addToscaPolicy((ToscaPolicy) object);
154 * Add or replace a ToscaPolicy instance. The policy is keyed by name and
157 * @param toscaPolicy the ToscaPolicy being added
158 * @return if a ToscaPolicy with this name/version previously existed within
159 * this TdjamController, it is returned; otherwise, 'null' is returned.
161 public synchronized ToscaPolicy addToscaPolicy(ToscaPolicy toscaPolicy) {
162 Map<String, ToscaPolicy> level2 =
163 toscaPolicies.computeIfAbsent(toscaPolicy.getName(),
164 key -> new HashMap<String, ToscaPolicy>());
165 ToscaPolicy prev = level2.put(toscaPolicy.getVersion(), toscaPolicy);
167 // update 'ControlLoopParams' entries
168 for (ControlLoopParams clp : controlLoopParams.values()) {
169 if (clp.getToscaPolicy() == prev) {
170 clp.setToscaPolicy(toscaPolicy);
174 logger.debug("ToscaPolicy name={}, version={}, count={}, prev={}",
175 toscaPolicy.getName(), toscaPolicy.getVersion(), toscaPolicies.size(), (prev != null));
178 // attempt to create a 'ControlLoopParams' instance from this object
179 ControlLoopParams params =
180 ControlLoopUtils.toControlLoopParams(toscaPolicy);
181 if (params != null) {
182 addControlLoopParams(params);
188 * Remove a ToscaPolicy instance associated with the specified name and
191 * @param name the name of the ToscaPolicy to remove
192 * @param version the version of the ToscaPolicy to remove
193 * @return the ToscaPolicy that was removed, or 'null' if not found
195 public synchronized ToscaPolicy removeToscaPolicy(String name, String version) {
196 ToscaPolicy prev = null;
197 Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
199 if (level2 != null && (prev = level2.remove(version)) != null) {
200 // remove all 'ControlLoopParams' entries referencing this policy
201 for (ControlLoopParams clp :
202 new ArrayList<>(controlLoopParams.values())) {
203 if (clp.getToscaPolicy() == prev) {
204 controlLoopParams.remove(clp.getClosedLoopControlName());
212 * Fetch a ToscaPolicy instance associated with the specified name and
215 * @param name the name of the ToscaPolicy
216 * @param version the version of the ToscaPolicy
217 * @return the ToscaPolicy, or 'null' if not found
219 public synchronized ToscaPolicy getToscaPolicy(String name, String version) {
220 Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
221 return (level2 == null ? null : level2.get(version));
225 * Return a collection of all ToscaPolicy instances.
227 * @return all ToscaPolicy instances
229 public synchronized Collection<ToscaPolicy> getAllToscaPolicies() {
230 HashSet<ToscaPolicy> rval = new HashSet<>();
231 for (Map<String, ToscaPolicy> map : toscaPolicies.values()) {
232 rval.addAll(map.values());
238 * Add a new 'ControlLoopParams' instance -- they are keyed by
239 * 'closedLoopControlName'.
241 * @param clp the 'ControlLoopParams' instance to add
242 * @return the 'ControlLoopParams' instance previously associated with the
243 * 'closedLoopControlName' ('null' if it didn't exist)
245 public synchronized ControlLoopParams addControlLoopParams(ControlLoopParams clp) {
246 ToscaPolicy toscaPolicy =
247 getToscaPolicy(clp.getPolicyName(), clp.getPolicyVersion());
248 if (toscaPolicy == null) {
249 // there needs to be a 'ToscaPolicy' instance with a matching
251 logger.debug("Missing ToscaPolicy, name={}, version={}",
252 clp.getPolicyName(), clp.getPolicyVersion());
256 clp.setToscaPolicy(toscaPolicy);
257 ControlLoopParams prev =
258 controlLoopParams.put(clp.getClosedLoopControlName(), clp);
260 logger.debug("ControlLoopParams name={}, version={}, closedLoopControlName={}, count={}, prev={}",
261 clp.getPolicyName(), clp.getPolicyVersion(),
262 clp.getClosedLoopControlName(), controlLoopParams.size(), (prev != null));
268 * Return a collection of all ControlLoopParams instances.
270 * @return all ControlLoopParams instances
272 public synchronized Collection<ControlLoopParams> getAllControlLoopParams() {
273 return new ArrayList<>(controlLoopParams.values());
277 * Return a collection of all EventManager instances.
279 * @return all EventManager instances
282 public synchronized Collection<ControlLoopEventManager> getAllEventManagers() {
283 return new ArrayList<>(eventManagers.values());
287 * Return a collection of all onsetToEventManager instances.
289 * @return all onsetToEventManager instances
292 public synchronized Collection<ControlLoopEventManager> getAllOnsetToEventManager() {
293 return new ArrayList<>(onsetToEventManager.values());
297 * Reset the controller.
300 public synchronized void reset() {
301 toscaPolicies.clear();
302 controlLoopParams.clear();
303 eventManagers.clear();
304 onsetToEventManager.clear();
308 public boolean stop() {
311 // stop all 'TopicData' instances
312 for (TopicData topicData : topicDataTable.values()) {
319 * Remove a ControlLoopParams instance associated with the specified
320 * 'closedLoopControlName'.
322 * @param closedLoopControlName the closedLoopControlName identifying the
323 * ControlLoopParams instance
324 * @return the 'ControlLoopParams' instance, 'null' if not found
326 public synchronized ControlLoopParams removeControlLoopParams(String closedLoopControlName) {
327 return controlLoopParams.remove(closedLoopControlName);
331 * Dump out the ToscaPolicy and ControlLoopParams tables in
332 * human-readable form.
334 private void dumpTables() {
335 ByteArrayOutputStream bos = new ByteArrayOutputStream();
336 PrintStream out = new PrintStream(bos, true);
338 // name(25) version(10) closedLoopControlName(...)
340 String format = "%-25s %-10s %s\n";
341 out.println("ToscaPolicy Table");
342 out.format(format, "Name", "Version", "");
343 out.format(format, "----", "-------", "");
345 for (Map<String, ToscaPolicy> level2 : toscaPolicies.values()) {
346 for (ToscaPolicy tp : level2.values()) {
347 out.format(format, tp.getName(), tp.getVersion(), "");
351 out.println("\nControlLoopParams Table");
352 out.format(format, "Name", "Version", "ClosedLoopControlName");
353 out.format(format, "----", "-------", "---------------------");
354 for (ControlLoopParams cp : controlLoopParams.values()) {
355 out.format(format, cp.getPolicyName(), cp.getPolicyVersion(),
356 cp.getClosedLoopControlName());
359 if (logger.isDebugEnabled()) {
360 logger.debug(new String(bos.toByteArray(), StandardCharsets.UTF_8));
365 * Find or create a 'TopicData' instance associated with the specified
368 * @param name the topic name
369 * @return the new or existing 'TopicData' instance associated with 'name'
371 private TopicData getTopicData(String name) {
372 return topicDataTable.computeIfAbsent(name, key -> new TopicData(name));
375 /* ============================================================ */
378 * Process an incoming 'ControlLoopEvent'.
380 * @param event the incoming 'ControlLoopEvent'
382 private void processEvent(ControlLoopEvent event) {
383 String clName = event.getClosedLoopControlName();
384 ControlLoopParams params = controlLoopParams.get(clName);
385 if (params == null) {
386 logger.debug("No ControlLoopParams for event: {}", event);
390 UUID requestId = event.getRequestId();
391 if (event instanceof CanonicalOnset) {
392 CanonicalOnset coEvent = (CanonicalOnset) event;
394 if (requestId == null) {
395 // the requestId should not be 'null'
396 handleNullRequestId(coEvent, params);
400 ControlLoopEventManager manager = onsetToEventManager.computeIfAbsent(coEvent, key -> {
401 // a ControlLoopEventManager does not yet exist for this
402 // 'event' -- create one, with the initial event
404 ControlLoopEventManager mgr = new ControlLoopEventManager(params, coEvent);
405 eventManagers.put(requestId, mgr);
407 } catch (ControlLoopException e) {
408 logger.error("Exception creating ControlLoopEventManager", e);
413 if (manager != null && !manager.getSerialWorkQueue().isRunning()) {
414 // new manager - start it by processing the initial event
415 manager.getSerialWorkQueue().start();
420 if (event instanceof VirtualControlLoopEvent) {
421 ControlLoopEventManager manager = eventManagers.get(requestId);
422 if (manager != null) {
423 manager.getSerialWorkQueue()
424 .queueAndRun(() -> manager.subsequentEvent((VirtualControlLoopEvent) event));
429 // this block of code originally appeared in the 'EVENT.CLEANUP'
431 String ruleName = "EVENT.CLEANUP";
433 logger.info("{}: {}", clName, ruleName);
434 logger.debug("{}: {}: orphan event={}", clName, ruleName, event);
438 * Generate and send a notification message in response to a 'CanonicalOnset'
439 * with a null 'requestId'.
441 * @param event the CanonicalOnset event
442 * @param params the associated ControlLoopParams
444 private void handleNullRequestId(CanonicalOnset event,
445 ControlLoopParams params) {
446 // this block of code originally appeared in the 'EVENT' Drools rule
447 String ruleName = "EVENT";
448 String clName = event.getClosedLoopControlName();
450 VirtualControlLoopNotification notification =
451 new VirtualControlLoopNotification(event);
452 notification.setNotification(ControlLoopNotificationType.REJECTED);
453 notification.setFrom("policy");
454 notification.setMessage("Missing requestId");
455 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
456 notification.setPolicyScope(params.getPolicyScope());
457 notification.setPolicyVersion(params.getPolicyVersion());
460 // Generate notification
463 PolicyEngineConstants.getManager().deliver(POLICY_CL_MGT, notification);
465 } catch (RuntimeException e) {
466 logger.warn("{}: {}.{}: event={} exception generating notification",
467 clName, params.getPolicyName(), ruleName,
472 /* ============================================================ */
475 * This nested class corresponds to a single topic name. At present, the
476 * only topics that are directly handled by this class are
477 * 'ControlLoopEvent', and subclasses (hence, the call to 'processEvent').
478 * If other event types later need to be directly handled, this may need to
479 * become an abstract class, with subclasses for the various event types.
481 private class TopicData implements TopicListener {
485 // set of 'TopicSource' instances associated with this topic
486 // (probably only one, but the underlying APIs support a list)
487 private List<TopicSource> topicSources = null;
490 * Constructor -- initialize the 'TopicData' instance.
492 * @param name the topic name
494 private TopicData(String name) {
499 * Register all of the 'TopicSource' instances associated with this
500 * topic, and start the listeners.
502 private void start() {
503 if (topicSources == null) {
504 // locate topic sources
505 ArrayList<String> topics = new ArrayList<>();
507 topicSources = TopicEndpointManager.getManager().getTopicSources(topics);
510 for (TopicSource consumer : topicSources) {
511 consumer.register(this);
517 * Unregister all of the 'TopicSource' instances associated with this
518 * topic, and stop the listeners.
520 private void stop() {
521 if (topicSources != null) {
522 for (TopicSource consumer : topicSources) {
523 consumer.unregister(this);
529 /*===========================*/
530 /* 'TopicListener' interface */
531 /*===========================*/
534 public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {
535 logger.debug("TopicData.onTopicEvent: {}", event);
536 Object decodedObject =
537 EventProtocolCoderConstants.getManager().decode(groupId, artifactId, topic, event);
538 if (decodedObject != null) {
539 logger.debug("Decoded to object of {}", decodedObject.getClass());
540 if (decodedObject instanceof ControlLoopEvent) {
541 PolicyEngineConstants.getManager().getExecutorService().execute(() ->
542 processEvent((ControlLoopEvent) decodedObject));
548 /* ============================================================ */
551 * This is a 'ControlLoopEventManager2' variant designed to run under
554 private class ControlLoopEventManager extends ControlLoopEventManager2 {
555 private static final long serialVersionUID = 1L;
557 // used to serialize method calls from multiple threads, which avoids the
558 // need for additional synchronization
559 private final SerialWorkQueue serialWorkQueue;
561 private final ControlLoopParams params;
564 private final CanonicalOnset event;
567 * Constructor - initialize a ControlLoopEventManager.
569 * @param params the 'ControlLoopParam's instance associated with the
570 * 'closedLoopControlName'
571 * @param event the initial ControlLoopEvent
573 private ControlLoopEventManager(ControlLoopParams params, CanonicalOnset event)
574 throws ControlLoopException {
576 super(params, event);
577 this.params = params;
579 this.serialWorkQueue = new SerialWorkQueue(this::initialEvent);
583 * Return the SerialWorkQueue.
585 * @return the SerialWorkQueue
587 private SerialWorkQueue getSerialWorkQueue() {
588 return serialWorkQueue;
592 * This is a notification from the base class that a state transition
596 protected void notifyUpdate() {
601 * Process the initial event from DCAE that caused the
602 * 'ControlLoopEventManager' to be created.
604 private void initialEvent() {
605 // this block of code originally appeared in the 'EVENT' Drools rule
606 String ruleName = "EVENT";
607 UUID requestId = event.getRequestId();
608 String clName = event.getClosedLoopControlName();
610 VirtualControlLoopNotification notification;
614 // Check the event, because we need it to not be null when
615 // we create the ControlLoopEventManager. The ControlLoopEventManager
616 // will do extra syntax checking as well as check if the closed loop is disabled.
619 notification = makeNotification();
620 notification.setNotification(ControlLoopNotificationType.ACTIVE);
621 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
622 } catch (Exception e) {
623 logger.warn("{}: {}.{}", clName, params.getPolicyName(), ruleName, e);
624 eventManagers.remove(requestId, this);
625 onsetToEventManager.remove(event, this);
626 notification = new VirtualControlLoopNotification(event);
627 notification.setNotification(ControlLoopNotificationType.REJECTED);
628 notification.setMessage("Exception occurred: " + e.getMessage());
629 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
630 notification.setPolicyScope(params.getPolicyScope());
631 notification.setPolicyVersion(params.getPolicyVersion());
634 // Generate notification
637 PolicyEngineConstants.getManager().deliver(POLICY_CL_MGT, notification);
639 } catch (RuntimeException e) {
640 logger.warn("{}: {}.{}: event={} exception generating notification",
641 clName, params.getPolicyName(), ruleName,
647 * Process a subsequent event from DCAE.
649 * @param event the VirtualControlLoopEvent event
651 private void subsequentEvent(VirtualControlLoopEvent event) {
652 // this block of code originally appeared in the
653 // 'EVENT.MANAGER>NEW.EVENT' Drools rule
654 String ruleName = "EVENT.MANAGER.NEW.EVENT";
657 // Check what kind of event this is
659 switch (onNewEvent(event)) {
662 // Ignore any bad syntax events
664 logger.warn("{}: {}.{}: syntax error",
665 getClosedLoopControlName(), getPolicyName(), ruleName);
668 case FIRST_ABATEMENT:
669 case SUBSEQUENT_ABATEMENT:
671 // TODO: handle the abatement. Currently, it's just discarded.
676 case SUBSEQUENT_ONSET:
679 // We don't care about subsequent onsets
681 logger.warn("{}: {}.{}: subsequent onset",
682 getClosedLoopControlName(), getPolicyName(), ruleName);
688 * Called when a state transition occurs.
690 private void update() {
691 // handle synchronization by running it under the SerialWorkQueue
692 getSerialWorkQueue().queueAndRun(() -> {
702 * Called when a state transition occurs, and we are in the active state.
704 private void updateActive() {
706 // no notification needed
710 // this block of code originally appeared in the
711 // 'EVENT.MANAGER.PROCESSING' Drools rule
712 String ruleName = "EVENT.MANAGER.PROCESSING";
713 VirtualControlLoopNotification notification =
716 logger.info("{}: {}.{}: manager={}",
717 getClosedLoopControlName(), getPolicyName(), ruleName,
720 // Generate notification
723 notification.setPolicyName(getPolicyName() + "." + ruleName);
724 PolicyEngineConstants.getManager().deliver(POLICY_CL_MGT, notification);
726 } catch (RuntimeException e) {
727 logger.warn("{}: {}.{}: manager={} exception generating notification",
728 getClosedLoopControlName(), getPolicyName(), ruleName,
732 // Generate Response notification
735 ControlLoopResponse clResponse = getControlLoopResponse();
736 if (clResponse != null) {
737 PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse);
740 } catch (RuntimeException e) {
741 logger.warn("{}: {}.{}: manager={} exception generating Response notification",
742 getClosedLoopControlName(), getPolicyName(), ruleName,
746 // Discard this message and wait for the next response.
753 * Called when a state transition has occurred, and we are not in the
756 private void updateInactive() {
757 // this block of code originally appeared in the 'EVENT.MANAGER.FINAL'
759 String ruleName = "EVENT.MANAGER.FINAL";
760 VirtualControlLoopNotification notification =
763 logger.info("{}: {}.{}: manager={}",
764 getClosedLoopControlName(), getPolicyName(), ruleName,
767 // Generate notification
770 notification.setPolicyName(getPolicyName() + "." + ruleName);
771 PolicyEngineConstants.getManager().deliver(POLICY_CL_MGT, notification);
772 } catch (RuntimeException e) {
773 logger.warn("{}: {}.{}: manager={} exception generating notification",
774 getClosedLoopControlName(), getPolicyName(), ruleName,
778 // Destroy the manager
782 // Remove the entry from the table
783 eventManagers.remove(getRequestId(), this);
784 onsetToEventManager.remove(event, this);
788 /* ============================================================ */
791 * An instance of this class is called by 'IndexedPolicyControllerFactory'.
792 * It does the build operation when the value of the 'controller.type'
793 * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
795 public static class PolicyBuilder implements PolicyControllerFeatureApi {
797 public int getSequenceNumber() {
802 public PolicyController beforeInstance(String name, Properties properties) {
803 if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
804 return new TdjamController(name, properties);
810 /* ============================================================ */
813 * An instance of this class is called by 'IndexedDroolsControllerFactory'.
814 * It does the build operation when the value of the 'controller.type'
815 * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
817 public static class DroolsBuilder implements DroolsControllerFeatureApi {
819 public int getSequenceNumber() {
824 public DroolsController beforeInstance(Properties properties,
825 String groupId, String artifactId, String version,
826 List<TopicCoderFilterConfiguration> decoderConfigurations,
827 List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError {
829 if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
830 return NonDroolsPolicyController.getBuildInProgress();