Implementing TaskDefinition delete.  Ensuring that task definitions cannot be added, changed, or deleted in an active product.
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java b/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
index 3814efe..35a7701 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/client/PortfolioManager.java
@@ -138,6 +138,7 @@
           produces = MediaType.APPLICATION_JSON_VALUE,
           consumes = MediaType.APPLICATION_JSON_VALUE
   )
+  @ThrowsException(status = HttpStatus.CONFLICT, exception = ProductInUseException.class)
   void createTaskDefinition(
           @PathVariable("productidentifier") final String productIdentifier,
           final TaskDefinition taskDefinition);
@@ -153,16 +154,25 @@
           @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier);
 
   @RequestMapping(
-          value = "/products/{productidentifier}/tasks/{taskdefinitionidentifier}",
-          method = RequestMethod.PUT,
-          produces = MediaType.ALL_VALUE,
-          consumes = MediaType.APPLICATION_JSON_VALUE
-  )
+      value = "/products/{productidentifier}/tasks/{taskdefinitionidentifier}",
+      method = RequestMethod.PUT,
+      produces = MediaType.ALL_VALUE,
+      consumes = MediaType.APPLICATION_JSON_VALUE)
+  @ThrowsException(status = HttpStatus.CONFLICT, exception = ProductInUseException.class)
   void changeTaskDefinition(
-          @PathVariable("productidentifier") final String productIdentifier,
-          @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier,
-          final TaskDefinition taskDefinition);
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier,
+      final TaskDefinition taskDefinition);
 
+  @RequestMapping(
+      value = "/products/{productidentifier}/tasks/{taskdefinitionidentifier}",
+      method = RequestMethod.DELETE,
+      produces = MediaType.ALL_VALUE,
+      consumes = MediaType.APPLICATION_JSON_VALUE)
+  @ThrowsException(status = HttpStatus.CONFLICT, exception = ProductInUseException.class)
+  void deleteTaskDefinition(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier);
 
   @RequestMapping(
           value = "/products/{productidentifier}/charges/",
diff --git a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
index 87ba261..777e819 100644
--- a/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
+++ b/api/src/main/java/io/mifos/portfolio/api/v1/events/EventConstants.java
@@ -31,6 +31,7 @@
   String PUT_CASE = "put-case";
   String POST_TASK_DEFINITION = "post-task-definition";
   String PUT_TASK_DEFINITION = "put-task-definition";
+  String DELETE_TASK_DEFINITION = "delete-task-definition";
   String POST_CHARGE_DEFINITION = "post-charge-definition";
   String PUT_CHARGE_DEFINITION = "put-charge-definition";
   String DELETE_PRODUCT_CHARGE_DEFINITION = "delete-product-charge-definition";
@@ -43,6 +44,7 @@
   String SELECTOR_PUT_CASE = SELECTOR_NAME + " = '" + PUT_CASE + "'";
   String SELECTOR_POST_TASK_DEFINITION = SELECTOR_NAME + " = '" + POST_TASK_DEFINITION + "'";
   String SELECTOR_PUT_TASK_DEFINITION = SELECTOR_NAME + " = '" + PUT_TASK_DEFINITION + "'";
+  String SELECTOR_DELETE_TASK_DEFINITION = SELECTOR_NAME + " = '" + DELETE_TASK_DEFINITION + "'";
   String SELECTOR_POST_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + POST_CHARGE_DEFINITION + "'";
   String SELECTOR_PUT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + PUT_CHARGE_DEFINITION + "'";
   String SELECTOR_DELETE_PRODUCT_CHARGE_DEFINITION = SELECTOR_NAME + " = '" + DELETE_PRODUCT_CHARGE_DEFINITION + "'";
diff --git a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
index bf96465..efca4f2 100644
--- a/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
+++ b/component-test/src/main/java/io/mifos/portfolio/AbstractPortfolioTest.java
@@ -30,6 +30,7 @@
 import io.mifos.portfolio.api.v1.events.CaseEvent;
 import io.mifos.portfolio.api.v1.events.ChargeDefinitionEvent;
 import io.mifos.portfolio.api.v1.events.EventConstants;
+import io.mifos.portfolio.api.v1.events.TaskDefinitionEvent;
 import io.mifos.portfolio.service.config.PortfolioServiceConfiguration;
 import io.mifos.portfolio.service.internal.util.AccountingAdapter;
 import io.mifos.portfolio.service.internal.util.RhythmAdapter;
@@ -163,8 +164,7 @@
 
   Product createAndEnableProduct() throws InterruptedException {
     final Product product = createAdjustedProduct(x -> {});
-    portfolioManager.enableProduct(product.getIdentifier(), true);
-    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_PRODUCT_ENABLE, product.getIdentifier()));
+    enableProduct(product);
     return product;
   }
 
