org.geppetto.frontend.controllers.WebsocketConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.geppetto.frontend.controllers.WebsocketConnection.java

Source

/*******************************************************************************
 * The MIT License (MIT)
 * 
 * Copyright (c) 2011 - 2015 OpenWorm.
 * http://openworm.org
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the MIT License
 * which accompanies this distribution, and is available at
 * http://opensource.org/licenses/MIT
 *
 * Contributors:
 *        OpenWorm - http://openworm.org/people.html
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights 
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *******************************************************************************/
package org.geppetto.frontend.controllers;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geppetto.core.common.GeppettoExecutionException;
import org.geppetto.core.common.GeppettoInitializationException;
import org.geppetto.core.manager.IGeppettoManager;
import org.geppetto.core.utilities.URLReader;
import org.geppetto.frontend.messages.GeppettoTransportMessage;
import org.geppetto.frontend.messages.InboundMessages;
import org.geppetto.frontend.messages.OutboundMessages;
import org.geppetto.frontend.messaging.DefaultMessageSenderFactory;
import org.geppetto.frontend.messaging.MessageSender;
import org.geppetto.frontend.messaging.MessageSenderEvent;
import org.geppetto.frontend.messaging.MessageSenderListener;
import org.geppetto.simulation.manager.ExperimentRunManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

/**
 * Class used to process Web Socket Connections. Messages sent from the connecting clients, web socket connections, are received in here.
 * 
 * @author matteocantarelli
 *
 */
public class WebsocketConnection extends MessageInbound implements MessageSenderListener {

    private static Log logger = LogFactory.getLog(WebsocketConnection.class);

    private ConnectionHandler connectionHandler;

    private String connectionID;

    protected ApplicationContext applicationContext;

    @Autowired
    private DefaultMessageSenderFactory messageSenderFactory;

    private MessageSender messageSender;

    @Autowired
    private IGeppettoManager geppettoManager;

    public WebsocketConnection() {
        super();
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        // FIXME Matteo: The following line is in the wrong place but any proper place seems to break DataNucleus.
        // By calling getInstance on the ExperimentRunManager we are initializing it since the first time getInstance
        // is called we check the database to see if there are any experiments queued that should be executed.
        // Calling getInstance on ExperimentRunManager triggers this process. Ideally the ExperimentRunManager
        // should be a spring bean with scope singleton but when the different parts of the application were trying to use
        // it autowired the application was hanging without exceptions or error...so this class became a traditional
        // singleton. Ideally the singleton should be initialized as soon as the geppetto server starts, for this reason an
        // ApplicationListenerBean was added. Unfortunately if the ExperimentRunManager is initialized before the bundle is
        // started DataNucleus starts giving problems. In the initializaton of ExperimentRunManager there is a method called
        // loadExperiment which queries the database to fetch the projects. After hours of investigation turns out that if
        // a query is performed before the bundle is started then DataNucleus will subsequently have problems when performing
        // perist opeartions, complaining that classes are not resolved and throwing ClassNotResolvedException. The issue
        // was manifesting itself as soon as something was getting persisted, e.g. calling setActiveExperiment on a project.
        // The downside of this line being here, beside being obviously the wrong place is that the ExperimentRunManager
        // will get initialized only AFTER someone connects, so there has to be at least one connection before geppetto
        // starts running experiments, this is as likely to happen as ugly.
        ExperimentRunManager.getInstance();
        // End of the rant, I hope the above will sound silly and wrong in the future. Matteo
        this.connectionHandler = new ConnectionHandler(this, geppettoManager);
    }

    @Override
    protected void onOpen(WsOutbound outbound) {
        messageSender = messageSenderFactory.getMessageSender(getWsOutbound(), this);
        connectionID = ConnectionsManager.getInstance().addConnection(this);
        sendMessage(null, OutboundMessages.CLIENT_ID, connectionID);
    }

    @Override
    protected void onClose(int status) {
        messageSender.shutdown();
        connectionHandler.closeProject();
    }

    @Override
    protected void onBinaryMessage(ByteBuffer message) throws IOException {
        throw new UnsupportedOperationException("Binary message not supported.");
    }

    /**
     * @param requestID
     * @param type
     * @param message
     */
    public void sendMessage(String requestID, OutboundMessages type, String message) {
        messageSender.sendMessage(requestID, type, message);
    }

