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.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
33 import java.util.Properties;
34 import java.util.UUID;
35 import java.util.concurrent.ConcurrentHashMap;
36 import org.onap.policy.common.endpoints.event.comm.Topic;
37 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
38 import org.onap.policy.common.endpoints.event.comm.TopicListener;
39 import org.onap.policy.common.endpoints.event.comm.TopicSource;
40 import org.onap.policy.controlloop.CanonicalOnset;
41 import org.onap.policy.controlloop.ControlLoopEvent;
42 import org.onap.policy.controlloop.ControlLoopException;
43 import org.onap.policy.controlloop.ControlLoopNotificationType;
44 import org.onap.policy.controlloop.ControlLoopResponse;
45 import org.onap.policy.controlloop.VirtualControlLoopEvent;
46 import org.onap.policy.controlloop.VirtualControlLoopNotification;
47 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
48 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
49 import org.onap.policy.controlloop.utils.ControlLoopUtils;
50 import org.onap.policy.drools.controller.DroolsController;
51 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
52 import org.onap.policy.drools.features.PolicyControllerFeatureApi;
53 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
54 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
55 import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset;
56 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
57 import org.onap.policy.drools.system.PolicyController;
58 import org.onap.policy.drools.system.PolicyEngineConstants;
59 import org.onap.policy.extension.system.NonDroolsPolicyController;
60 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 * This replaces a Drools session with Java code. Although Drools memory
66 * is simulated when running the Junit tests, there is no actual use of
69 public class TdjamController extends NonDroolsPolicyController {
70 private static Logger logger = LoggerFactory.getLogger(TdjamController.class);
72 // the 'controller.type' property is set to this value
73 private static final String TDJAM_CONTROLLER_BUILDER_TAG = "tdjam";
75 // additional data associated with session
76 private final String groupId;
77 private final String artifactId;
79 // top-level tosca policy table (first key = name, second key = version)
80 private final Map<String, Map<String, ToscaPolicy>> toscaPolicies = new HashMap<>();
82 // maps 'controlLoopControlName' to 'ControlLoopParams'
83 private final Map<String, ControlLoopParams> controlLoopParams = new HashMap<>();
85 // maps 'requestId' to 'ControlLoopEventManager'
86 private final Map<UUID, ControlLoopEventManager> eventManagers = new ConcurrentHashMap<>();
88 // maps onset to 'ControlLoopEventManager'
89 private final Map<VirtualControlLoopEvent, ControlLoopEventManager> onsetToEventManager = new ConcurrentHashMap<>();
91 // maps 'topic' to 'TopicData'
92 private final Map<String, TopicData> topicDataTable = new ConcurrentHashMap<>();
94 /* ============================================================ */
97 * Initialize a new 'TdjamController'.
99 * @param name the controller name
100 * @param properties properties defining the controller
102 public TdjamController(String name, Properties properties) {
103 super(name, properties);
105 this.groupId = getGroupId();
106 this.artifactId = getArtifactId();
111 private void init() {
112 // go through all of the incoming message decoders associated
113 // with this controller
114 for (ProtocolCoderToolset pct :
115 EventProtocolCoderConstants.getManager()
116 .getDecoders(groupId, artifactId)) {
117 // go through the 'CoderFilters' instances, and see if there are
118 // any that we are interested in
119 for (CoderFilters cf : pct.getCoders()) {
121 Class<?> clazz = Class.forName(cf.getCodedClass());
122 if (ControlLoopEvent.class.isAssignableFrom(clazz)) {
123 // this one is of interest
124 logger.debug("TdjamController using CoderFilters: {}", cf);
125 getTopicData(pct.getTopic());
127 } catch (ClassNotFoundException e) {
128 logger.error("CoderFilter refers to unknown class: {}",
129 cf.getCodedClass(), e);
134 // start all 'TopicData' instances
135 for (TopicData topicData : topicDataTable.values()) {
141 public <T> boolean offer(T object) {
142 if (object instanceof ToscaPolicy) {
143 addToscaPolicy((ToscaPolicy) object);
150 * Add or replace a ToscaPolicy instance. The policy is keyed by name and
153 * @param toscaPolicy the ToscaPolicy being added
154 * @return if a ToscaPolicy with this name/version previously existed within
155 * this TdjamController, it is returned; otherwise, 'null' is returned.
157 public synchronized ToscaPolicy addToscaPolicy(ToscaPolicy toscaPolicy) {
158 Map<String, ToscaPolicy> level2 =
159 toscaPolicies.computeIfAbsent(toscaPolicy.getName(),
160 key -> new HashMap<String, ToscaPolicy>());
161 ToscaPolicy prev = level2.put(toscaPolicy.getVersion(), toscaPolicy);
163 // update 'ControlLoopParams' entries
164 for (ControlLoopParams clp : controlLoopParams.values()) {
165 if (clp.getToscaPolicy() == prev) {
166 clp.setToscaPolicy(toscaPolicy);
170 logger.debug("ToscaPolicy name={}, version={}, count={}, prev={}",
171 toscaPolicy.getName(), toscaPolicy.getVersion(), toscaPolicies.size(), (prev != null));
174 // attempt to create a 'ControlLoopParams' instance from this object
175 ControlLoopParams params =
176 ControlLoopUtils.toControlLoopParams(toscaPolicy);
177 if (params != null) {
178 addControlLoopParams(params);
184 * Remove a ToscaPolicy instance associated with the specified name and
187 * @param name the name of the ToscaPolicy to remove
188 * @param version the version of the ToscaPolicy to remove
189 * @return the ToscaPolicy that was removed, or 'null' if not found
191 public synchronized ToscaPolicy removeToscaPolicy(String name, String version) {
192 ToscaPolicy prev = null;
193 Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
195 if (level2 != null && (prev = level2.remove(version)) != null) {
196 // remove all 'ControlLoopParams' entries referencing this policy
197 for (ControlLoopParams clp :
198 new ArrayList<>(controlLoopParams.values())) {
199 if (clp.getToscaPolicy() == prev) {
200 controlLoopParams.remove(clp.getClosedLoopControlName());
208 * Fetch a ToscaPolicy instance associated with the specified name and
211 * @param name the name of the ToscaPolicy
212 * @param version the version of the ToscaPolicy
213 * @return the ToscaPolicy, or 'null' if not found
215 public synchronized ToscaPolicy getToscaPolicy(String name, String version) {
216 Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
217 return (level2 == null ? null : level2.get(version));
221 * Return a collection of all ToscaPolicy instances.
223 * @return all ToscaPolicy instances
225 public synchronized Collection<ToscaPolicy> getAllToscaPolicies() {
226 HashSet<ToscaPolicy> rval = new HashSet<>();
227 for (Map<String, ToscaPolicy> map : toscaPolicies.values()) {
228 rval.addAll(map.values());
234 * Add a new 'ControlLoopParams' instance -- they are keyed by
235 * 'closedLoopControlName'.
237 * @param clp the 'ControlLoopParams' instance to add
238 * @return the 'ControlLoopParams' instance previously associated with the
239 * 'closedLoopControlName' ('null' if it didn't exist)
241 public synchronized ControlLoopParams addControlLoopParams(ControlLoopParams clp) {
242 ToscaPolicy toscaPolicy =
243 getToscaPolicy(clp.getPolicyName(), clp.getPolicyVersion());
244 if (toscaPolicy == null) {
245 // there needs to be a 'ToscaPolicy' instance with a matching
247 logger.debug("Missing ToscaPolicy, name={}, version={}",
248 clp.getPolicyName(), clp.getPolicyVersion());
252 clp.setToscaPolicy(toscaPolicy);
253 ControlLoopParams prev =
254 controlLoopParams.put(clp.getClosedLoopControlName(), clp);
256 logger.debug("ControlLoopParams name={}, version={}, closedLoopControlName={}, count={}, prev={}",
257 clp.getPolicyName(), clp.getPolicyVersion(),
258 clp.getClosedLoopControlName(), controlLoopParams.size(), (prev != null));
264 * Return a collection of all ControlLoopParams instances.
266 * @return all ControlLoopParams instances
268 public synchronized Collection<ControlLoopParams> getAllControlLoopParams() {
269 return new ArrayList<>(controlLoopParams.values());
273 * Return a collection of all EventManager instances.
275 * @return all EventManager instances
278 public synchronized Collection<ControlLoopEventManager> getAllEventManagers() {
279 return new ArrayList<>(eventManagers.values());
283 * Return a collection of all onsetToEventManager instances.
285 * @return all onsetToEventManager instances
288 public synchronized Collection<ControlLoopEventManager> getAllOnsetToEventManager() {
289 return new ArrayList<>(onsetToEventManager.values());
293 * Reset the controller.
296 public synchronized void reset() {
297 toscaPolicies.clear();
298 controlLoopParams.clear();
299 eventManagers.clear();
300 onsetToEventManager.clear();
304 public boolean stop() {
307 // stop all 'TopicData' instances
308 for (TopicData topicData : topicDataTable.values()) {
315 * Remove a ControlLoopParams instance associated with the specified
316 * 'closedLoopControlName'.
318 * @param closedLoopControlName the closedLoopControlName identifying the
319 * ControlLoopParams instance
320 * @return the 'ControlLoopParams' instance, 'null' if not found
322 public synchronized ControlLoopParams removeControlLoopParams(String closedLoopControlName) {
323 return controlLoopParams.remove(closedLoopControlName);
327 * Dump out the ToscaPolicy and ControlLoopParams tables in
328 * human-readable form.
330 private void dumpTables() {
331 ByteArrayOutputStream bos = new ByteArrayOutputStream();
332 PrintStream out = new PrintStream(bos, true);
334 // name(25) version(10) closedLoopControlName(...)
336 String format = "%-25s %-10s %s\n";
337 out.println("ToscaPolicy Table");
338 out.format(format, "Name", "Version", "");
339 out.format(format, "----", "-------", "");
341 for (Map<String, ToscaPolicy> level2 : toscaPolicies.values()) {
342 for (ToscaPolicy tp : level2.values()) {
343 out.format(format, tp.getName(), tp.getVersion(), "");
347 out.println("\nControlLoopParams Table");
348 out.format(format, "Name", "Version", "ClosedLoopControlName");
349 out.format(format, "----", "-------", "---------------------");
350 for (ControlLoopParams cp : controlLoopParams.values()) {
351 out.format(format, cp.getPolicyName(), cp.getPolicyVersion(),
352 cp.getClosedLoopControlName());
355 logger.debug(new String(bos.toByteArray()));
359 * Find or create a 'TopicData' instance associated with the specified
362 * @param name the topic name
363 * @return the new or existing 'TopicData' instance associated with 'name'
365 private TopicData getTopicData(String name) {
366 return topicDataTable.computeIfAbsent(name, key -> new TopicData(name));
369 /* ============================================================ */
372 * Process an incoming 'ControlLoopEvent'.
374 * @param event the incoming 'ControlLoopEvent'
376 private void processEvent(ControlLoopEvent event) {
377 String clName = event.getClosedLoopControlName();
378 ControlLoopParams params = controlLoopParams.get(clName);
379 if (params == null) {
380 logger.debug("No ControlLoopParams for event: {}", event);
384 UUID requestId = event.getRequestId();
385 if (event instanceof CanonicalOnset) {
386 CanonicalOnset coEvent = (CanonicalOnset) event;
388 if (requestId == null) {
389 // the requestId should not be 'null'
390 handleNullRequestId(coEvent, params);
394 ControlLoopEventManager manager = onsetToEventManager.computeIfAbsent(coEvent, key -> {
395 // a ControlLoopEventManager does not yet exist for this
396 // 'event' -- create one, with the initial event
398 ControlLoopEventManager mgr = new ControlLoopEventManager(params, coEvent);
399 eventManagers.put(requestId, mgr);
401 } catch (ControlLoopException e) {
402 logger.error("Exception creating ControlLoopEventManager", e);
407 if (manager != null && !manager.getSerialWorkQueue().isRunning()) {
408 // new manager - start it by processing the initial event
409 manager.getSerialWorkQueue().start();
414 if (event instanceof VirtualControlLoopEvent) {
415 ControlLoopEventManager manager = eventManagers.get(requestId);
416 if (manager != null) {
417 manager.getSerialWorkQueue()
418 .queueAndRun(() -> manager.subsequentEvent((VirtualControlLoopEvent) event));
423 // this block of code originally appeared in the 'EVENT.CLEANUP'
425 String ruleName = "EVENT.CLEANUP";
427 logger.info("{}: {}", clName, ruleName);
428 logger.debug("{}: {}: orphan event={}", clName, ruleName, event);
432 * Generate and send a notification message in response to a 'CanonicalOnset'
433 * with a null 'requestId'.
435 * @param event the CanonicalOnset event
436 * @param params the associated ControlLoopParams
438 private void handleNullRequestId(CanonicalOnset event,
439 ControlLoopParams params) {
440 // this block of code originally appeared in the 'EVENT' Drools rule
441 String ruleName = "EVENT";
442 String clName = event.getClosedLoopControlName();
444 VirtualControlLoopNotification notification =
445 new VirtualControlLoopNotification(event);
446 notification.setNotification(ControlLoopNotificationType.REJECTED);
447 notification.setFrom("policy");
448 notification.setMessage("Missing requestId");
449 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
450 notification.setPolicyScope(params.getPolicyScope());
451 notification.setPolicyVersion(params.getPolicyVersion());
454 // Generate notification
457 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
459 } catch (RuntimeException e) {
460 logger.warn("{}: {}.{}: event={} exception generating notification",
461 clName, params.getPolicyName(), ruleName,
466 /* ============================================================ */
469 * This nested class corresponds to a single topic name. At present, the
470 * only topics that are directly handled by this class are
471 * 'ControlLoopEvent', and subclasses (hence, the call to 'processEvent').
472 * If other event types later need to be directly handled, this may need to
473 * become an abstract class, with subclasses for the various event types.
475 private class TopicData implements TopicListener {
479 // set of 'TopicSource' instances associated with this topic
480 // (probably only one, but the underlying APIs support a list)
481 private List<TopicSource> topicSources = null;
484 * Constructor -- initialize the 'TopicData' instance.
486 * @param name the topic name
488 private TopicData(String name) {
493 * Register all of the 'TopicSource' instances associated with this
494 * topic, and start the listeners.
496 private void start() {
497 if (topicSources == null) {
498 // locate topic sources
499 ArrayList<String> topics = new ArrayList<>();
501 topicSources = TopicEndpointManager.getManager().getTopicSources(topics);
504 for (TopicSource consumer : topicSources) {
505 consumer.register(this);
511 * Unregister all of the 'TopicSource' instances associated with this
512 * topic, and stop the listeners.
514 private void stop() {
515 if (topicSources != null) {
516 for (TopicSource consumer : topicSources) {
517 consumer.unregister(this);
523 /*===========================*/
524 /* 'TopicListener' interface */
525 /*===========================*/
528 public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {
529 logger.debug("TopicData.onTopicEvent: {}", event);
530 Object decodedObject =
531 EventProtocolCoderConstants.getManager().decode(groupId, artifactId, topic, event);
532 if (decodedObject != null) {
533 logger.debug("Decoded to object of {}", decodedObject.getClass());
534 if (decodedObject instanceof ControlLoopEvent) {
535 PolicyEngineConstants.getManager().getExecutorService().execute(() ->
536 processEvent((ControlLoopEvent) decodedObject));
542 /* ============================================================ */
545 * This is a 'ControlLoopEventManager2' variant designed to run under
548 private class ControlLoopEventManager extends ControlLoopEventManager2 {
549 private static final long serialVersionUID = 1L;
551 // used to serialize method calls from multiple threads, which avoids the
552 // need for additional synchronization
553 private final SerialWorkQueue serialWorkQueue;
555 private final ControlLoopParams params;
558 private final CanonicalOnset event;
561 * Constructor - initialize a ControlLoopEventManager.
563 * @param params the 'ControlLoopParam's instance associated with the
564 * 'closedLoopControlName'
565 * @param event the initial ControlLoopEvent
567 private ControlLoopEventManager(ControlLoopParams params, CanonicalOnset event)
568 throws ControlLoopException {
570 super(params, event);
571 this.params = params;
573 this.serialWorkQueue = new SerialWorkQueue(this::initialEvent);
577 * Return the SerialWorkQueue.
579 * @return the SerialWorkQueue
581 private SerialWorkQueue getSerialWorkQueue() {
582 return serialWorkQueue;
586 * This is a notification from the base class that a state transition
590 protected void notifyUpdate() {
595 * Process the initial event from DCAE that caused the
596 * 'ControlLoopEventManager' to be created.
598 private void initialEvent() {
599 // this block of code originally appeared in the 'EVENT' Drools rule
600 String ruleName = "EVENT";
601 UUID requestId = event.getRequestId();
602 String clName = event.getClosedLoopControlName();
604 VirtualControlLoopNotification notification;
608 // Check the event, because we need it to not be null when
609 // we create the ControlLoopEventManager. The ControlLoopEventManager
610 // will do extra syntax checking as well as check if the closed loop is disabled.
614 } catch (Exception e) {
615 eventManagers.remove(requestId, this);
616 onsetToEventManager.remove(event, this);
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 notification = new VirtualControlLoopNotification(event);
625 notification.setNotification(ControlLoopNotificationType.REJECTED);
626 notification.setMessage("Exception occurred: " + e.getMessage());
627 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
628 notification.setPolicyScope(params.getPolicyScope());
629 notification.setPolicyVersion(params.getPolicyVersion());
632 // Generate notification
635 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
637 } catch (RuntimeException e) {
638 logger.warn("{}: {}.{}: event={} exception generating notification",
639 clName, params.getPolicyName(), ruleName,
645 * Process a subsequent event from DCAE.
647 * @param event the VirtualControlLoopEvent event
649 private void subsequentEvent(VirtualControlLoopEvent event) {
650 // this block of code originally appeared in the
651 // 'EVENT.MANAGER>NEW.EVENT' Drools rule
652 String ruleName = "EVENT.MANAGER.NEW.EVENT";
655 // Check what kind of event this is
657 switch (onNewEvent(event)) {
660 // Ignore any bad syntax events
662 logger.warn("{}: {}.{}: syntax error",
663 getClosedLoopControlName(), getPolicyName(), ruleName);
666 case FIRST_ABATEMENT:
667 case SUBSEQUENT_ABATEMENT:
669 // TODO: handle the abatement. Currently, it's just discarded.
674 case SUBSEQUENT_ONSET:
677 // We don't care about subsequent onsets
679 logger.warn("{}: {}.{}: subsequent onset",
680 getClosedLoopControlName(), getPolicyName(), ruleName);
686 * Called when a state transition occurs.
688 private void update() {
689 // handle synchronization by running it under the SerialWorkQueue
690 getSerialWorkQueue().queueAndRun(() -> {
700 * Called when a state transition occurs, and we are in the active state.
702 private void updateActive() {
704 // no notification needed
708 // this block of code originally appeared in the
709 // 'EVENT.MANAGER.PROCESSING' Drools rule
710 String ruleName = "EVENT.MANAGER.PROCESSING";
711 VirtualControlLoopNotification notification =
714 logger.info("{}: {}.{}: manager={}",
715 getClosedLoopControlName(), getPolicyName(), ruleName,
718 // Generate notification
721 notification.setPolicyName(getPolicyName() + "." + ruleName);
722 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
724 } catch (RuntimeException e) {
725 logger.warn("{}: {}.{}: manager={} exception generating notification",
726 getClosedLoopControlName(), getPolicyName(), ruleName,
730 // Generate Response notification
733 ControlLoopResponse clResponse = getControlLoopResponse();
734 if (clResponse != null) {
735 PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse);
738 } catch (RuntimeException e) {
739 logger.warn("{}: {}.{}: manager={} exception generating Response notification",
740 getClosedLoopControlName(), getPolicyName(), ruleName,
744 // Discard this message and wait for the next response.
751 * Called when a state transition has occurred, and we are not in the
754 private void updateInactive() {
755 // this block of code originally appeared in the 'EVENT.MANAGER.FINAL'
757 String ruleName = "EVENT.MANAGER.FINAL";
758 VirtualControlLoopNotification notification =
761 logger.info("{}: {}.{}: manager={}",
762 getClosedLoopControlName(), getPolicyName(), ruleName,
765 // Generate notification
768 notification.setPolicyName(getPolicyName() + "." + ruleName);
769 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
770 } catch (RuntimeException e) {
771 logger.warn("{}: {}.{}: manager={} exception generating notification",
772 getClosedLoopControlName(), getPolicyName(), ruleName,
776 // Destroy the manager
780 // Remove the entry from the table
781 eventManagers.remove(getRequestId(), this);
782 onsetToEventManager.remove(event, this);
786 /* ============================================================ */
789 * An instance of this class is called by 'IndexedPolicyControllerFactory'.
790 * It does the build operation when the value of the 'controller.type'
791 * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
793 public static class PolicyBuilder implements PolicyControllerFeatureApi {
795 public int getSequenceNumber() {
800 public PolicyController beforeInstance(String name, Properties properties) {
801 if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
802 return new TdjamController(name, properties);
808 /* ============================================================ */
811 * An instance of this class is called by 'IndexedDroolsControllerFactory'.
812 * It does the build operation when the value of the 'controller.type'
813 * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
815 public static class DroolsBuilder implements DroolsControllerFeatureApi {
817 public int getSequenceNumber() {
822 public DroolsController beforeInstance(Properties properties,
823 String groupId, String artifactId, String version,
824 List<TopicCoderFilterConfiguration> decoderConfigurations,
825 List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError {
827 if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
828 return TdjamController.getBuildInProgress();