com.ibm.sbt.test.lib.MockSerializer.java Source code

Java tutorial

Introduction

Here is the source code for com.ibm.sbt.test.lib.MockSerializer.java

Source

package com.ibm.sbt.test.lib;
/*
 *  Copyright IBM Corp. 2013
 * 
 * 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.
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map.Entry;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.conn.EofSensorInputStream;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.ibm.commons.runtime.Context;
import com.ibm.commons.util.io.base64.Base64OutputStream;
import com.ibm.commons.xml.DOMUtil;
import com.ibm.commons.xml.XMLException;
import com.ibm.sbt.services.client.ClientServicesException;
import com.ibm.sbt.services.endpoints.Endpoint;

/**
 * 
 * @author Lorenzo Boccaccia
 * @author Carlos Manias
 * 
 */
public class MockSerializer {

    // used to know when to append and when to reset the mock file; //TODO will
    // not work if we test with multiple endpoints in the same test run
    private static final HashSet<String> seen = new HashSet<String>();
    private static final HashMap<String, Iterator<Node>> replyStream = new HashMap<String, Iterator<Node>>();
    private Endpoint endpoint;
    private String endpointName;

    public MockSerializer(Endpoint endpoint) {
        if (endpoint instanceof MockEndpoint) {
            endpointName = ((MockEndpoint) endpoint).getInnerEndpoint();
        } else {
            for (Entry e : Context.get().getSessionMap().entrySet()) {
                if (e.getValue() == endpoint)
                    this.endpointName = e.getKey().toString();
            }
        }
    }

    public void writeData(String data) throws IOException {
        File file = getFile(true);
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        // Seek to end of file
        System.out.println("Writing Record @" + file.length() + " in " + file.getAbsolutePath());
        raf.seek((file.length() - "\n</responses>".length()));

        raf.write(data.getBytes("UTF-8"));
        raf.write("\n</responses>".getBytes("UTF-8"));

    }

    private Iterator<Node> getReader() throws IOException {
        String path = getResource();
        if (replyStream.containsKey(path)) {
            return replyStream.get(path);
        }
        Document doc;
        try {

            doc = DOMUtil.createDocument(getClass().getResourceAsStream(path), false);

            Iterator<Node> nodeIt = DOMUtil.evaluateXPath(doc, "//response").getNodeIterator();
            replyStream.put(path, nodeIt);
            return nodeIt;

        } catch (XMLException e) {
            throw new IOException(e);
        }

    }

    public synchronized HttpResponse recordResponse(HttpResponse response) {
        try {
            StringWriter out = new StringWriter();
            out.write("\n<response ");

            out.write("statusCode=\"");
            int statusCode = response.getStatusLine().getStatusCode();
            out.write(String.valueOf(statusCode).trim());
            out.write("\" ");
            out.write("statusReason=\"");
            String reasonPhrase = response.getStatusLine().getReasonPhrase();
            out.write(String.valueOf(reasonPhrase).trim());
            out.write("\">\n");
            out.write("<headers>");
            Header[] allHeaders = response.getAllHeaders();
            out.write(serialize(allHeaders));
            String serializedEntity = null;
            if (response.getEntity() != null) {
                out.write("</headers>\n<data><![CDATA[");
                serializedEntity = serialize(response.getEntity().getContent());
                serializedEntity = serializedEntity.replaceAll("<!\\[CDATA\\[", "\\!\\[CDATA\\[")
                        .replaceAll("\\]\\]>", "\\]\\]");
                out.write(serializedEntity);
                out.write("]]></data>\n</response>");
            } else {
                out.write("</headers>\n</response>");
            }
            out.flush();
            out.close();
            writeData(out.toString());

            return buildResponse(allHeaders, statusCode, reasonPhrase, serializedEntity);

        } catch (IOException e) {
            throw new UnsupportedOperationException(e);
        }
    }