@@ -256,4 +256,27 @@
     return entryAccountAssignment;
   }
 
+  TaskDefinition createTaskDefinition(Product product) throws InterruptedException {
+    final TaskDefinition taskDefinition = getTaskDefinition();
+    portfolioManager.createTaskDefinition(product.getIdentifier(), taskDefinition);
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.POST_TASK_DEFINITION, new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
+    return taskDefinition;
+  }
+
+  TaskDefinition getTaskDefinition() {
+    final TaskDefinition ret = new TaskDefinition();
+    ret.setIdentifier(Fixture.generateUniqueIdentifer("task"));
+    ret.setDescription("But how do you feel about this?");
+    ret.setName("feep");
+    ret.setMandatory(false);
+    ret.setActions(new HashSet<>());
+    ret.setFourEyes(true);
+    return ret;
+  }
+
+  void enableProduct(final Product product) throws InterruptedException {
+    portfolioManager.enableProduct(product.getIdentifier(), true);
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_PRODUCT_ENABLE, product.getIdentifier()));
+  }
+
 }
diff --git a/component-test/src/main/java/io/mifos/portfolio/TestTaskDefinitions.java b/component-test/src/main/java/io/mifos/portfolio/TestTaskDefinitions.java
index 0472a75..25438b6 100644
--- a/component-test/src/main/java/io/mifos/portfolio/TestTaskDefinitions.java
+++ b/component-test/src/main/java/io/mifos/portfolio/TestTaskDefinitions.java
@@ -15,6 +15,8 @@
  */
 package io.mifos.portfolio;
 
+import io.mifos.core.api.util.NotFoundException;
+import io.mifos.portfolio.api.v1.client.ProductInUseException;
 import io.mifos.portfolio.api.v1.domain.Product;
 import io.mifos.portfolio.api.v1.domain.TaskDefinition;
 import io.mifos.portfolio.api.v1.events.EventConstants;
