org.shaf.server.controller.CmdActionController.java Source code

Java tutorial

Introduction

Here is the source code for org.shaf.server.controller.CmdActionController.java

Source

/**
 * Copyright 2014-2015 SHAF-WORK
 * 
 * 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 org.shaf.server.controller;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Map;

import org.shaf.core.content.DescriptionContent;
import org.shaf.core.content.ErrorContent;
import org.shaf.core.content.ProgressContent;
import org.shaf.core.content.ProtocolContent;
import org.shaf.core.content.ReceiptContent;
import org.shaf.core.content.StructuredContent;
import org.shaf.core.content.UnstructuredContent;
import org.shaf.core.io.storage.StorageDriver;
import org.shaf.core.io.storage.StorageType;
import org.shaf.core.net.NetworkContentException;
import org.shaf.core.net.NetworkTransformer;
import org.shaf.core.security.Firewall;
import org.shaf.core.security.Role;
import org.shaf.core.util.ClassUtils;
import org.shaf.core.util.Log;
import org.shaf.core.util.TextMatrix;
import org.shaf.core.util.UriUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.google.common.io.ByteStreams;

/**
 * The controller for processing actions sending from the command-line shell.
 * 
 * @author Mykola Galushka
 */
@Controller
@RequestMapping("/cmd")
public class CmdActionController extends GenericActionController {

    /**
     * Defines a logger.
     */
    private static final Log LOG = Log.forClass(CmdActionController.class);

    /**
     * Tests if the specified role is available for the established connection.
     * 
     * @param name
     *            the role name to check.
     * @return {@code true} if the role is avaliable and {@code false}
     *         otherwise.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/role/{name}", method = RequestMethod.GET)
    public @ResponseBody String onRole(@PathVariable String name) throws Exception {
        LOG.debug("CALL: /cmd/role/{" + name + "}");

        long start = 0, finish = 0;
        try {
            boolean flag = false;

            start = System.currentTimeMillis();
            Role role = Role.findRole(name);
            if (role != null) {
                flag = super.getFirewall().isAllowed(role);
            }
            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(new UnstructuredContent(finish - start, String.valueOf(flag)));
        } catch (Exception exc) {
            String message = "Failed to obtain asscess status for '" + name + "' role.";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    /**
     * Returns the {@link StructuredContent structured content} containing
     * information about specified category.
     * 
     * @param category
     *            the listing category.
     * @return the XML with structured content.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/list/{category}", method = RequestMethod.GET)
    public @ResponseBody String onList(@PathVariable String category) throws Exception {
        LOG.debug("CALL: /cmd/list/{" + category + "}");

        Firewall firewall = super.getFirewall();
        String username = super.getUserName();

        long start = 0, finish = 0;
        try {
            TextMatrix info = null;

            start = System.currentTimeMillis();
            if ("applications".equals(category)) {
                firewall.allow(Role.ADMIN);
                info = OPER.getApplications();
            } else if ("processes".equals(category)) {
                firewall.allow(Role.ADMIN, Role.USER);
                info = OPER.getProcesses("all");
            } else if ("jobs".equals(category)) {
                firewall.allow(Role.USER);
                info = OPER.getJobs();
            } else if ("uploads".equals(category)) {
                firewall.allow(Role.PROVIDER);
                info = OPER.getStorages(firewall, username, StorageType.PROVIDER.getName(), "all");
            } else if ("downloads".equals(category)) {
                firewall.allow(Role.CONSUMER);
                info = OPER.getStorages(firewall, username, StorageType.CONSUMER.getName(), "all");
            }

            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(new StructuredContent(finish - start, info));
        } catch (Exception exc) {
            String message = "Failed to list '" + category + "' category.";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    /**
     * Returns the process {@link DescriptionContent description} for the
     * specified process alias.
     * 
     * @param alias
     *            the process alias.
     * @return the process description.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/descr/{alias}", method = RequestMethod.GET, produces = "text/xml")
    public @ResponseBody String onDescription(@PathVariable String alias) throws Exception {
        LOG.debug("CALL service: /descr/" + alias);

        long start = 0, finish = 0;
        try {
            super.getFirewall().allow(Role.ADMIN, Role.USER);

            start = System.currentTimeMillis();
            DescriptionContent content = OPER.getProcessDescr(alias);
            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(content);
        } catch (Exception exc) {
            String message = "Failed to obtain the '" + alias + "' process description.";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    /**
     * Submits the specified process for execution and returns launching
     * {@link ReceiptContent receipt}.
     * 
     * @param alias
     *            the process alias.
     * @param args
     *            the process arguments.
     * @return the receipt about successful submit operation.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/submit/{alias}/{arguments}", method = RequestMethod.GET, produces = "text/xml")
    public @ResponseBody String onSubmit(@PathVariable String alias, @PathVariable String arguments)
            throws Exception {
        LOG.debug("CALL service: /submit/run/{" + alias + "}/{" + UriUtils.decodeArguments(arguments) + "}");

        long start = 0, finish = 0;
        try {
            super.getFirewall().allow(Role.USER);

            start = System.currentTimeMillis();
            ReceiptContent content = new ReceiptContent(
                    OPER.launchProcess(alias, UriUtils.decodeArguments(arguments)));
            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(content);
        } catch (Exception exc) {
            String message = "Failed to submit '" + alias + "' process for execution.";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    /**
     * Forces to forget the specified job.
     * 
     * @param id
     *            the job ID.
     * @return the receipt about successful forget operation.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/outcome/{id}", method = RequestMethod.GET, produces = "text/xml")
    public @ResponseBody String onOutcome(@PathVariable String id) throws Exception {
        LOG.debug("CALL service: /cmd/outcome/{" + id + "}");

        long start = 0, finish = 0;
        try {
            super.getFirewall().allow(Role.USER);

            start = System.currentTimeMillis();
            ProtocolContent content = null;

            if (OPER.isJobAvailable(id)) {
                /*
                 * Gets the job execution time.
                 */
                long time = OPER.getJobExecutionTime(id);