    private HttpResponse buildResponse(Header[] allHeaders, int statusCode, String reasonPhrase,
            String serializedEntity) {
        BasicHttpResponse r = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 0), statusCode, reasonPhrase);
        r.setHeaders(allHeaders);

        if (serializedEntity != null) {
            BasicHttpEntity e = new BasicHttpEntity();
            // TODO: use content-encoding header
            try {
                e.setContent(new ByteArrayInputStream(serializedEntity.getBytes("UTF-8")));
            } catch (UnsupportedEncodingException e1) {
                throw new UnsupportedOperationException(e1);
            }
            for (Header h : allHeaders) {
                if (h.getName().equalsIgnoreCase("Content-Type")) {
                    e.setContentType(h.getValue());
                }
            }
            e.setContentLength(serializedEntity.length());
            r.setEntity(e);
        }
        return r;
    }

    private String serialize(InputStream is) throws IOException {
        ByteArrayOutputStream w = new ByteArrayOutputStream();
        IOUtils.copy(is, w);
        return w.toString("UTF-8");
    }

    private String serialize(Node o) throws IOException {
        ByteArrayOutputStream w = new ByteArrayOutputStream();
        try {
            DOMUtil.serialize(w, o, null);
        } catch (XMLException e) {
            throw new IOException(e);
        }
        return w.toString("UTF-8");

    }

    private String serialize(Header[] o) {
        StringWriter w = new StringWriter();
        for (Header h : o) {
            w.write("\n    <header>\n        <name><![CDATA[");
            w.write(h.getName());
            w.write("]]></name>\n        <value><![CDATA[");
            w.write(h.getValue());
            w.write("]]></value>\n    </header>");

        }

        return w.toString();
    }

    private String serialize(Object o) throws IOException {
        if (o instanceof EofSensorInputStream)
            return serialize((EofSensorInputStream) o);
        if (o instanceof Node)
            return serialize((Node) o);
        if (o instanceof Header[])
            return serialize((Header[]) o);
        ByteArrayOutputStream w = new ByteArrayOutputStream();
        Base64OutputStream base64OutputStream = new Base64OutputStream(w);
        ObjectOutputStream os = new ObjectOutputStream(base64OutputStream);
        os.writeObject(o);
        os.flush();
        os.close();
        base64OutputStream.flush();
        base64OutputStream.close();
        return w.toString("UTF-8");
    }

    private Object deserialize(String o) throws IOException {
        ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(o.getBytes()));
        try {
            return is.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException(e);
        } finally {
            is.close();
        }
    }

    public HttpResponse replayResponse() throws ClientServicesException {
        try {
            Node r = getReader().next();

            NamedNodeMap nnm = r.getAttributes();

            String code = nnm.getNamedItem("statusCode").getTextContent();
            String reason = nnm.getNamedItem("statusReason").getTextContent();

            Node headers = (Node) DOMUtil.evaluateXPath(r, "./headers").getSingleNode();
            Node data = (Node) DOMUtil.evaluateXPath(r, "./data").getSingleNode();
            String entity = null;
            if (data != null) {
                if (data.getFirstChild() == null)
                    entity = "";
                else
                    entity = ((CharacterData) data.getFirstChild()).getData();
            }
            Iterator<Node> hIt = (Iterator<Node>) DOMUtil.evaluateXPath(headers, "./header").getNodeIterator();
            ArrayList<Header> allHeaders = new ArrayList<Header>();

            while (hIt.hasNext()) {
                Node headerNode = hIt.next();
                String name = ((Node) DOMUtil.evaluateXPath(headerNode, "./name").getSingleNode()).getTextContent();
                String value = ((Node) DOMUtil.evaluateXPath(headerNode, "./value").getSingleNode())
                        .getTextContent();
                allHeaders.add(new BasicHeader(name, value));
            }

            return buildResponse(allHeaders.toArray(new Header[allHeaders.size()]), Integer.valueOf(code), reason,
                    entity);

        } catch (FileNotFoundException e) {
            StackTraceElement trace = getStackTraceElement();
            String fullClassName = trace.getClassName();
            String methodName = trace.getMethodName();
            String endpointName = getEndpointName();
            throw new MockingException(e,
                    "Mocking file missing for test: " + fullClassName + "." + methodName + "/" + endpointName);
        } catch (Exception e) {
            throw new MockingException(e, "Corrupted Mocking file, please regenerate: " + getPath());
        }
    }

    private void deserializeThrowable(Node r) {
        // TODO Auto-generated method stub

    }

    private StackTraceElement getStackTraceElement() {
        StackTraceElement last = null;
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        for (StackTraceElement trace : stackTraceElements) {
            try {
                if (Class.forName(trace.getClassName()).getMethod(trace.getMethodName())
                        .isAnnotationPresent(Test.class))
                    last = trace;
                if (Class.forName(trace.getClassName()).getMethod(trace.getMethodName())
                        .isAnnotationPresent(After.class))
                    last = trace;
                if (Class.forName(trace.getClassName()).getMethod(trace.getMethodName())
                        .isAnnotationPresent(Before.class))
                    last = trace;
                if (Class.forName(trace.getClassName()).getMethod(trace.getMethodName())
                        .isAnnotationPresent(AfterClass.class))
                    last = trace;
                if (Class.forName(trace.getClassName()).getMethod(trace.getMethodName())
                        .isAnnotationPresent(BeforeClass.class))
                    last = trace;
            } catch (Exception e) {
            }
        }
        return last;
    }

    private File getFile(boolean write) throws IOException {
        String path = getPath();
        boolean reset = !seen.contains(path);
        seen.add(path);

        File file = new File(path);
        File parentFolder = new File(file.getParent());
        parentFolder.mkdirs();
        if (write && reset && file.exists())
            file.delete();
        if (!file.exists()) {
            file.createNewFile();
            FileOutputStream st = new FileOutputStream(file);
            st.write("<?xml version=\"1.0\"?>\n<responses>\n</responses>".getBytes("UTF-8"));
            st.flush();
            st.close();
        }
        return file;
    }

    private String getEndpointName() {
        return endpointName;
    }

    private String getPath() {
        StackTraceElement trace = getStackTraceElement();
        String basePath = System.getProperty("user.dir");
        String fullClassName = trace.getClassName().replace(".", File.separator);
        String className = fullClassName.substring(fullClassName.lastIndexOf(File.separatorChar));
        String packageName = fullClassName.substring(0, fullClassName.lastIndexOf(File.separatorChar));
        String methodName = trace.getMethodName();
        String endpointName = getEndpointName();
        String path = new StringBuilder(basePath).append(File.separator).append("src").append(File.separator)
                .append("test").append(File.separator).append("resources").append(File.separator)
                .append(packageName).append(File.separator).append("mockData").append(File.separator)
                .append(endpointName).append(className).append("_").append(methodName).append(".mock").toString();
        return path;
    }

    private String getResource() {
        StackTraceElement trace = getStackTraceElement();
        String methodName = trace.getMethodName();

        String fullClassName = trace.getClassName().replace(".", "/");
        String packageName = fullClassName.substring(0, fullClassName.lastIndexOf("/"));
        String className = fullClassName.substring(fullClassName.lastIndexOf("/"));
        String resource = new StringBuilder("/").append(packageName).append("/").append("mockData").append("/")
                .append(endpointName).append(className).append("_").append(methodName).append(".mock").toString();
        return resource;
    }
}