@@ -22,7 +24,6 @@
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.util.HashSet;
 import java.util.List;
 
 /**
@@ -52,12 +53,29 @@
 
     portfolioManager.changeTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier(), taskDefinition);
     Assert.assertTrue(this.eventRecorder.wait(EventConstants.PUT_TASK_DEFINITION,
-            new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
+        new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
 
     final TaskDefinition taskDefinitionRead = portfolioManager.getTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier());
     Assert.assertEquals(taskDefinition,taskDefinitionRead);
   }
 
+  @Test(expected = ProductInUseException.class)
+  public void shouldNotChangeTaskDefinitionForProductWithCases() throws InterruptedException {
+    final Product product = createProduct();
+    final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+    enableProduct(product);
+
+    createCase(product.getIdentifier());
+
+    taskDefinition.setDescription("bleblablub");
+    taskDefinition.setFourEyes(false);
+
+    portfolioManager.changeTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier(), taskDefinition);
+    Assert.assertFalse(this.eventRecorder.wait(EventConstants.PUT_TASK_DEFINITION,
+        new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
+  }
+
   @Test
   public void shouldAddTaskDefinition() throws InterruptedException {
     final Product product = createProduct();
@@ -71,21 +89,41 @@
     Assert.assertTrue(tasks.size() == initialTaskCount + 1);
   }
 
-  private TaskDefinition createTaskDefinition(Product product) throws InterruptedException {
+  @Test(expected = ProductInUseException.class)
+  public void shouldNotCreateTaskDefinitionForProductWithCases() throws InterruptedException {
+    final Product product = createAndEnableProduct();
+    createCase(product.getIdentifier());
+
     final TaskDefinition taskDefinition = getTaskDefinition();
     portfolioManager.createTaskDefinition(product.getIdentifier(), taskDefinition);
-    Assert.assertTrue(this.eventRecorder.wait(EventConstants.POST_TASK_DEFINITION, new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
-    return taskDefinition;
+    Assert.assertFalse(this.eventRecorder.wait(EventConstants.POST_TASK_DEFINITION, new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
   }
 
-  private TaskDefinition getTaskDefinition() {
-    final TaskDefinition ret = new TaskDefinition();
-    ret.setIdentifier(Fixture.generateUniqueIdentifer("task"));
-    ret.setDescription("But how do you feel about this?");
-    ret.setName("feep");
-    ret.setMandatory(false);
-    ret.setActions(new HashSet<>());
-    ret.setFourEyes(true);
-    return ret;
+  @Test
+  public void shouldDeleteTaskDefinition() throws InterruptedException {
+    final Product product = createProduct();
+    final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+    portfolioManager.deleteTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier());
+    Assert.assertTrue(this.eventRecorder.wait(EventConstants.DELETE_TASK_DEFINITION, new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
+
+    try {
+      portfolioManager.getTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier());
+      Assert.fail();
+    }
+    catch (final NotFoundException ignored) {
+    }
+  }
+
+  @Test(expected = ProductInUseException.class)
+  public void shouldNotDeleteTaskDefinitionForProductWithCases() throws InterruptedException {
+    final Product product = createProduct();
+    final TaskDefinition taskDefinition = createTaskDefinition(product);
+
+    enableProduct(product);
+    createCase(product.getIdentifier());
+
+    portfolioManager.deleteTaskDefinition(product.getIdentifier(), taskDefinition.getIdentifier());
+    Assert.assertFalse(this.eventRecorder.wait(EventConstants.DELETE_TASK_DEFINITION, new TaskDefinitionEvent(product.getIdentifier(), taskDefinition.getIdentifier())));
   }
 }
\ No newline at end of file
diff --git a/component-test/src/main/java/io/mifos/portfolio/listener/TaskDefinitionEventListener.java b/component-test/src/main/java/io/mifos/portfolio/listener/TaskDefinitionEventListener.java
index 6865b99..adaf189 100644
--- a/component-test/src/main/java/io/mifos/portfolio/listener/TaskDefinitionEventListener.java
+++ b/component-test/src/main/java/io/mifos/portfolio/listener/TaskDefinitionEventListener.java
@@ -50,12 +50,22 @@
   }
 
   @JmsListener(
-          subscription = EventConstants.DESTINATION,
-          destination = EventConstants.DESTINATION,
-          selector = EventConstants.SELECTOR_PUT_TASK_DEFINITION
+      subscription = EventConstants.DESTINATION,
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_PUT_TASK_DEFINITION
   )
   public void onChangeTaskDefinition(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
-                              final String payload) {
+                                     final String payload) {
     this.eventRecorder.event(tenant, EventConstants.PUT_TASK_DEFINITION, payload, TaskDefinitionEvent.class);
   }
+
+  @JmsListener(
+      subscription = EventConstants.DESTINATION,
+      destination = EventConstants.DESTINATION,
+      selector = EventConstants.SELECTOR_DELETE_TASK_DEFINITION
+  )
+  public void onDeleteTaskDefinition(@Header(TenantHeaderFilter.TENANT_HEADER) final String tenant,
+                                     final String payload) {
+    this.eventRecorder.event(tenant, EventConstants.DELETE_TASK_DEFINITION, payload, TaskDefinitionEvent.class);
+  }
 }
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteTaskDefinitionCommand.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteTaskDefinitionCommand.java
new file mode 100644
index 0000000..8fdba76
--- /dev/null
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/DeleteTaskDefinitionCommand.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 The Mifos Initiative.
+ *
+ * Licensed 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 io.mifos.portfolio.service.internal.command;
+
+/**
+ * @author Myrle Krantz
+ */
+public class DeleteTaskDefinitionCommand {
+  private final String productIdentifier;
+  private final String taskIdentifier;
+
+  public DeleteTaskDefinitionCommand(final String productIdentifier, final String taskIdentifier) {
+    this.productIdentifier = productIdentifier;
+    this.taskIdentifier = taskIdentifier;
+  }
+
+  public String getProductIdentifier() {
+    return productIdentifier;
+  }
+
+  public String getTaskIdentifier() {
+    return taskIdentifier;
+  }
+
+  @Override
+  public String toString() {
+    return "DeleteTaskDefinitionCommand{" +
+        "productIdentifier='" + productIdentifier + '\'' +
+        ", taskIdentifier='" + taskIdentifier + '\'' +
+        '}';
+  }
+}
diff --git a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskDefinitionCommandHandler.java b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskDefinitionCommandHandler.java
index 039ba68..6f672d2 100644
--- a/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskDefinitionCommandHandler.java
+++ b/service/src/main/java/io/mifos/portfolio/service/internal/command/handler/TaskDefinitionCommandHandler.java
@@ -21,6 +21,7 @@
 import io.mifos.portfolio.api.v1.events.TaskDefinitionEvent;
 import io.mifos.portfolio.service.internal.command.ChangeTaskDefinitionCommand;
 import io.mifos.portfolio.service.internal.command.CreateTaskDefinitionCommand;
