src/main/java/io/bosh/client/stemcells/SpringStemcells.java
public class SpringStemcells extends AbstractSpringOperations implements Stemcells {

    public SpringStemcells(RestOperations restOperations, URI root) {
        super(restOperations, root);
    }

    @Override
    public Observable<List<Stemcell>> list() {
        return get(Stemcell[].class, 
                builder -> builder.pathSegment("stemcells"))
               .map(results -> Arrays.asList(results));
    }

}
src/main/java/io/bosh/client/vms/SpringVms.java
public class SpringVms extends AbstractSpringOperations implements Vms {

    private final ObjectMapper mapper = new ObjectMapper();
    private final Tasks tasks;
    
    public SpringVms(RestOperations restOperations, URI root, Tasks tasks) {
        super(restOperations, root);
        this.tasks = tasks;
    }

    @Override
    public Observable<List<VmSummary>> list(String deploymentName) {
        return get(VmSummary[].class,
                builder -> builder.pathSegment("deployments", deploymentName, "vms"))
                .map(results -> Arrays.asList(results));
    }
    
    @Override
    public Observable<List<Vm>> listDetails(String deploymentName) {
        return getEntity(Void.class, builder -> builder.pathSegment("deployments", deploymentName, "vms")
                        .queryParam("format", "full"))
            .flatMap(response -> tasks.trackToCompletion(getTaskId(response)))
            .flatMap(task -> get(String.class, builder -> builder.pathSegment("tasks", task.getId(), "output")
                                                           .queryParam("type", "result"))
            .filter(rawDetails -> rawDetails != null)
            .map(rawDetails -> rawDetails.split("\n"))
            .map(rawDetails -> {
                List<Vm> details = new ArrayList<Vm>();
                for (String vm : rawDetails) {
                    try {
                        details.add(mapper.readValue(vm.getBytes(), Vm.class));
                    } catch (IOException e) {
                        throw new DirectorException("Unable to read VM data into VmDetails: " + vm, e);
                    }
                }
                return details;
             }));
    }

}
src/main/java/io/bosh/client/releases/Releases.java
public interface Releases {

    Observable<List<ReleaseSummary>> list();
    
    Observable<Release> get(String releaseName);
}
src/main/java/io/bosh/client/deployments/Deployments.java
public interface Deployments {

    Observable<List<DeploymentSummary>> list();
    
    Observable<Deployment> get(String deploymentName);
    
    Observable<List<Problem>> cloudcheck(String deploymentName);
}
src/main/java/io/bosh/client/vms/Vms.java
public interface Vms {

    Observable<List<VmSummary>> list(String deploymentName);
    
    Observable<List<Vm>> listDetails(String deploymentName); 
}
src/main/java/io/bosh/client/errands/SpringErrands.java
public class SpringErrands extends AbstractSpringOperations implements Errands {

    public SpringErrands(RestOperations restOperations, URI root) {
        super(restOperations, root);
    }

    @Override
    public Observable<List<ErrandSummary>> list(String deploymentName) {
        return get( ErrandSummary[].class, builder -> builder.pathSegment("deployments", deploymentName, "errands"))
               .map(response -> Arrays.asList(response));
    }
}
src/main/java/io/bosh/client/info/Info.java
public interface Info {

    Observable<DirectorInfo> info();
}
src/main/java/io/bosh/client/tasks/SpringTasks.java
public class SpringTasks extends AbstractSpringOperations implements Tasks {

    private static final int DEFAULT_RECENT_TASK_COUNT = 30;
    private static final int TASK_TRACKING_POLL_INTERVAL = 1000;
    
    public SpringTasks(RestOperations restOperations, URI root) {
        super(restOperations, root);
    }

    @Override
    public Observable<List<Task>> listRunning() {
        return get(Task[].class,
                builder -> builder.pathSegment("tasks")
                                   .queryParam("state", "processing,cancelling,queued"))
               .map(response -> Arrays.asList(response));
    }

