docs: @Override
  	public Protos.TaskInfo launch(Protos.SlaveID slaveId, MesosResourceAllocation allocation) {
  
  		ContaineredTaskManagerParameters tmParams = params.containeredParameters();
  
  		final Configuration dynamicProperties = new Configuration();
  
  		// incorporate the dynamic properties set by the template
  		dynamicProperties.addAll(containerSpec.getDynamicConfiguration());
  
  		// build a TaskInfo with assigned resources, environment variables, etc
  		final Protos.TaskInfo.Builder taskInfo = Protos.TaskInfo.newBuilder()
  			.setSlaveId(slaveId)
  			.setTaskId(taskID)
  			.setName(taskID.getValue());
  
  		// take needed resources from the overall allocation, under the assumption of adequate resources
  		Set<String> roles = mesosConfiguration.roles();
  		taskInfo.addAllResources(allocation.takeScalar("cpus", taskRequest.getCPUs(), roles));
  		taskInfo.addAllResources(allocation.takeScalar("gpus", taskRequest.getGPUs(), roles));
  		taskInfo.addAllResources(allocation.takeScalar("mem", taskRequest.getMemory(), roles));
  
  		if (taskRequest.getDisk() > 0.0) {
  			taskInfo.addAllResources(allocation.takeScalar("disk", taskRequest.getDisk(), roles));
  		}
  
  		final Protos.CommandInfo.Builder cmd = taskInfo.getCommandBuilder();
  		final Protos.Environment.Builder env = cmd.getEnvironmentBuilder();
  		final StringBuilder jvmArgs = new StringBuilder();
  
  		//configure task manager hostname property if hostname override property is supplied
  		Option<String> taskManagerHostnameOption = params.getTaskManagerHostname();
  
  		if (taskManagerHostnameOption.isDefined()) {
  			// replace the TASK_ID pattern by the actual task id value of the Mesos task
  			final String taskManagerHostname = MesosTaskManagerParameters.TASK_ID_PATTERN
  				.matcher(taskManagerHostnameOption.get())
  				.replaceAll(Matcher.quoteReplacement(taskID.getValue()));
  
  			dynamicProperties.setString(TaskManagerOptions.HOST, taskManagerHostname);
  		}
  
  		// take needed ports for the TM
  		Set<String> tmPortKeys = extractPortKeys(containerSpec.getDynamicConfiguration());
  		List<Protos.Resource> portResources = allocation.takeRanges("ports", tmPortKeys.size(), roles);
  		taskInfo.addAllResources(portResources);
  		Iterator<String> portsToAssign = tmPortKeys.iterator();
  		rangeValues(portResources).forEach(port -> dynamicProperties.setLong(portsToAssign.next(), port));
  		if (portsToAssign.hasNext()) {
  			throw new IllegalArgumentException("insufficient # of ports assigned");
  		}
  
  		// ship additional files
  		for (ContainerSpecification.Artifact artifact : containerSpec.getArtifacts()) {
  			cmd.addUris(Utils.uri(resolver, artifact));
  		}
  
  		// add user-specified URIs
  		for (String uri : params.uris()) {
  			cmd.addUris(CommandInfo.URI.newBuilder().setValue(uri));
  		}
  
  		// propagate environment variables
  		for (Map.Entry<String, String> entry : params.containeredParameters().taskManagerEnv().entrySet()) {
  			env.addVariables(variable(entry.getKey(), entry.getValue()));
  		}
  		for (Map.Entry<String, String> entry : containerSpec.getEnvironmentVariables().entrySet()) {
  			env.addVariables(variable(entry.getKey(), entry.getValue()));
  		}
  
  		// propagate the Mesos task ID to the TM
  		env.addVariables(variable(MesosConfigKeys.ENV_FLINK_CONTAINER_ID, taskInfo.getTaskId().getValue()));
  
  		// finalize the memory parameters
  		jvmArgs.append(" -Xms").append(tmParams.taskManagerHeapSizeMB()).append("m");
  		jvmArgs.append(" -Xmx").append(tmParams.taskManagerHeapSizeMB()).append("m");
  		if (tmParams.taskManagerDirectMemoryLimitMB() >= 0) {
  			jvmArgs.append(" -XX:MaxDirectMemorySize=").append(tmParams.taskManagerDirectMemoryLimitMB()).append("m");
  		}
  
  		// pass dynamic system properties
  		jvmArgs.append(' ').append(
  			ContainerSpecification.formatSystemProperties(containerSpec.getSystemProperties()));
  
  		// finalize JVM args
  		env.addVariables(variable(MesosConfigKeys.ENV_JVM_ARGS, jvmArgs.toString()));
  
  		// populate TASK_NAME and FRAMEWORK_NAME environment variables to the TM container
  		env.addVariables(variable(MesosConfigKeys.ENV_TASK_NAME, taskInfo.getTaskId().getValue()));
  		env.addVariables(variable(MesosConfigKeys.ENV_FRAMEWORK_NAME, mesosConfiguration.frameworkInfo().getName()));
  
  		// build the launch command w/ dynamic application properties
  		StringBuilder launchCommand = new StringBuilder();
  		if (params.bootstrapCommand().isDefined()) {
  			launchCommand.append(params.bootstrapCommand().get()).append(" && ");
  		}
  		launchCommand
  			.append(params.command())
  			.append(" ")
  			.append(ContainerSpecification.formatSystemProperties(dynamicProperties));
  		cmd.setValue(launchCommand.toString());
  
  		// build the container info
  		Protos.ContainerInfo.Builder containerInfo = Protos.ContainerInfo.newBuilder();
  		// in event that no docker image or mesos image name is specified, we must still
  		// set type to MESOS
  		containerInfo.setType(Protos.ContainerInfo.Type.MESOS);
  		switch (params.containerType()) {
  			case MESOS:
  				if (params.containerImageName().isDefined()) {
  					containerInfo
  						.setMesos(Protos.ContainerInfo.MesosInfo.newBuilder()
  							.setImage(Protos.Image.newBuilder()
  								.setType(Protos.Image.Type.DOCKER)
  								.setDocker(Protos.Image.Docker.newBuilder()
  									.setName(params.containerImageName().get()))));
  				}
  				break;
  
  			case DOCKER:
  				assert(params.containerImageName().isDefined());
  				containerInfo
  					.setType(Protos.ContainerInfo.Type.DOCKER)
  					.setDocker(Protos.ContainerInfo.DockerInfo.newBuilder()
  						.addAllParameters(params.dockerParameters())
  						.setNetwork(Protos.ContainerInfo.DockerInfo.Network.HOST)
  						.setImage(params.containerImageName().get())
  						.setForcePullImage(params.dockerForcePullImage()));
  				break;
  
  			default:
  				throw new IllegalStateException("unsupported container type");
  		}
  
  		// add any volumes to the containerInfo
  		containerInfo.addAllVolumes(params.containerVolumes());
  		taskInfo.setContainer(containerInfo);
  
  		return taskInfo.build();
  	}