+import io.mifos.portfolio.service.internal.command.DeleteTaskDefinitionCommand;
 import io.mifos.portfolio.service.internal.mapper.TaskDefinitionMapper;
 import io.mifos.portfolio.service.internal.repository.ProductEntity;
 import io.mifos.portfolio.service.internal.repository.ProductRepository;
@@ -75,18 +76,33 @@
     final String productIdentifier = changeTaskDefinitionCommand.getProductIdentifier();
 
     final TaskDefinitionEntity existingTaskDefinition
-            = taskDefinitionRepository.findByProductIdAndTaskIdentifier(productIdentifier, taskDefinition.getIdentifier())
-            .orElseThrow(() -> ServiceException.internalError("task definition not found."));
+        = taskDefinitionRepository.findByProductIdAndTaskIdentifier(productIdentifier, taskDefinition.getIdentifier())
+        .orElseThrow(() -> ServiceException.internalError("task definition not found."));
 
     final TaskDefinitionEntity taskDefinitionEntity =
-            TaskDefinitionMapper.map(existingTaskDefinition.getProduct(), taskDefinition);
+        TaskDefinitionMapper.map(existingTaskDefinition.getProduct(), taskDefinition);
     taskDefinitionEntity.setId(existingTaskDefinition.getId());
     taskDefinitionEntity.setId(existingTaskDefinition.getId());
     taskDefinitionRepository.save(taskDefinitionEntity);
 
     return new TaskDefinitionEvent(
-            changeTaskDefinitionCommand.getProductIdentifier(),
-            changeTaskDefinitionCommand.getInstance().getIdentifier());
+        changeTaskDefinitionCommand.getProductIdentifier(),
+        changeTaskDefinitionCommand.getInstance().getIdentifier());
+  }
+
+  @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+  @EventEmitter(selectorName = EventConstants.SELECTOR_NAME, selectorValue = EventConstants.DELETE_TASK_DEFINITION)
+  public TaskDefinitionEvent process(final DeleteTaskDefinitionCommand deleteTaskDefinitionCommand) {
+    final String productIdentifier = deleteTaskDefinitionCommand.getProductIdentifier();
+    final String taskIdentifier = deleteTaskDefinitionCommand.getTaskIdentifier();
+
+    final TaskDefinitionEntity existingTaskDefinition
+        = taskDefinitionRepository.findByProductIdAndTaskIdentifier(productIdentifier, taskIdentifier)
+        .orElseThrow(() -> ServiceException.notFound("Task definition ''{0}.{1}'' not found.", productIdentifier, taskIdentifier));
+
+    taskDefinitionRepository.delete(existingTaskDefinition);
+
+    return new TaskDefinitionEvent(productIdentifier, taskIdentifier);
   }
 
 }
diff --git a/service/src/main/java/io/mifos/portfolio/service/rest/TaskDefinitionRestController.java b/service/src/main/java/io/mifos/portfolio/service/rest/TaskDefinitionRestController.java
index c524cbc..18c5439 100644
--- a/service/src/main/java/io/mifos/portfolio/service/rest/TaskDefinitionRestController.java
+++ b/service/src/main/java/io/mifos/portfolio/service/rest/TaskDefinitionRestController.java
@@ -17,14 +17,16 @@
 
 import io.mifos.anubis.annotation.AcceptedTokenType;
 import io.mifos.anubis.annotation.Permittable;
+import io.mifos.core.command.gateway.CommandGateway;
+import io.mifos.core.lang.ServiceException;
 import io.mifos.portfolio.api.v1.PermittableGroupIds;
 import io.mifos.portfolio.api.v1.domain.TaskDefinition;
 import io.mifos.portfolio.service.internal.command.ChangeTaskDefinitionCommand;
 import io.mifos.portfolio.service.internal.command.CreateTaskDefinitionCommand;
+import io.mifos.portfolio.service.internal.command.DeleteTaskDefinitionCommand;
+import io.mifos.portfolio.service.internal.service.CaseService;
 import io.mifos.portfolio.service.internal.service.ProductService;
 import io.mifos.portfolio.service.internal.service.TaskDefinitionService;
