From c57a267dd9beb361b72e97ff043164ac8407daf4 Mon Sep 17 00:00:00 2001 From: danikov Date: Thu, 16 Feb 2012 00:59:54 +0000 Subject: [PATCH 1/4] import code/upgrade task to support predicate for awaiting completion + status --- .../vcloud/director/v1_5/domain/Task.java | 15 +++- .../domain/TaskInErrorStateException.java | 41 +++++++++++ .../director/v1_5/domain/TaskStatus.java | 73 +++++++++++++++++++ .../v1_5/features/TaskAsyncClient.java | 5 +- .../director/v1_5/features/TaskClient.java | 4 +- .../director/v1_5/predicates/TaskSuccess.java | 70 ++++++++++++++++++ 6 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskInErrorStateException.java create mode 100644 labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskStatus.java create mode 100644 labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/TaskSuccess.java diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Task.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Task.java index dfe569d62a..8c270c8441 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Task.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Task.java @@ -147,6 +147,13 @@ public class Task extends EntityType { this.status = status; return this; } + + /** + * @see Task#getStatus() + */ + public Builder status(TaskStatus status) { + return this.status(status.toString()); + } /** * @see Task#getOperation() @@ -411,8 +418,12 @@ public class Task extends EntityType { *
  • aborted - The task was aborted by an administrative action. * */ - public String getStatus() { - return status; + public TaskStatus getStatus() { + return TaskStatus.fromValue(status); + } + + public void setStatus(TaskStatus status) { + this.setStatus(status.toString()); } public void setStatus(String status) { diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskInErrorStateException.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskInErrorStateException.java new file mode 100644 index 0000000000..5b8fe5a272 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskInErrorStateException.java @@ -0,0 +1,41 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.vcloud.director.v1_5.domain; + +/** + * + * @author Adrian Cole + * + */ +public class TaskInErrorStateException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + private final Task task; + + public TaskInErrorStateException(Task task) { + super("error on task: " + task + " error: " + task.getError()); + this.task = task; + } + + public Task getTask() { + return task; + } + +} diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskStatus.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskStatus.java new file mode 100644 index 0000000000..ebef2f5218 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/TaskStatus.java @@ -0,0 +1,73 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.vcloud.director.v1_5.domain; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * @author Adrian Cole + */ +public enum TaskStatus { + /** + * The task has completed and returned a value indicating success. + */ + SUCCESS, + /** + * The task is running. + */ + RUNNING, + + /** + * The task has been queued for execution. + */ + QUEUED, + /** + * The task has completed and returned a value indicating an error. + */ + ERROR, + /** + * not an official status, temporarily in. + */ + CANCELLED, UNRECOGNIZED; + public String value() { + return name().toLowerCase(); + } + + @Override + public String toString() { + return value(); + } + + public static TaskStatus fromValue(String status) { + if ("CANCELED".equals(status.toUpperCase())) { + // TODO: ecloud hack + status = "CANCELLED"; + } else if ("FAILED".equals(status.toUpperCase())) { + status = "ERROR"; + } else if ("COMPLETED".equals(status.toUpperCase())) { + status = "SUCCESS"; + } + try { + return valueOf(checkNotNull(status, "status").toUpperCase()); + } catch (IllegalArgumentException e) { + return UNRECOGNIZED; + } + } + +} \ No newline at end of file diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskAsyncClient.java index 9eeb7340e7..1c39d7f7ef 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskAsyncClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskAsyncClient.java @@ -29,12 +29,11 @@ import org.jclouds.rest.annotations.EndpointParam; import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.JAXBResponseParser; import org.jclouds.rest.annotations.RequestFilters; -import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; +import org.jclouds.vcloud.director.v1_5.domain.Reference; import org.jclouds.vcloud.director.v1_5.domain.Task; import org.jclouds.vcloud.director.v1_5.domain.TasksList; import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; import org.jclouds.vcloud.director.v1_5.functions.OrgReferenceToTaskListEndpoint; -import org.jclouds.vcloud.director.v1_5.functions.ReferenceToEndpoint; import org.jclouds.vcloud.director.v1_5.functions.ThrowVCloudErrorOn4xx; import com.google.common.util.concurrent.ListenableFuture; @@ -53,7 +52,7 @@ public interface TaskAsyncClient { @Consumes @JAXBResponseParser @ExceptionParser(ThrowVCloudErrorOn4xx.class) - ListenableFuture getTaskList(@EndpointParam(parser = OrgReferenceToTaskListEndpoint.class) ReferenceType orgRef); + ListenableFuture getTaskList(@EndpointParam(parser = OrgReferenceToTaskListEndpoint.class) Reference orgRef); /** * @see TaskClient#getTask(URI) diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskClient.java index 990ad8b613..c3790eb412 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/TaskClient.java @@ -22,7 +22,7 @@ import java.net.URI; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; -import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; +import org.jclouds.vcloud.director.v1_5.domain.Reference; import org.jclouds.vcloud.director.v1_5.domain.Task; import org.jclouds.vcloud.director.v1_5.domain.TasksList; @@ -45,7 +45,7 @@ public interface TaskClient { * @param orgId the unique id for the organization * @return a list of tasks */ - TasksList getTaskList(ReferenceType orgRef); + TasksList getTaskList(Reference orgRef); /** * Retrieves a task. diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/TaskSuccess.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/TaskSuccess.java new file mode 100644 index 0000000000..df0015def3 --- /dev/null +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/predicates/TaskSuccess.java @@ -0,0 +1,70 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.jclouds.vcloud.director.v1_5.predicates; + +import java.net.URI; + +import javax.annotation.Resource; +import javax.inject.Singleton; + +import org.jclouds.logging.Logger; +import org.jclouds.rest.RestContext; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorAsyncClient; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; +import org.jclouds.vcloud.director.v1_5.domain.Task; +import org.jclouds.vcloud.director.v1_5.domain.TaskInErrorStateException; +import org.jclouds.vcloud.director.v1_5.domain.TaskStatus; +import org.jclouds.vcloud.director.v1_5.features.TaskClient; + +import com.google.common.base.Predicate; +import com.google.inject.Inject; + +/** + * + * Tests to see if a task succeeds. + * + * @author Adrian Cole + */ +@Singleton +public class TaskSuccess implements Predicate { + + private final TaskClient taskClient; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public TaskSuccess(RestContext context) { + this.taskClient = context.getApi().getTaskClient(); + } + + public boolean apply(URI taskUri) { + logger.trace("looking for status on task %s", taskUri); + + Task task = taskClient.getTask(taskUri); + // perhaps task isn't available, yet + if (task == null) + return false; + logger.trace("%s: looking for status %s: currently: %s", task, TaskStatus.SUCCESS, task.getStatus()); + if (task.getStatus() == TaskStatus.ERROR) + throw new TaskInErrorStateException(task); + return task.getStatus() == TaskStatus.SUCCESS; + } + +} From f7c35b7e616c755642b3c8ec69a361fdd0a2f762 Mon Sep 17 00:00:00 2001 From: danikov Date: Thu, 16 Feb 2012 01:00:10 +0000 Subject: [PATCH 2/4] strip out proxying --- .../v1_5/features/CatalogClientLiveTest.java | 15 ++--- .../BaseVCloudDirectorClientLiveTest.java | 65 +------------------ 2 files changed, 7 insertions(+), 73 deletions(-) diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java index b34e37ee8a..ba26090415 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/CatalogClientLiveTest.java @@ -18,14 +18,11 @@ */ package org.jclouds.vcloud.director.v1_5.features; -import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.*; -import static org.jclouds.vcloud.director.v1_5.domain.Checks.*; -import static org.testng.Assert.*; +import static org.jclouds.vcloud.director.v1_5.domain.Checks.checkTask; +import static org.testng.Assert.assertFalse; import java.net.URI; -import org.jclouds.vcloud.director.v1_5.domain.Metadata; -import org.jclouds.vcloud.director.v1_5.domain.MetadataEntry; import org.jclouds.vcloud.director.v1_5.domain.OrgList; import org.jclouds.vcloud.director.v1_5.domain.Reference; import org.jclouds.vcloud.director.v1_5.domain.Task; @@ -55,11 +52,11 @@ public class CatalogClientLiveTest extends BaseVCloudDirectorClientLiveTest { @Test(testName = "GET /tasksList/{id}") public void testGetTaskList() { - orgList = getOrgList(); + orgList = context.getApi().getOrgClient().getOrgList(); orgRef = Iterables.getFirst(orgList.getOrgs(), null); // Call the method being tested - taskList = getTaskList(orgRef); + taskList = context.getApi().getTaskClient().getTaskList(orgRef); // NOTE The environment MUST have ... @@ -76,7 +73,7 @@ public class CatalogClientLiveTest extends BaseVCloudDirectorClientLiveTest { taskUri = Iterables.getFirst(taskList.getTasks(), null).getHref(); // Call the method being tested - task = getTask(taskUri); + task = context.getApi().getTaskClient().getTask(taskUri); // Check required elements and attributes checkTask(task); @@ -85,6 +82,6 @@ public class CatalogClientLiveTest extends BaseVCloudDirectorClientLiveTest { @Test(testName = "GET /task/{id}/metadata/", dependsOnMethods = { "testGetTask" }) public void testCancelTask() { // Call the method being tested - cancelTask(taskUri); + context.getApi().getTaskClient().cancelTask(taskUri); } } \ No newline at end of file diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java index 7c882e136d..5bd2cb9c4b 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/internal/BaseVCloudDirectorClientLiveTest.java @@ -18,7 +18,6 @@ */ package org.jclouds.vcloud.director.v1_5.internal; -import java.net.URI; import java.util.Properties; import org.jclouds.compute.BaseVersionedServiceLiveTest; @@ -28,15 +27,6 @@ import org.jclouds.rest.RestContextFactory; import org.jclouds.sshj.config.SshjSshClientModule; import org.jclouds.vcloud.director.v1_5.VCloudDirectorAsyncClient; import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; -import org.jclouds.vcloud.director.v1_5.domain.Metadata; -import org.jclouds.vcloud.director.v1_5.domain.MetadataEntry; -import org.jclouds.vcloud.director.v1_5.domain.Org; -import org.jclouds.vcloud.director.v1_5.domain.OrgList; -import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; -import org.jclouds.vcloud.director.v1_5.domain.Task; -import org.jclouds.vcloud.director.v1_5.domain.TasksList; -import org.jclouds.vcloud.director.v1_5.features.OrgClient; -import org.jclouds.vcloud.director.v1_5.features.TaskClient; import org.testng.annotations.AfterGroups; import org.testng.annotations.BeforeGroups; import org.testng.annotations.Test; @@ -50,7 +40,7 @@ import com.google.inject.Module; * @author Adrian Cole */ @Test(groups = "live") -public class BaseVCloudDirectorClientLiveTest extends BaseVersionedServiceLiveTest implements OrgClient, TaskClient { +public class BaseVCloudDirectorClientLiveTest extends BaseVersionedServiceLiveTest { public BaseVCloudDirectorClientLiveTest() { provider = "vcloud-director"; } @@ -71,57 +61,4 @@ public class BaseVCloudDirectorClientLiveTest extends BaseVersionedServiceLiveTe context.close(); } - - private final OrgClient orgClient = context.getApi().getOrgClient(); - private final TaskClient taskClient = context.getApi().getTaskClient(); - - /* - * Proxying implementations of OrgClient. - */ - - /** @see OrgClient#getOrgList() */ - @Override - public OrgList getOrgList() { - return orgClient.getOrgList(); - } - - /** @see OrgClient#getOrg(ReferenceType) */ - @Override - public Org getOrg(ReferenceType orgRef) { - return orgClient.getOrg(orgRef); - } - - /** @see OrgClient#getOrgMetadata(ReferenceType) */ - @Override - public Metadata getOrgMetadata(ReferenceType orgRef) { - return orgClient.getOrgMetadata(orgRef); - } - - /** @see OrgClient#getOrgMetadataEntry(ReferenceType, String) */ - @Override - public MetadataEntry getOrgMetadataEntry(ReferenceType orgRef, String key) { - return orgClient.getOrgMetadataEntry(orgRef, key); - } - - /* - * Proxying implementations of TaskClient. - */ - - /** @see TaskClient#getTaskList(ReferenceType) */ - @Override - public TasksList getTaskList(ReferenceType orgRef) { - return taskClient.getTaskList(orgRef); - } - - /** @see TaskClient#getTask(URI) */ - @Override - public Task getTask(URI taskUri) { - return taskClient.getTask(taskUri); - } - - /** @see TaskClient#cancelTask(URI */ - @Override - public void cancelTask(URI taskUri) { - taskClient.cancelTask(taskUri); - } } From 639b4202fbbea49b3dbbde0180129fc6f1d64b87 Mon Sep 17 00:00:00 2001 From: danikov Date: Thu, 16 Feb 2012 17:30:17 +0000 Subject: [PATCH 3/4] make check messages more detailed --- .../vcloud/director/v1_5/domain/Checks.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java index f994c1b270..1bf7b70fae 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/domain/Checks.java @@ -18,7 +18,10 @@ */ package org.jclouds.vcloud.director.v1_5.domain; -import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; import java.net.URI; import java.util.Set; @@ -87,7 +90,9 @@ public class Checks { } public static void checkType(String type) { - assertTrue(VCloudDirectorMediaType.ALL.contains(type), "The Type must be a valid media type"); + assertTrue(VCloudDirectorMediaType.ALL.contains(type), + String.format("The Type (%s) must be a valid media type - %s", type, + Iterables.toString(VCloudDirectorMediaType.ALL))); } // NOTE this does not currently check anything @@ -104,7 +109,9 @@ public class Checks { public static void checkLink(Link link) { // Check required fields assertNotNull(link.getRel(), "The Rel attribute of a Link must be set"); - assertTrue(Link.Rel.ALL.contains(link.getRel()), "The Rel attribute of a Link must be one of the allowed list"); + assertTrue(Link.Rel.ALL.contains(link.getRel()), + String.format("The Rel attribute (%s) of a Link must be one of the allowed list - %s", + link.getRel(), Iterables.toString(Link.Rel.ALL))); // Check parent type checkReferenceType(link); @@ -113,7 +120,9 @@ public class Checks { public static void checkTask(Task task) { // Check required fields assertNotNull(task.getStatus(), "The Status attribute of a Task must be set"); - assertTrue(Task.Status.ALL.contains(task.getStatus()), "The Status of a Task must be one of the allowed list"); + assertTrue(Task.Status.ALL.contains(task.getStatus().toString()), + String.format("The Status of a Task (%s) must be one of the allowed list - %s", + task.getStatus().toString(), Iterables.toString(Task.Status.ALL))); // Check optional fields // NOTE operation cannot be checked From 5f8b031c956fef709dac2e9e4be35738964240a1 Mon Sep 17 00:00:00 2001 From: danikov Date: Thu, 16 Feb 2012 17:31:59 +0000 Subject: [PATCH 4/4] add missing acceptable link rels & media types --- .../vcloud/director/v1_5/VCloudDirectorMediaType.java | 6 +++++- .../java/org/jclouds/vcloud/director/v1_5/domain/Link.java | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java index 29bd986cbd..eae847ebd3 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java @@ -68,11 +68,15 @@ public class VCloudDirectorMediaType { public static final String MEDIA = "application/vnd.vmware.vcloud.media+xml"; public static final String OWNER = "application/vnd.vmware.vcloud.owner+xml"; + + public static final String VDC = "application/vnd.vmware.vcloud.vdc+xml"; + + public static final String ADMIN_USER = "application/vnd.vmware.admin.user+xml"; public static final List ALL = Arrays.asList( SESSION, ERROR, ORG_LIST, METADATA, METADATA_ENTRY, METADATA_VALUE, ORG, TASKS_LIST, TASK, ORG_NETWORK, CATALOG, CATALOG_ITEM, CATALOG_ITEMS, CATALOGS_LIST, PROPERTY, - MEDIA, OWNER + MEDIA, OWNER, VDC, ADMIN_USER ); } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Link.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Link.java index 7779d9a929..c56c8ed202 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Link.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/Link.java @@ -47,10 +47,13 @@ public class Link extends ReferenceType { public static final String DOWN = "down"; public static final String EDIT = "edit"; public static final String DELETE = "delete"; + public static final String ADD = "add"; + public static final String REMOVE = "remove"; + public static final String CATALOG_ITEM = "catalogItem"; public static final String TASK_CANCEL = "task:cancel"; public static final List ALL = Arrays.asList( - UP, DOWN, EDIT, DELETE, TASK_CANCEL + UP, DOWN, EDIT, DELETE, ADD, REMOVE, CATALOG_ITEM, TASK_CANCEL ); }