io.bosh.client.jobs.SpringJobs.java Source code

Java tutorial

Introduction

Here is the source code for io.bosh.client.jobs.SpringJobs.java

Source

/*
 * 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.jobs;

import io.bosh.client.DirectorException;
import io.bosh.client.deployments.Deployments;
import io.bosh.client.internal.AbstractSpringOperations;
import io.bosh.client.tasks.Task;
import io.bosh.client.tasks.Tasks;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.util.Arrays;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.zip.GZIPInputStream;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestOperations;
import org.springframework.web.util.UriComponentsBuilder;

import rx.Observable;

/**
 * @author David Ehringer
 */
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);
        });
    }
}