org.bimserver.servlets.ServiceRunnerServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.bimserver.servlets.ServiceRunnerServlet.java

Source

package org.bimserver.servlets;

/******************************************************************************
 * Copyright (C) 2009-2019  BIMserver.org
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see {@literal<http://www.gnu.org/licenses/>}.
 *****************************************************************************/

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.bimserver.BimServer;
import org.bimserver.BimserverDatabaseException;
import org.bimserver.bimbots.BimBotsOutput;
import org.bimserver.bimbots.BimBotsServiceInterface;
import org.bimserver.database.BimDatabase;
import org.bimserver.database.DatabaseSession;
import org.bimserver.database.OldQuery;
import org.bimserver.endpoints.EndPoint;
import org.bimserver.models.store.InternalServicePluginConfiguration;
import org.bimserver.models.store.ObjectState;
import org.bimserver.models.store.PluginDescriptor;
import org.bimserver.models.store.StorePackage;
import org.bimserver.models.store.User;
import org.bimserver.models.store.UserSettings;
import org.bimserver.notifications.TopicKey;
import org.bimserver.plugins.services.ServicePlugin;
import org.bimserver.shared.exceptions.UserException;
import org.bimserver.webservices.authorization.AuthenticationException;
import org.bimserver.webservices.authorization.Authorization;
import org.bimserver.webservices.authorization.RunServiceAuthorization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class ServiceRunnerServlet extends SubServlet {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRunnerServlet.class);

    public ServiceRunnerServlet(BimServer bimServer, ServletContext servletContext) {
        super(bimServer, servletContext);
    }

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (request.getRequestURI().endsWith("/servicelist")) {
            processServiceList(request, response);
            return;
        }
        String token = null;
        if (request.getHeader("Authorization") != null) {
            String a = request.getHeader("Authorization");
            if (a.startsWith("Bearer")) {
                token = a.substring(7);
            }
        }
        if (token == null) {
            token = request.getHeader("Token");
        }

        LOGGER.info("Token: " + token);
        if (token == null) {
            response.sendError(403, "Token required");
        }

        String serviceName = request.getHeader("ServiceName");
        if (serviceName == null) {
            serviceName = request.getRequestURI();
            LOGGER.info("Request URI: " + serviceName);
            if (serviceName.startsWith("/services/")) {
                serviceName = serviceName.substring(10);
            }
        }
        if (serviceName == null || serviceName.trim().contentEquals("") || serviceName.trim().contentEquals("/")
                || serviceName.contentEquals("/services") || serviceName.contentEquals("/services/")) {
            // Get it from the token
            try {
                Authorization authorization = Authorization.fromToken(getBimServer().getEncryptionKey(), token);
                LOGGER.info("Authorization: " + authorization);
                if (authorization instanceof RunServiceAuthorization) {
                    RunServiceAuthorization runServiceAuthorization = (RunServiceAuthorization) authorization;
                    serviceName = "" + runServiceAuthorization.getSoid();
                    LOGGER.info("Got SOID from token (" + serviceName + ")");
                }
            } catch (AuthenticationException e) {
                LOGGER.error("", e);
            }
        }
        LOGGER.info("ServiceName: " + serviceName);
        long serviceOid = Long.parseLong(serviceName);

        String inputType = request.getHeader("Input-Type");
        LOGGER.info("Input-Type: " + inputType);

        Set<String> acceptedFlows = new LinkedHashSet<>();
        if (request.getHeader("Accept-Flow") == null) {
            // Default
            acceptedFlows.add("SYNC");
        } else {
            String[] flows = request.getHeader("Accept-Flow").split(",");
            for (String flow : flows) {
                acceptedFlows.add(flow);
            }
        }

        try (DatabaseSession session = getBimServer().getDatabase().createSession()) {
            Authorization authorization = Authorization.fromToken(getBimServer().getEncryptionKey(), token);
            User user = session.get(authorization.getUoid(), OldQuery.getDefault());
            if (user == null) {
                LOGGER.error("Service \"" + serviceName + "\" not found for this user");
                throw new UserException("No user found with uoid " + authorization.getUoid());
            }
            if (user.getState() == ObjectState.DELETED) {
                LOGGER.error("User has been deleted");
                throw new UserException("User has been deleted");
            }
            InternalServicePluginConfiguration foundService = null;
            UserSettings userSettings = user.getUserSettings();
            for (InternalServicePluginConfiguration internalServicePluginConfiguration : userSettings
                    .getServices()) {
                if (internalServicePluginConfiguration.getOid() == serviceOid) {
                    foundService = internalServicePluginConfiguration;
                    break;
                }
            }
            if (foundService == null) {
                LOGGER.info("Service \"" + serviceName + "\" not found for this user");
                throw new ServletException("Service \"" + serviceName + "\" not found for this user");
            }
            PluginDescriptor pluginDescriptor = foundService.getPluginDescriptor();
            ServicePlugin servicePlugin = getBimServer().getPluginManager()
                    .getServicePlugin(pluginDescriptor.getPluginClassName(), true);
            if (servicePlugin instanceof BimBotsServiceInterface) {
                LOGGER.info("Found service " + servicePlugin);
                BimBotsServiceInterface bimBotsServiceInterface = (BimBotsServiceInterface) servicePlugin;
                EndPoint endPoint = getBimServer().getEndPointManager().get(token);
                InputStream inputStream = request.getInputStream();
                String contextId = request.getHeader("Context-Id");
                if (endPoint == null || !acceptedFlows.contains("ASYNC_WS")) {
                    // Don't use a websocket, so synchronously process bimbot
                    BimBotsOutput bimBotsOutput = new BimBotRunner(getBimServer(), inputStream, contextId,
                            inputType, authorization, foundService, bimBotsServiceInterface).runBimBot();

                    response.setContentLength(bimBotsOutput.getData().length);
                    response.setHeader("Output-Type", bimBotsOutput.getSchemaName());
                    response.setHeader("Data-Title", bimBotsOutput.getTitle());
                    response.setHeader("Content-Type", bimBotsOutput.getContentType());
                    response.setHeader("Content-Disposition", bimBotsOutput.getContentDisposition());
                    if (bimBotsOutput.getContextId() != null) {
                        response.setHeader("Context-Id", bimBotsOutput.getContextId());
                    }
                    response.getOutputStream().write(bimBotsOutput.getData());
                } else {
                    TopicKey topicKey = new TopicKey();
                    response.setHeader("Output-Type", "Async");
                    response.setHeader("Topic-Id", "" + topicKey.getId());
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    IOUtils.copy(inputStream, baos);
                    // When storing bimbot runs, a streaming deserialzer is used later in the process, memory usage can be reduced by 
                    // streaming the data, but that would require the http socket to stay open (and the response witheld), which is not an option, hence the copy.

                    // TODO this can only start as soon as the response has been sent
                    getBimServer().getExecutorService()
                            .submit(new BimBotRunner(getBimServer(), new ByteArrayInputStream(baos.toByteArray()),
                                    contextId, inputType, authorization, foundService, bimBotsServiceInterface,
                                    endPoint.getStreamingSocketInterface(), topicKey.getId(),
                                    endPoint.getEndPointId()));
                }
            } else {
                throw new ServletException(
                        "Service \"" + serviceName + "\" does not implement the BimBotsServiceInterface");
            }
        } catch (AuthenticationException e) {
            LOGGER.error("", e);
        } catch (BimserverDatabaseException e) {
            LOGGER.error("", e);
        } catch (UserException e) {
            LOGGER.error("", e);
        }
    }

    private void processServiceList(HttpServletRequest request, HttpServletResponse response) {
        BimDatabase database = getBimServer().getDatabase();
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode result = mapper.createObjectNode();
        ArrayNode array = mapper.createArrayNode();
        ArrayNode capabilities = mapper.createArrayNode();
        capabilities.add("WEBSOCKET");
        result.set("capabilities", capabilities);
        result.set("services", array);
        try (DatabaseSession session = database.createSession()) {
            for (PluginDescriptor pluginDescriptor : session.getAllOfType(
                    StorePackage.eINSTANCE.getPluginDescriptor(), PluginDescriptor.class, OldQuery.getDefault())) {
                if (pluginDescriptor.getPluginInterfaceClassName().equals(ServicePlugin.class.getName())) {
                    ServicePlugin servicePlugin = getBimServer().getPluginManager()
                            .getServicePlugin(pluginDescriptor.getPluginClassName(), true);
                    if (servicePlugin instanceof BimBotsServiceInterface) {
                        try {
                            BimBotsServiceInterface bimBotsServiceInterface = (BimBotsServiceInterface) servicePlugin;

                            ObjectNode descriptorJson = mapper.createObjectNode();
                            descriptorJson.put("id", pluginDescriptor.getOid());
                            descriptorJson.put("name", pluginDescriptor.getName());
                            descriptorJson.put("description", pluginDescriptor.getDescription());
                            descriptorJson.put("provider",
                                    getBimServer().getServerSettingsCache().getServerSettings().getName());
                            descriptorJson.put("providerIcon",
                                    getBimServer().getServerSettingsCache().getServerSettings().getIcon());

                            ArrayNode inputs = mapper.createArrayNode();
                            ArrayNode outputs = mapper.createArrayNode();

                            for (String schemaName : bimBotsServiceInterface.getAvailableInputs()) {
                                inputs.add(schemaName);
                            }
                            for (String schemaName : bimBotsServiceInterface.getAvailableOutputs()) {
                                outputs.add(schemaName);
                            }

                            descriptorJson.set("inputs", inputs);
                            descriptorJson.set("outputs", outputs);

                            ObjectNode oauth = mapper.createObjectNode();
                            String siteAddress = getBimServer().getServerSettingsCache().getServerSettings()
                                    .getSiteAddress();
                            oauth.put("authorizationUrl", siteAddress + "/oauth/authorize");
                            oauth.put("registerUrl", siteAddress + "/oauth/register");
                            oauth.put("tokenUrl", siteAddress + "/oauth/access");

                            descriptorJson.set("oauth", oauth);
                            descriptorJson.put("resourceUrl", siteAddress + "/services");
                            array.add(descriptorJson);
                        } catch (Exception e) {
                            LOGGER.error("", e);
                        }
                    }
                }
            }
            response.setContentType("application/json");
            response.getOutputStream().write(mapper.writeValueAsBytes(result));
        } catch (BimserverDatabaseException e) {
            LOGGER.error("", e);
        } catch (JsonProcessingException e) {
            LOGGER.error("", e);
        } catch (IOException e) {
            LOGGER.error("", e);
        }
    }
}