org.pentaho.di.www.ge.GraphEditorWebSocketTest.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.di.www.ge.GraphEditorWebSocketTest.java

Source

/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * 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.pentaho.di.www.ge;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.apache.html.dom.HTMLDocumentImpl;
import org.atmosphere.cpr.AtmosphereServlet;
import org.atmosphere.wasync.ClientFactory;
import org.atmosphere.wasync.Decoder;
import org.atmosphere.wasync.Encoder;
import org.atmosphere.wasync.Event;
import org.atmosphere.wasync.Function;
import org.atmosphere.wasync.Request;
import org.atmosphere.wasync.RequestBuilder;
import org.atmosphere.wasync.Socket;
import org.atmosphere.wasync.impl.AtmosphereClient;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
import org.cyberneko.html.parsers.DOMFragmentParser;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.security.Realm;
import org.eclipse.jetty.client.security.SimpleRealmResolver;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.AbstractBuffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.IO;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.row.value.ValueMetaBase;
import org.pentaho.di.core.xml.XMLHandler;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransConfiguration;
import org.pentaho.di.trans.TransExecutionConfiguration;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.TransPreviewFactory;
import org.pentaho.di.trans.steps.rowgenerator.RowGeneratorMeta;
import org.pentaho.di.www.AddTransServlet;
import org.pentaho.di.www.CarteSingleton;
import org.pentaho.di.www.GetRootServlet;
import org.pentaho.di.www.GetStatusServlet;
import org.pentaho.di.www.JobMap;
import org.pentaho.di.www.PauseTransServlet;
import org.pentaho.di.www.SlaveServerStatus;
import org.pentaho.di.www.SlaveServerTransStatus;
import org.pentaho.di.www.SocketRepository;
import org.pentaho.di.www.StartTransServlet;
import org.pentaho.di.www.TransformationMap;
import org.pentaho.di.www.WebResult;
import org.pentaho.di.www.ge.websocket.message.GEBaseMessage;
import org.pentaho.di.www.ge.websocket.message.GEMessageType;
import org.pentaho.di.www.ge.websocket.message.GERequest;
import org.pentaho.di.www.ge.websocket.message.GERequestEncoderDecoder;
import org.pentaho.di.www.ge.websocket.message.GERequestType;
import org.pentaho.di.www.ge.websocket.message.GEResponse;
import org.pentaho.di.www.ge.websocket.message.GEResponseEncoderDecoder;
import org.pentaho.di.www.ge.websocket.message.GEUpdateEncoderDecoder;
import org.pentaho.di.www.ge.websocket.message.trans.GETransGridUpdate;
import org.pentaho.di.www.ge.websocket.message.trans.GETransGridUpdateEncoderDecoder;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.w3c.dom.html.HTMLDocument;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GraphEditorWebSocketTest {
    private final static Logger logger = LoggerFactory.getLogger(GraphEditorWebSocketTest.class);
    private final static ObjectMapper mapper = new ObjectMapper();

    private CarteSingleton carte = CarteSingleton.getInstance();

    private String carteObjectId;

    private AtmosphereClient wAsyncClient;

    private Server _server;
    private HttpClient _client;
    private Realm _realm;
    private String _protocol;
    private String _baseUrl;
    private String _requestContent;
    private RequestBuilder requestBuilder;

    @Before
    public void before() {

        carte.setJobMap(new JobMap());
        carte.setTransformationMap(new TransformationMap());
        carte.setSocketRepository(new SocketRepository(new LogChannel("Carte")));

        try {
            _server = new Server();
            configureServer(_server);
            _server.start();

            int port = _server.getConnectors()[0].getLocalPort();
            _baseUrl = _protocol + "://localhost:" + port;

            System.out.println("Started");

            carteObjectId = addTransServlet();
            System.out.println(String.format("Installed trans %s", carteObjectId));

            wAsyncClient = ClientFactory.getDefault().newClient(AtmosphereClient.class);
            String url = _baseUrl + "/ged/tenant1000/service";
            //url = url.replaceAll("http", "ws");
            requestBuilder = wAsyncClient.newRequestBuilder().method(Request.METHOD.GET).uri(url)
                    .trackMessageLength(true).encoder(new Encoder<Object, String>() {
                        @Override
                        public String encode(Object data) {
                            try {
                                return mapper.writeValueAsString(data);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }).decoder(new Decoder<String, Object>() {
                        @Override
                        public Object decode(Event type, String data) {

                            data = data.trim();

                            // Padding
                            if (data.length() == 0) {
                                return null;
                            }

                            if (type.equals(Event.MESSAGE)) {
                                try {
                                    logger.debug(String.format("Browser received message %s", data));
                                    int index = data.indexOf("|");
                                    data = data.substring(index >= 0 ? (index + 1) : 0);
                                    ObjectNode obj = (ObjectNode) mapper.readTree(data);
                                    Object msgObj = null;
                                    GEMessageType mt = GEMessageType.valueOf(obj.get("msgType").getTextValue());
                                    if (mt == GEMessageType.REQUEST)
                                        msgObj = GERequestEncoderDecoder.INSTANCE.decode(data);
                                    else if (mt == GEMessageType.RESPONSE)
                                        msgObj = GEResponseEncoderDecoder.INSTANCE.decode(data);
                                    else //Update
                                        msgObj = GEUpdateEncoderDecoder.INSTANCE.decode(data);
                                    return obj;
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    logger.debug("Invalid message {}", data);
                                    return null;
                                }
                            } else {
                                return null;
                            }
                        }
                    }).decoder(new Decoder<String, GETransGridUpdate>() {
                        @Override
                        public GETransGridUpdate decode(Event type, String data) {
                            logger.debug(String.format("Browser GETransGridUpdate received message %s", data));
                            return GETransGridUpdateEncoderDecoder.INSTANCE.decode(data);
                        }
                    }).transport(Request.TRANSPORT.WEBSOCKET);
        } catch (Exception ex) {
            ex.printStackTrace();
            Assert.fail(ex.getMessage());
        }

    }

    /* ------------------------------------------------------------ */
    @After
    public void tearDown() throws Exception {
        if (_server != null) {
            _server.stop();
            _server = null;
        }
    }

    @Test
    public void testClient() throws IOException {
        try {
            Socket socket = wAsyncClient.create();
            final CountDownLatch latch = new CountDownLatch(1);
            socket.on("message", new Function<GEResponse>() {
                @Override
                public void on(GEResponse t) {
                    logger.info("GEResponse Message: " + t.toString());
                }
            }).on("message", new Function<String>() {
                @Override
                public void on(String t) {
                    logger.info("String Message: " + t.toString());
                }
            }).on(new Function<Throwable>() {

                @Override
                public void on(Throwable t) {
                    t.printStackTrace();
                }

            }).on(Event.CLOSE.name(), new Function<String>() {
                @Override
                public void on(String t) {
                    logger.info("Connection closed");
                }
            }).on(Event.OPEN.name(), new Function<String>() {
                @Override
                public void on(String t) {
                    logger.info("Connection opened");
                }
            }).open(requestBuilder.build());

            //Run a trans
            Map<String, String> params = new HashMap<String, String>();
            params.put(GEBaseMessage.PARAM_CARTE_OBJECT_ID, carteObjectId);
            params.put(GEBaseMessage.PARAM_REPOSITORY_ID, "");
            params.put(GEBaseMessage.PARAM_REPOSITORY_USERNAME, "");
            params.put(GEBaseMessage.PARAM_REPOSITORY_PWD, "");
            GERequest request = new GERequest(GERequestType.EXEC_TRANS, params);
            socket.fire(request);

            latch.await(30, TimeUnit.SECONDS);
            socket.close();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static Node parse(String content) throws SAXException, IOException {
        DOMFragmentParser parser = new DOMFragmentParser();
        HTMLDocument document = new HTMLDocumentImpl();
        DocumentFragment fragment = document.createDocumentFragment();

        InputSource is = new InputSource(new StringReader(content));
        parser.parse(is, fragment);
        return fragment;
    }

    public static Node findTextNode(Node parent, String parentNodeName) {
        List<Node> nodes = flatten(parent, null);
        for (Node node : nodes) {
            if (node.getNodeType() == Node.TEXT_NODE
                    && node.getParentNode().getNodeName().equalsIgnoreCase(parentNodeName)) {
                return node;
            }
        }
        return null;
    }

    // public static Node findNode(Node parent, String nodeName, short nodeType,
    // int index) {
    // List<Node> nodes = flatten(parent, null);
    // for (Node node : nodes) {
    // if (node.getNodeName().equals(nodeName) && node.getNodeType() ==
    // nodeType) {
    // return node;
    // }
    // }
    // return null;
    // }

    public static List<Node> flatten(Node parent, List<Node> nodes) {
        Node child = parent.getFirstChild();
        if (nodes == null) {
            nodes = new ArrayList<Node>();
        }
        nodes.add(parent);
        while (child != null) {
            flatten(child, nodes);
            child = child.getNextSibling();
        }
        return nodes;
    }

    public static void print(Node node, String indent) {
        // System.out.println(indent + node.getClass().getName());
        if (node.getNodeType() == Node.TEXT_NODE && !StringUtils.isEmpty(node.getTextContent().trim())) {
            System.out.println(node.getParentNode().getNodeName());
            System.out.println(node.getNodeName() + node.getTextContent());
        }
        Node child = node.getFirstChild();
        while (child != null) {
            print(child, indent + " ");
            child = child.getNextSibling();
        }
    }

    // CHECKSTYLE:Indentation:OFF
    public static Trans generateTestTransformation() {
        RowGeneratorMeta A = new RowGeneratorMeta();
        A.allocate(3);
        A.setRowLimit("1000000");

        A.getFieldName()[0] = "ID";
        A.getFieldType()[0] = ValueMetaBase.getTypeDesc(ValueMetaInterface.TYPE_INTEGER);
        A.getFieldLength()[0] = 7;
        A.getValue()[0] = "1234";

        A.getFieldName()[1] = "Name";
        A.getFieldType()[1] = ValueMetaBase.getTypeDesc(ValueMetaInterface.TYPE_STRING);
        A.getFieldLength()[1] = 35;
        A.getValue()[1] = "Some name";

        A.getFieldName()[2] = "Last updated";
        A.getFieldType()[2] = ValueMetaBase.getTypeDesc(ValueMetaInterface.TYPE_DATE);
        A.getFieldFormat()[2] = "yyyy/MM/dd";
        A.getValue()[2] = "2010/02/09";

        TransMeta transMeta = TransPreviewFactory.generatePreviewTransformation(null, A, "A");
        transMeta.setName("CarteUnitTest");
        transMeta.setSizeRowset(25);
        transMeta.setFeedbackSize(5);
        transMeta.setUsingThreadPriorityManagment(false);

        return new Trans(transMeta);
    }

    public String addTransServlet() {
        String carteObjId = null;

        try {
            startClient(_realm);

            ContentExchange getExchange = new ContentExchange();
            String url = _baseUrl + AddTransServlet.CONTEXT_PATH + "?xml=Y";
            getExchange.setURL(url);
            //getExchange.setRequestHeader("Content-Type", "application/xml");
            getExchange.setMethod(HttpMethods.GET);
            getExchange.setRequestHeader("Host", "tester");
            getExchange.setVersion("HTTP/1.0");

            TransExecutionConfiguration transExecConfig = new TransExecutionConfiguration();
            Trans trans = GraphEditorWebSocketTest.generateTestTransformation();
            TransConfiguration transConfig = new TransConfiguration(trans.getTransMeta(), transExecConfig);

            final AbstractBuffer cb = new ByteArrayBuffer(transConfig.getXML().getBytes("UTF-8"));
            getExchange.setRequestContent(cb);

            _client.send(getExchange);
            int state = getExchange.waitForDone();

            String content = "";
            int responseStatus = getExchange.getResponseStatus();
            if (responseStatus == HttpStatus.OK_200) {
                content = getExchange.getResponseContent();
            }

            stopClient();

            WebResult webResult = new WebResult(XMLHandler.loadXMLString(content, WebResult.XML_TAG));
            Assert.assertEquals(WebResult.STRING_OK, webResult.getResult());

            SlaveServerStatus status = getStatus();
            SlaveServerTransStatus transStatus = status.findTransStatus(trans.getName(), null); // find the first one
            Assert.assertNotNull(transStatus);
            Assert.assertFalse(transStatus.isPaused());
            Assert.assertFalse(transStatus.isRunning());

            carteObjId = webResult.getId();
            Assert.assertNotNull(carteObjId);
        } catch (Exception ex) {
            ex.printStackTrace();
            Assert.fail(ex.getMessage());
        }

        return carteObjId;
    }

    public SlaveServerStatus getStatus() {
        ContentExchange getExchange = new ContentExchange();
        getExchange.setURL(_baseUrl + GetStatusServlet.CONTEXT_PATH + "?xml=Y");
        getExchange.setRequestHeader("Host", "tester");
        getExchange.setMethod(HttpMethods.GET);
        getExchange.setVersion("HTTP/1.0");

        try {
            startClient(_realm);

            _client.send(getExchange);
            int state = getExchange.waitForDone();

            String content = "";
            int responseStatus = getExchange.getResponseStatus();
            if (responseStatus == HttpStatus.OK_200) {
                content = getExchange.getResponseContent();
            }

            stopClient();

            return SlaveServerStatus.fromXML(content);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    protected String getResponseBody(HttpURLConnection conn) throws IOException {
        InputStream in = null;
        try {
            in = conn.getInputStream();
            return IO.toString(in);
        } finally {
            IO.close(in);
        }
    }

    /* ------------------------------------------------------------ */
    protected void configureServer(Server server) throws Exception {
        setProtocol("http");

        SelectChannelConnector connector = new SelectChannelConnector();
        server.addConnector(connector);

        // Add all the servlets defined in kettle-servlets.xml ...
        //
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        HandlerList handlers = new HandlerList();

        // Root
        //
        ServletContextHandler rootHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        rootHandler.setContextPath(GetRootServlet.CONTEXT_PATH);
        handlers.addHandler(rootHandler);
        GetRootServlet rootServlet = new GetRootServlet();
        rootServlet.setJettyMode(true);
        rootHandler.addServlet(new ServletHolder(rootServlet), "/*");

        ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);//new Context( contexts, servlet.getServletPath(), Context.SESSIONS );
        servletHandler.setContextPath(GetStatusServlet.CONTEXT_PATH);
        handlers.addHandler(servletHandler);
        ServletHolder servletHolder = new ServletHolder(GetStatusServlet.class);
        rootHandler.addServlet(servletHolder, GetStatusServlet.CONTEXT_PATH + "/*");

        servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);//new Context( contexts, servlet.getServletPath(), Context.SESSIONS );
        servletHandler.setContextPath(AddTransServlet.CONTEXT_PATH);
        handlers.addHandler(servletHandler);
        servletHolder = new ServletHolder(AddTransServlet.class);
        rootHandler.addServlet(servletHolder, AddTransServlet.CONTEXT_PATH);

        servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);//new Context( contexts, servlet.getServletPath(), Context.SESSIONS );
        servletHandler.setContextPath(StartTransServlet.CONTEXT_PATH);
        handlers.addHandler(servletHandler);
        servletHolder = new ServletHolder(StartTransServlet.class);
        rootHandler.addServlet(servletHolder, StartTransServlet.CONTEXT_PATH + "/*");

        servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);//new Context( contexts, servlet.getServletPath(), Context.SESSIONS );
        servletHandler.setContextPath(PauseTransServlet.CONTEXT_PATH);
        handlers.addHandler(servletHandler);
        servletHolder = new ServletHolder(PauseTransServlet.class);
        rootHandler.addServlet(servletHolder, PauseTransServlet.CONTEXT_PATH + "/*");

        // Atmosphere 
        AtmosphereServlet atmosphereServlet = new AtmosphereServlet();
        servletHolder = new ServletHolder(atmosphereServlet);
        servletHolder.setInitParameter("com.sun.jersey.config.property.packages", "org.pentaho.di.www.websocket");
        servletHolder.setAsyncSupported(true);
        servletHolder.setInitParameter("org.atmosphere.useWebSocket", "true");
        //ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        //context.setContextPath("/ged/*");
        rootHandler.addServlet(servletHolder, "/ged/*");
        //handlers.addHandler(context);

        server.setHandler(handlers);

    }

    /* ------------------------------------------------------------ */
    protected void startClient(Realm realm) throws Exception {
        _client = new HttpClient();
        configureClient(_client);

        if (realm != null)
            _client.setRealmResolver(new SimpleRealmResolver(realm));

        _client.start();
    }

    /* ------------------------------------------------------------ */
    protected void configureClient(HttpClient client) throws Exception {
        client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
    }

    /* ------------------------------------------------------------ */
    protected void stopClient() throws Exception {
        if (_client != null) {
            _client.stop();
            _client = null;
        }
    }

    /* ------------------------------------------------------------ */
    protected String getBaseUrl() {
        return _baseUrl;
    }

    /* ------------------------------------------------------------ */
    protected HttpClient getClient() {
        return _client;
    }

    /* ------------------------------------------------------------ */
    protected Realm getRealm() {
        return _realm;
    }

    /* ------------------------------------------------------------ */
    protected void setProtocol(String protocol) {
        _protocol = protocol;
    }

    /* ------------------------------------------------------------ */
    protected void setRealm(Realm realm) {
        _realm = realm;
    }

    /* ------------------------------------------------------------ */
    public static void copyStream(InputStream in, OutputStream out) {
        try {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) >= 0) {
                out.write(buffer, 0, len);
            }
        } catch (EOFException e) {
            System.err.println(e);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}