org.locationtech.geogig.spring.service.LegacyConsoleService.java Source code

Java tutorial

Introduction

Here is the source code for org.locationtech.geogig.spring.service.LegacyConsoleService.java

Source

/* Copyright (c) 2017 Boundless and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/edl-v10.html
 * 
 * Contributors:
 * Johnathan Garrett (Prominent Edge) - initial implementation
 */
package org.locationtech.geogig.spring.service;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;

import org.locationtech.geogig.cli.ArgumentTokenizer;
import org.locationtech.geogig.cli.Console;
import org.locationtech.geogig.cli.GeogigCLI;
import org.locationtech.geogig.porcelain.ConfigGet;
import org.locationtech.geogig.repository.Context;
import org.locationtech.geogig.repository.Platform;
import org.locationtech.geogig.repository.Repository;
import org.locationtech.geogig.repository.impl.GeoGIG;
import org.locationtech.geogig.rest.repository.RepositoryProvider;
import org.locationtech.geogig.spring.dto.ConsoleRunCommandResponse;
import org.locationtech.geogig.spring.dto.ConsoleRunCommandResponse.ConsoleError;
import org.springframework.stereotype.Service;

import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.io.CharSource;
import com.google.common.io.FileBackedOutputStream;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

/**
 *
 */
@Service("legacyConsoleService")
public class LegacyConsoleService extends AbstractRepositoryService {

    public ConsoleRunCommandResponse runCommand(RepositoryProvider provider, String repoName, InputStream input) {
        final Repository repository = getRepository(provider, repoName);
        final Reader body = new InputStreamReader(input);
        final JsonParser parser = new JsonParser();
        final JsonElement jsonElement = parser.parse(body);
        Preconditions.checkArgument(jsonElement.isJsonObject(), "Json body must be supplied.");
        final JsonObject json = jsonElement.getAsJsonObject();
        Preconditions.checkArgument("2.0".equals(json.get("jsonrpc").getAsString()));
        ConsoleRunCommandResponse response;
        if (!checkConsoleEnabled(repository.context())) {
            response = serviceDisabled(json);
        } else {
            response = processRequest(json, repository);
        }
        return response;
    }

    private ConsoleRunCommandResponse processRequest(JsonObject json, final Repository repo) {
        final String command = json.get("method").getAsString();
        final String queryId = json.get("id").getAsString();
        // not used, we're getting the whole command and args in the "method" object
        // JsonArray paramsArray = json.get("params").getAsJsonArray();

        InputStream in = new ByteArrayInputStream(new byte[0]);
        // dumps output to a temp file if > threshold
        FileBackedOutputStream out = new FileBackedOutputStream(4096);
        try {
            // pass it a BufferedOutputStream 'cause it doesn't buffer the internal FileOutputStream
            Console console = new Console(in, new BufferedOutputStream(out)).disableAnsi();
            Platform platform = repo.platform();

            GeoGIG geogig = new GeoGIG(repo);
            GeogigCLI geogigCLI = new GeogigCLI(geogig, console);
            geogigCLI.setPlatform(platform);
            geogigCLI.disableProgressListener();

            String[] args = ArgumentTokenizer.tokenize(command);
            final int exitCode = geogigCLI.execute(args);

            final int charCountLimit = getOutputLimit(repo.context());
            final StringBuilder output = getLimitedOutput(out, charCountLimit);
            String result = null;
            ConsoleError error = null;

            if (exitCode == 0) {
                result = output.toString();
            } else {
                Exception exception = geogigCLI.exception;
                error = buildError(exitCode, output, exception);
            }
            return new ConsoleRunCommandResponse(queryId, result, error);
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // delete temp file
            try {
                out.reset();
            } catch (IOException ignore) {
                ignore.printStackTrace();
            }
        }
    }

    private ConsoleRunCommandResponse serviceDisabled(JsonObject request) {
        final String queryId = request.get("id").getAsString();

        String message = "Web-console service is disabled. Run 'geogig config web.console.enabled true' on a real terminal to enable it.";
        ConsoleError error = new ConsoleError(-1, message);

        return new ConsoleRunCommandResponse(queryId, null, error);
    }

    private boolean checkConsoleEnabled(Context ctx) {
        Optional<String> configOption = ctx.command(ConfigGet.class).setName("web.console.enabled").call();

        boolean enabled = configOption.isPresent() && Boolean.parseBoolean(configOption.get());
        return enabled;
    }

    private int getOutputLimit(Context ctx) {
        final int defaultLimit = 1024 * 16;

        Optional<String> configuredLimit = ctx.command(ConfigGet.class).setName("web.console.limit").call();
        int limit = defaultLimit;
        if (configuredLimit.isPresent()) {
            try {
                limit = Integer.parseInt(configuredLimit.get());
            } catch (NumberFormatException ignore) {
                //
                limit = defaultLimit;
            }
            if (limit < 1024) {
                limit = 1024;
            }
        }
        return limit;
    }

    private StringBuilder getLimitedOutput(FileBackedOutputStream out, final int limit) throws IOException {

        CharSource charSource = out.asByteSource().asCharSource(Charsets.UTF_8);
        BufferedReader reader = charSource.openBufferedStream();
        final StringBuilder output = new StringBuilder();
        int count = 0;
        String line;
        while ((line = reader.readLine()) != null) {
            output.append(line).append('\n');
            count += line.length();
            if (count >= limit) {
                output.append("\nNote: output limited to ").append(count)
                        .append(" characters. Run config web.console.limit <newlimit> to change the current ")
                        .append(limit).append(" soft limit.");
                break;
            }
        }
        return output;
    }

    private ConsoleError buildError(final int exitCode, final StringBuilder output, Exception exception) {

        if (output.length() == 0 && exception != null && exception.getMessage() != null) {
            output.append(exception.getMessage());
        }
        String message = output.toString();
        return new ConsoleError(Integer.valueOf(exitCode), message);
    }
}