                /*
                 * Gets the current gob outcome.
                 */
                if (!OPER.isJobActive(id)) {

                    Object outcome = OPER.getJobOutcome(id);
                    if (outcome == null) {
                        /*
                         * The job is completed without any outcome. In this
                         * case we prepare just a simple message to indicate
                         * that this job is done.
                         */
                        content = new UnstructuredContent(time, "done");
                    } else if (outcome instanceof Exception) {
                        /*
                         * If the job execution is failed, the outcome receives
                         * an exception. The following code constructs the
                         * error-content, which includes information about
                         * occurred exception.
                         */
                        content = new ErrorContent(time, outcome.toString());
                    } else if (ClassUtils.isCollection(outcome)) {
                        /*
                         * If the job outcome is Collection, we assume, that
                         * this is a structured data, which can be converted
                         * into the table (with only one column). It allows to
                         * prepared the structured content by the following
                         * code.
                         */
                        content = new StructuredContent(time, TextMatrix.create((Collection<?>) outcome));
                    } else if (ClassUtils.isMap(outcome)) {
                        /*
                         * If the job outcome is Map, we assume, that this is a
                         * structured data, which can be converted into the
                         * table (with only two columns - first for keys and
                         * second for values). It allows to prepared the
                         * structured content by the following code.
                         */
                        content = new StructuredContent(time, TextMatrix.create((Map<?, ?>) outcome));
                    } else if (outcome instanceof TextMatrix) {
                        /*
                         * If the job outcome is Table, we assume, that this is
                         * a structured data (containing certain amount columns
                         * and rows). It allows to prepared the structured
                         * content by the following code.
                         */
                        content = new StructuredContent(time, (TextMatrix) outcome);
                    } else {
                        /*
                         * For anything else the unstructured content is
                         * created.
                         */
                        content = new UnstructuredContent(time, outcome.toString());
                    }
                } else {
                    /*
                     * If the job still running it returns the progress content.
                     */
                    content = new ProgressContent(time, OPER.getJobProgress(id));
                }
            }
            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(content);

        } catch (Exception exc) {
            String message = "Failed to obtain the process outcome from job #" + id + ".";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    /**
     * Forces to forget the specified job.
     * 
     * @param id
     *            the job ID.
     * @return the receipt about successful forget operation.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/forget/{id}", method = RequestMethod.GET, produces = "text/xml")
    public @ResponseBody String onForget(@PathVariable String id) throws Exception {
        LOG.debug("CALL service: /cmd/forget/{" + id + "}");

        long start = 0, finish = 0;
        try {
            super.getFirewall().allow(Role.USER);

            start = System.currentTimeMillis();
            OPER.removeJob(id);
            finish = System.currentTimeMillis();

            return NetworkTransformer.getTransport(new ReceiptContent(id));
        } catch (Exception exc) {
            String message = "Failed to forget job #" + id + ".";

            LOG.fatal(message, exc);
            return NetworkTransformer.getTransport(new ErrorContent(finish - start, message));
        }
    }

    // ========================================================================
    // Provider Functions
    // ========================================================================

    /**
     * Puts data to the server.
     * 
     * @param storage
     *            the storage type.
     * @param alias
     *            the data alias.
     * @param data
     *            the data to upload.
     * @throws NetworkContentException
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/upload/{storage}/{alias}", method = RequestMethod.POST)
    public void onUpload(@PathVariable String storage, @PathVariable String alias, @RequestBody MultipartFile data)
            throws Exception {
        LOG.debug("CALL service: /cmd/upload/{" + storage + "}/{" + alias + "} with attached data.");
        LOG.trace("The attached data: " + data);

        if (data.isEmpty()) {
            LOG.warn("There no providing data.");
        } else {
            Firewall firewall = super.getFirewall();
            String username = super.getUserName();
            StorageDriver driver = OPER.getStorageDriver(firewall, username, StorageType.PROVIDER, storage);

            LOG.trace("Driver for uploading: " + driver);
            LOG.trace("Uploading data name: " + data.getName());
            LOG.trace("Uploading data size: " + data.getSize());

            try (InputStream in = data.getInputStream(); OutputStream out = driver.getOutputStream(alias)) {
                ByteStreams.copy(in, out);
            } catch (IOException exc) {
                throw exc;
            }
        }
    }

    // ========================================================================
    // Consumer Functions
    // ========================================================================

    /**
     * Gets data from the server.
     * 
     * @param storage
     *            the resource type.
     * @param alias
     *            the data alias.
     * @return the entity for downloading data.
     * @throws Exception
     *             if an error occurs.
     */
    @RequestMapping(value = "/download/{storage}/{alias}", method = RequestMethod.GET)
    public @ResponseBody ResponseEntity<InputStreamResource> onDownload(@PathVariable String storage,
            @PathVariable String alias) throws Exception {
        LOG.debug("CALL service: /cmd/download/{" + storage + "}/{" + alias + "}");

        Firewall firewall = super.getFirewall();
        String username = super.getUserName();
        StorageDriver driver = OPER.getStorageDriver(firewall, username, StorageType.CONSUMER, storage);

        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        header.setContentLength(driver.getLength(alias));
        header.setContentDispositionFormData("attachment", alias);

        return new ResponseEntity<InputStreamResource>(new InputStreamResource(driver.getInputStream(alias)),
                header, HttpStatus.OK);
    }
}