    @Override
    public Observable<List<Task>> listRecent() {
        return listRecent(DEFAULT_RECENT_TASK_COUNT);
    }

    @Override
    public Observable<List<Task>> listRecent(int count) {
        return get(Task[].class,
                builder -> builder.pathSegment("tasks")
                                   .queryParam("limit", count)
                                   .queryParam("verbose", 1))
               .map(response -> Arrays.asList(response));
    }

    @Override
    public Observable<Task> get(String id) {
        return get(Task.class, builder -> builder.pathSegment("tasks", id));
    }

    @Override
    public Observable<Task> trackToCompletion(String id) {
        return Observable.interval(TASK_TRACKING_POLL_INTERVAL, TimeUnit.MILLISECONDS, Schedulers.io())
                .flatMap(tick -> get(id))
                .skipWhile(task -> task.isInProgress()) // TODO consider condition for max tries/timeout
                .first();
    }

}
src/main/java/io/bosh/client/tasks/Tasks.java
public interface Tasks {

    Observable<List<Task>> listRunning();
    
    Observable<List<Task>> listRecent();

    Observable<List<Task>> listRecent(int count);
    
    Observable<Task> get(String id);
    
    Observable<Task> trackToCompletion(String id);
}
src/main/java/io/bosh/client/deployments/SpringDeployments.java
public class SpringDeployments extends AbstractSpringOperations implements Deployments {

    private final Tasks tasks;
    
    public SpringDeployments(RestTemplate restTemplate, URI root, Tasks tasks) {
        super(restTemplate, root);
        this.tasks = tasks;
    }

    @Override
    public Observable<List<DeploymentSummary>> list() {
        return get(DeploymentSummary[].class, 
                builder -> builder.pathSegment("deployments"))
               .map(results -> Arrays.asList(results));
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Override
    public Observable<Deployment> get(String deploymentName) {
        return get(Deployment.class, 
                   builder -> builder.pathSegment("deployments", deploymentName))
               .map(response -> {
                   response.setName(deploymentName);

                   ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
                   Map manifestMap = null;
                   try {
                       manifestMap = mapper.readValue(response.getRawManifest(), Map.class);
                   } catch (IOException e) {
                       throw new DirectorException("Unable to parse deployment manifest", e);
                   }
                   response.setManifestMap(manifestMap);
                   return response;
               });
    }

    @Override
    public Observable<List<Problem>> cloudcheck(String deploymentName) {
        return postForEntity(Void.class, null, builder -> builder.pathSegment("deployments", deploymentName, "scans"))
                .flatMap(response -> tasks.trackToCompletion(getTaskId(response)))
                .flatMap(task -> get(Problem[].class, builder -> builder.pathSegment("deployments", deploymentName, "problems")))
                .map(problems -> Arrays.asList(problems));
    }

}
src/main/java/io/bosh/client/info/SpringInfo.java
public class SpringInfo extends AbstractSpringOperations implements Info {

    public SpringInfo(RestOperations restOperations, URI root) {
        super(restOperations, root);
    }

    @Override
    public Observable<DirectorInfo> info() {
        return get(DirectorInfo.class, 
                builder -> builder.pathSegment("info"));
    }

}
src/main/java/io/bosh/client/jobs/Jobs.java
public interface Jobs {

    Observable<InputStream> fetchLogs(FetchLogsRequest request);

    /**
     * Check {@link Task#getState()} and {@link Task#getResult()} for the
     * outcome of this operation. This operation may timeout after a defined interval based on server-side
     * logic in the BOSH Director.
     * 
     * @param request
     * @return the BOSH {@link Task} to 
     */
    Observable<Task> stopJob(StopJobRequest request);
    
    Observable<Task> startJob(StartJobRequest request);
    
    Observable<Task> restartJob(RestartJobRequest request);
    