-import io.mifos.core.command.gateway.CommandGateway;
-import io.mifos.core.lang.ServiceException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
@@ -44,17 +46,20 @@
   private final CommandGateway commandGateway;
   private final TaskDefinitionService taskDefinitionService;
   private final ProductService productService;
+  private final CaseService caseService;
 
   @Autowired
   public TaskDefinitionRestController(
-          final CommandGateway commandGateway,
-          final TaskDefinitionService taskDefinitionService,
-          final ProductService productService)
+      final CommandGateway commandGateway,
+      final TaskDefinitionService taskDefinitionService,
+      final ProductService productService,
+      final CaseService caseService)
   {
     super();
     this.commandGateway = commandGateway;
     this.taskDefinitionService = taskDefinitionService;
     this.productService = productService;
+    this.caseService = caseService;
   }
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
@@ -73,9 +78,9 @@
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
   @RequestMapping(
-          method = RequestMethod.POST,
-          consumes = MediaType.APPLICATION_JSON_VALUE,
-          produces = MediaType.APPLICATION_JSON_VALUE
+      method = RequestMethod.POST,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
   )
   public @ResponseBody
   ResponseEntity<Void> createTaskDefinition(
@@ -84,6 +89,8 @@
   {
     checkProductExists(productIdentifier);
 
+    checkProductChangeable(productIdentifier);
+
     taskDefinitionService.findByIdentifier(productIdentifier, instance.getIdentifier())
             .ifPresent(taskDefinition -> {throw ServiceException.conflict("Duplicate identifier: " + taskDefinition.getIdentifier());});
 
@@ -110,17 +117,19 @@
 
   @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
   @RequestMapping(
-          value = "{taskdefinitionidentifier}",
-          method = RequestMethod.PUT,
-          consumes = MediaType.APPLICATION_JSON_VALUE,
-          produces = MediaType.ALL_VALUE
+      value = "{taskdefinitionidentifier}",
+      method = RequestMethod.PUT,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
   )
-  public ResponseEntity<Void> changeTaskDefinition(
+  public @ResponseBody ResponseEntity<Void> changeTaskDefinition(
           @PathVariable("productidentifier") final String productIdentifier,
           @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier,
           @RequestBody @Valid final TaskDefinition instance)
   {
-    checkProductExists(productIdentifier);
+    checkTaskDefinitionExists(productIdentifier, taskDefinitionIdentifier);
+
+    checkProductChangeable(productIdentifier);
 
     if (!taskDefinitionIdentifier.equals(instance.getIdentifier()))
       throw ServiceException.badRequest("Instance identifiers may not be changed.");
@@ -130,8 +139,40 @@
     return ResponseEntity.accepted().build();
   }
 
-  private void checkProductExists(@PathVariable("productidentifier") String productIdentifier) {
+  @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.PRODUCT_MANAGEMENT)
+  @RequestMapping(
+      value = "/{taskdefinitionidentifier}",
+      method = RequestMethod.DELETE,
+      consumes = MediaType.ALL_VALUE,
+      produces = MediaType.APPLICATION_JSON_VALUE
+  )
+  public @ResponseBody ResponseEntity<Void> deleteTaskDefinition(
+      @PathVariable("productidentifier") final String productIdentifier,
+      @PathVariable("taskdefinitionidentifier") final String taskDefinitionIdentifier
+  )
+  {
+    checkTaskDefinitionExists(productIdentifier, taskDefinitionIdentifier);
+
+    checkProductChangeable(productIdentifier);
+
+    commandGateway.process(new DeleteTaskDefinitionCommand(productIdentifier, taskDefinitionIdentifier));
+
+    return ResponseEntity.accepted().build();
+  }
+
+  private void checkTaskDefinitionExists(final String productIdentifier,
+                                         final String taskDefinitionIdentifier) {
+    taskDefinitionService.findByIdentifier(productIdentifier, taskDefinitionIdentifier)
+        .orElseThrow(() -> ServiceException.notFound("No task with the identifier ''{0}.{1}'' exists.", productIdentifier, taskDefinitionIdentifier));
+  }
+
+  private void checkProductExists(final String productIdentifier) {
     productService.findByIdentifier(productIdentifier)
             .orElseThrow(() -> ServiceException.notFound("Invalid product referenced."));
   }
+
+  private void checkProductChangeable(final String productIdentifier) {
+    if (caseService.existsByProductIdentifier(productIdentifier))
+      throw ServiceException.conflict("Cases exist for product with the identifier ''{0}''. Product cannot be changed.", productIdentifier);
+  }
 }
\ No newline at end of file