    /**
     * @param requestID
     * @param type
     * @param message
     */
    public void sendBinaryMessage(String requestID, Path path) {
        messageSender.sendFile(path);
    }

    /**
     * Receives message(s) from client.
     * 
     * @throws IOException
     */
    @Override
    protected void onTextMessage(CharBuffer message) throws IOException {
        String msg = message.toString();

        Map<String, String> parameters;
        long experimentId = -1;
        long projectId = -1;
        String instancePath = null;

        // de-serialize JSON
        GeppettoTransportMessage gmsg = new Gson().fromJson(msg, GeppettoTransportMessage.class);

        String requestID = gmsg.requestID;

        // switch on message type
        // NOTE: each message handler knows how to interpret the GeppettoMessage data field
        switch (InboundMessages.valueOf(gmsg.type.toUpperCase())) {
        case GEPPETTO_VERSION: {
            connectionHandler.getVersionNumber(requestID);
            break;
        }
        case NEW_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.newExperiment(requestID, projectId);
            break;
        }
        case CLONE_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            projectId = Long.parseLong(parameters.get("projectId"));
            experimentId = Long.parseLong(parameters.get("experimentId"));
            connectionHandler.cloneExperiment(requestID, projectId, experimentId);
            break;
        }
        case LOAD_PROJECT_FROM_URL: {
            connectionHandler.loadProjectFromURL(requestID, gmsg.data);
            messageSender.reset();
            break;
        }
        case LOAD_PROJECT_FROM_ID: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            if (parameters.containsKey("experimentId")) {
                experimentId = Long.parseLong(parameters.get("experimentId"));
            }
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.loadProjectFromId(requestID, projectId, experimentId);
            messageSender.reset();
            break;
        }
        case LOAD_PROJECT_FROM_CONTENT: {
            connectionHandler.loadProjectFromContent(requestID, gmsg.data);
            messageSender.reset();
            break;
        }
        case PERSIST_PROJECT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.persistProject(requestID, projectId);
            break;
        }
        case SAVE_PROJECT_PROPERTIES: {
            ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class);
            connectionHandler.saveProjectProperties(requestID, receivedObject.projectId, receivedObject.properties);
            break;
        }
        case SAVE_EXPERIMENT_PROPERTIES: {
            ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class);
            connectionHandler.saveExperimentProperties(requestID, receivedObject.projectId,
                    receivedObject.experimentId, receivedObject.properties);
            break;
        }
        case LOAD_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.loadExperiment(requestID, experimentId, projectId);
            break;
        }
        case GET_SCRIPT: {
            String urlString = gmsg.data;
            URL url = null;
            try {

                url = URLReader.getURL(urlString);

                connectionHandler.sendScriptData(requestID, url, this);

            } catch (MalformedURLException e) {
                sendMessage(requestID, OutboundMessages.ERROR_READING_SCRIPT, "");
            }
            break;
        }
        case GET_DATA_SOURCE_RESULTS: {
            URL url = null;
            String dataSourceName;
            try {
                parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
                }.getType());
                url = URLReader.getURL(parameters.get("url"));
                dataSourceName = parameters.get("data_source_name");

                connectionHandler.sendDataSourceResults(requestID, dataSourceName, url, this);

            } catch (MalformedURLException e) {
                sendMessage(requestID, OutboundMessages.ERROR_READING_SCRIPT, "");
            }
            break;
        }
        case PLAY_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.playExperiment(requestID, experimentId, projectId);
            break;
        }
        case DELETE_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.deleteExperiment(requestID, experimentId, projectId);
            break;
        }
        case RUN_EXPERIMENT: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            connectionHandler.runExperiment(requestID, experimentId, projectId);
            break;
        }
        case SET_WATCHED_VARIABLES: {
            ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class);
            try {
                connectionHandler.setWatchedVariables(requestID, receivedObject.variables,
                        receivedObject.experimentId, receivedObject.projectId, receivedObject.watch);
            } catch (GeppettoExecutionException e) {
                sendMessage(requestID, OutboundMessages.ERROR_SETTING_WATCHED_VARIABLES, "");
            } catch (GeppettoInitializationException e) {
                sendMessage(requestID, OutboundMessages.ERROR_SETTING_WATCHED_VARIABLES, "");
            }

            break;
        }
        case GET_SUPPORTED_OUTPUTS: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            instancePath = parameters.get("instancePath");
            connectionHandler.getSupportedOuputs(requestID, instancePath, experimentId, projectId);
            break;
        }
        case DOWNLOAD_MODEL: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            instancePath = parameters.get("instancePath");
            String format = parameters.get("format");
            connectionHandler.downloadModel(requestID, instancePath, format, experimentId, projectId);
            break;
        }
        case SET_PARAMETERS: {
            ReceivedObject receivedObject = new Gson().fromJson(gmsg.data, ReceivedObject.class);
            connectionHandler.setParameters(requestID, receivedObject.modelParameters, receivedObject.projectId,
                    receivedObject.experimentId);
            break;
        }
        case LINK_DROPBOX: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            String key = parameters.get("key");
            connectionHandler.linkDropBox(requestID, key);
            break;
        }
        case UNLINK_DROPBOX: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            String key = parameters.get("key");
            connectionHandler.unLinkDropBox(requestID, key);
            break;
        }
        case UPLOAD_MODEL: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            String format = parameters.get("format");
            String aspectPath = parameters.get("aspectPath");
            connectionHandler.uploadModel(aspectPath, projectId, experimentId, format);
            break;
        }
        case UPLOAD_RESULTS: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            String format = parameters.get("format");
            String aspectPath = parameters.get("aspectPath");
            connectionHandler.uploadResults(aspectPath, projectId, experimentId, format);
            break;
        }
        case DOWNLOAD_RESULTS: {
            parameters = new Gson().fromJson(gmsg.data, new TypeToken<HashMap<String, String>>() {
            }.getType());
            experimentId = Long.parseLong(parameters.get("experimentId"));
            projectId = Long.parseLong(parameters.get("projectId"));
            String format = parameters.get("format");
            String aspectPath = parameters.get("aspectPath");
            connectionHandler.downloadResults(requestID, aspectPath, projectId, experimentId, format);
            break;
        }
        case EXPERIMENT_STATUS: {
            connectionHandler.checkExperimentStatus(requestID, gmsg.data);
            break;
        }
        case FETCH_VARIABLE: {
            GeppettoModelAPIParameters receivedObject = new Gson().fromJson(gmsg.data,
                    GeppettoModelAPIParameters.class);
            connectionHandler.fetchVariable(requestID, receivedObject.projectId, receivedObject.dataSourceId,
                    receivedObject.variableId);
            break;
        }
        case RESOLVE_IMPORT_TYPE: {
            GeppettoModelAPIParameters receivedObject = new Gson().fromJson(gmsg.data,
                    GeppettoModelAPIParameters.class);
            connectionHandler.resolveImportType(requestID, receivedObject.projectId, receivedObject.paths);
            break;
        }
        case RUN_QUERY: {
            GeppettoModelAPIParameters receivedObject = new Gson().fromJson(gmsg.data,
                    GeppettoModelAPIParameters.class);
            connectionHandler.runQuery(requestID, receivedObject.projectId, receivedObject.runnableQueries);
            break;
        }
        case RUN_QUERY_COUNT: {
            GeppettoModelAPIParameters receivedObject = new Gson().fromJson(gmsg.data,
                    GeppettoModelAPIParameters.class);
            connectionHandler.runQueryCount(requestID, receivedObject.projectId, receivedObject.runnableQueries);
            break;
        }
        default: {
            // NOTE: no other messages expected for now
        }
        }
    }

    /**
     * @return
     */
    public String getConnectionID() {
        return connectionID;
    }

    /**
     * Handle events from the message sender.
     *
     * If there's an error during message transmission then terminate connection.
     *
     * @param event
     *            event from the message sender.
     */
    @Override
    public void handleMessageSenderEvent(MessageSenderEvent event) {
        if (event.getType().equals(MessageSenderEvent.Type.MESSAGE_SEND_FAILED)) {
            messageSender.shutdown();
            messageSender.removeListener(this);
            ConnectionsManager.getInstance().removeConnection(this);
        }
    }

    class ReceivedObject {
        Long projectId;
        Long experimentId;
        List<String> variables;
        boolean watch;
        Map<String, String> modelParameters;
        Map<String, String> properties;
    }

    class GeppettoModelAPIParameters {
        Long projectId;
        Long experimentId;
        String dataSourceId;
        List<String> paths;
        String variableId;
        List<RunnableQueryParameters> runnableQueries;
    }

    class RunnableQueryParameters {
        String queryId;
        String variableId;
    }

}