    Observable<Task> recreateJob(RecreateJobRequest request);
}
src/main/java/io/bosh/client/releases/SpringReleases.java
public class SpringReleases extends AbstractSpringOperations implements Releases {

    public SpringReleases(RestTemplate restTemplate, URI root) {
        super(restTemplate, root);
    }

    @Override
    public Observable<List<ReleaseSummary>> list() {
        return get(ReleaseSummary[].class, 
                builder -> builder.pathSegment("releases"))
              .map(results -> Arrays.asList(results));
    }

    @Override
    public Observable<Release> get(String releaseName) {
        return get(Release.class,
                builder -> builder.pathSegment("releases", releaseName))
               .map(response -> {
                   response.setName(releaseName);
                   return response;
               });
    }
}
src/main/java/io/bosh/client/jobs/SpringJobs.java
public class SpringJobs extends AbstractSpringOperations implements Jobs{

    private final Tasks tasks;
    private final Deployments deployments;
    
    public SpringJobs(RestOperations restOperations, URI root, Tasks tasks, Deployments deployments) {
        super(restOperations, root);
        this.tasks = tasks;
        this.deployments = deployments;
    }

    @Override
    public Observable<InputStream> fetchLogs(FetchLogsRequest request) {
        return getEntity(Void.class, builder -> 
                    builder.pathSegment("deployments", request.getDeploymentName())
                           .pathSegment("jobs", request.getJobName(), String.valueOf(request.getJobIndex()))
                           .pathSegment("logs")
                           .queryParam("type", request.getLogType().getType())
                           .queryParam("filters", String.join(",", request.getFilters())))
               .flatMap(response -> tasks.trackToCompletion(getTaskId(response)))
               .flatMap(task -> getGzip(builder -> builder.pathSegment("resources", task.getResult())))
               .map(file -> createInputStream(file));
    }


    private final Observable<File> getGzip(Consumer<UriComponentsBuilder> builderCallback) {
        // For responses that have a Content-Type of application/x-gzip, we need to
        // decompress them. The RestTemplate and HttpClient don't handle this for
        // us
        return createObservable(() -> {
            UriComponentsBuilder builder = UriComponentsBuilder.fromUri(this.root);
            builderCallback.accept(builder);
            URI uri = builder.build().toUri();
            return this.restOperations.execute(uri, HttpMethod.GET, null, new ResponseExtractor<File>() {
                @Override
                public File extractData(ClientHttpResponse response) throws IOException {
                    return decompress(response.getBody());
                }
            });
        });
    }
    
    private InputStream createInputStream(File file) {
        try {
            return new FileInputStream(file);
        } catch (FileNotFoundException e) {
            throw new DirectorException("Unable to create InputStream from file", e);
        }
    }

    private File decompress(InputStream compressed) {
        File file = null;
        try {
            file = File.createTempFile(UUID.randomUUID().toString(), ".log");
        } catch (IOException e) {
            throw new DirectorException("Unable to create temporary file to decompress log data", e);
        }
        try (InputStream ungzippedResponse = new GZIPInputStream(compressed);
                FileWriter writer = new FileWriter(file);
                Reader reader = new InputStreamReader(ungzippedResponse, "UTF-8")) {
            char[] buffer = new char[10240];
            for (int length = 0; (length = reader.read(buffer)) > 0;) {
                writer.write(buffer, 0, length);
            }
            return file;
        } catch (IOException e) {
            throw new DirectorException("Unable to decompress log data", e);
        }
    }
    
    private Observable<Task> changeJobState(String deploymentName, Consumer<UriComponentsBuilder> builderCallback) {
        HttpHeaders headers = new  HttpHeaders();
        headers.put("content-type",Arrays.asList("text/yaml"));
        
        return deployments.get(deploymentName)
        .flatMap(deployment -> exchangeForEntity(deployment.getRawManifest(), Void.class, headers, HttpMethod.PUT, 
                                builder -> builderCallback.accept(builder)))
        .flatMap(response -> tasks.trackToCompletion(getTaskId(response)));
    }
    
    private void buildChangeJobStateUri(String deployment, String job, Integer index, String newState, boolean skipDrain, UriComponentsBuilder builder) {
        builder.pathSegment("deployments", deployment, "jobs", job);
        if(index != null){
            builder.pathSegment(String.valueOf(index));
        }
        builder.queryParam("state", newState);
        if(skipDrain){
            builder.queryParam("skip_drain", "true");
        }
    }

    @Override
    public Observable<Task> stopJob(StopJobRequest request) {
        return changeJobState(request.getDeploymentName(), builder -> {
            String newState = "stopped";
            if(request.isPowerOffVm()){
                newState = "detached";
            }
            buildChangeJobStateUri(request.getDeploymentName(), request.getJobName(), request.getJobIndex(), newState, request.isSkipDrain(), builder);
        });
    }
    
    @Override
    public Observable<Task> startJob(StartJobRequest request) {
        return changeJobState(request.getDeploymentName(), builder -> {
            buildChangeJobStateUri(request.getDeploymentName(), request.getJobName(), request.getJobIndex(), "started", false, builder);
        });
    }

    @Override
    public Observable<Task> restartJob(RestartJobRequest request) {
        return changeJobState(request.getDeploymentName(), builder -> {
           buildChangeJobStateUri(request.getDeploymentName(), request.getJobName(), request.getJobIndex(), "restart", false, builder);
        });
    }

    @Override
    public Observable<Task> recreateJob(RecreateJobRequest request) {
        return changeJobState(request.getDeploymentName(), builder -> {
            buildChangeJobStateUri(request.getDeploymentName(), request.getJobName(), request.getJobIndex(), "recreate", false, builder);
         });
    }
}
src/main/java/io/bosh/client/errands/Errands.java
public interface Errands {

    Observable<List<ErrandSummary>> list(String deploymentName);
}
src/main/java/io/bosh/client/stemcells/Stemcells.java
public interface Stemcells {

    Observable<List<Stemcell>> list();
}


Question: import io.bosh.client.vms.Vms;
import java.net.URI;
import org.springframework.web.client.RestTemplate;
import io.bosh.client.deployments.Deployments;
import io.bosh.client.deployments.SpringDeployments;
import io.bosh.client.errands.Errands;
import io.bosh.client.errands.SpringErrands;
import io.bosh.client.info.Info;
import io.bosh.client.info.SpringInfo;
import io.bosh.client.jobs.Jobs;
import io.bosh.client.jobs.SpringJobs;
import io.bosh.client.releases.Releases;
import io.bosh.client.releases.SpringReleases;
import io.bosh.client.stemcells.SpringStemcells;
import io.bosh.client.stemcells.Stemcells;
import io.bosh.client.tasks.SpringTasks;
import io.bosh.client.tasks.Tasks;
import io.bosh.client.vms.SpringVms;
/*
 * Copyright 2015 the original author or authors.
 *
 * 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.bosh.client;




/**
 * @author David Ehringer
 */
public class SpringDirectorClient implements DirectorClient {
    
    private final RestTemplate restTemplate;

    private final Info info;
    private final Releases releases;
    private final Stemcells stemcells;
    private final Deployments deployments;
    private final Jobs jobs;
    private final Vms vms;
    private final Errands errands;
    private final Tasks tasks;

    SpringDirectorClient(URI root, RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        this.info = new SpringInfo(restTemplate, root);
        this.releases = new SpringReleases(restTemplate, root);
        this.stemcells = new SpringStemcells(restTemplate, root);
        this.errands = new SpringErrands(restTemplate, root);
        this.tasks = new SpringTasks(restTemplate, root);
Answer:         this.deployments = new SpringDeployments(restTemplate, root, tasks);