shapeways.api.robocreator.RoboCreatorWeb.java Source code

Java tutorial

Introduction

Here is the source code for shapeways.api.robocreator.RoboCreatorWeb.java

Source

/*****************************************************************************
 *                        Copyright Shapeways, Inc. (c) 2011
 *                               Java Source
 *
 * This source is licenses under the Apache License, version 2.0
 *
 * http://www.apache.org/licenses/LICENSE-2.0.html
 *
 *
 ****************************************************************************/

package shapeways.api.robocreator;

// Standard imports
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.RegionUtils;
import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.CreateQueueRequest;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.QueueAttributeName;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.google.gson.Gson;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import javax.servlet.*;
import javax.servlet.http.*;

/**
 * Web front end for Robocreators.
 *
 * @author Alan Hudson
 */
public class RoboCreatorWeb extends HttpServlet implements SQSQueueListener {
    private static final boolean DEBUG = true;
    private static final String QUEUE_PREPEND = "RoboCreator_";

    // What AWS instanceType are we on
    private String instanceType;

    // AWS Credentials
    private String awsAccessKey;
    private String awsAccessSecret;
    private String awsRegion;

    // Internal service queue for threads
    private ThreadPoolExecutor threadPool;

    // Queue name for messages across machines.
    private String kernels;
    private String queueUrl;
    private Integer visibilityTimeout;

    protected AmazonSQS sqs;
    protected Map<String, String> queUrlMap;

    /**
     * Initialize the servlet. Sets up the base directory properties for finding
     * stuff later on.
     */
    public void init() throws ServletException {

        if (DEBUG) {
            System.out.println("Starting RoboCreatorWeb");
        }

        ServletConfig config = getServletConfig();
        ServletContext ctx = config.getServletContext();

        kernels = getInitParameter("Kernels");
        if (kernels == null) {
            System.out.println("ServiceQueue is null, add entry to web.xml");
        }

        System.out.println("Handling Kernels: " + kernels);
        instanceType = getInstanceMetadata("instance-type", "localhost");

        // 0 = number of processors.  > 0 specific number.  Default is 1
        String num_threads_st = getInitParameter("NumThreads");
        Gson gson = new Gson();
        Map<String, Number> threadsMap = new HashMap<String, Number>();
        try {
            threadsMap = gson.fromJson(num_threads_st, Map.class);
        } catch (Exception e) {
            System.out.println(
                    "Cannot parse threads in RoboCreatorWeb.  Should be map of instanceType to numThreads");
            System.out.println("numThreads: " + num_threads_st);
            e.printStackTrace();
        }

        int threads = threadsMap.get(instanceType).intValue();

        if (threads == 0) {
            int cores = Runtime.getRuntime().availableProcessors();
            threads = cores;
        }

        threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(threads);

        System.out.println("ThreadPool: " + threadPool);
        awsAccessKey = getInitParameter("AWSAccessKey");
        awsAccessSecret = getInitParameter("AWSAccessSecret");
        awsRegion = getInitParameter("AWSRegion");
        String st = getInitParameter("AWSSQSVisibilityTimeout");
        if (st != null) {
            visibilityTimeout = Integer.parseInt(st);
        }

        // TODO: Not certain we want to do these here as it delays deploy and might stop all deploys if AWS is down.
        // Switch to a threaded runnable or maybe just add the job to the threadPool we have.
        sqs = new AmazonSQSClient(new BasicAWSCredentials(awsAccessKey, awsAccessSecret));

        Region region = RegionUtils.getRegion(awsRegion);
        sqs.setRegion(region);

        queUrlMap = new HashMap<String, String>();

        System.out.println("Creating Queues");
        String[] queues = kernels.split(" ");
        for (int i = 0; i < queues.length; i++) {
            threadPool.submit(new SQSCreateQueueTask(sqs, QUEUE_PREPEND + queues[i], visibilityTimeout, this));
        }
    }

    @Override
    public void queueCreated(String name, String url) {
        System.out.println("Queue created: " + name + " --> " + url);
        queUrlMap.put(name, url);
    }

    @Override
    public void queueFailed(String name, Exception e) {
        System.out.println("Queue failed: " + name);
    }

    @Override
    public void messagesReceived(List<Message> messages) {

    }

    @Override
    public void destroy() {
        threadPool.shutdown();
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getPathInfo();
        System.out.println("Path: " + url);

        if (url.contains("/order")) {
            handlePreview(req, resp);
        } else if (url.contains("/preview")) {
            handleOrder(req, resp);
        }
    }

    protected void handleOrder(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("application/json");
        Gson gson = new Gson();

        BufferedReader reader = req.getReader();
        String msg = IOUtils.toString(reader);
        System.out.println("Msg: " + msg);

        Map<String, Object> params = gson.fromJson(msg, Map.class);
        String url = req.getPathInfo();
        System.out.println("Path: " + url);

        params.put(QUEUE_PREPEND + "action", "order");

        int idx = url.indexOf("/", 1);
        String kernel_name = url.substring(1, idx);

        System.out.println("kernel: " + kernel_name);
        System.out.println("que: " + QUEUE_PREPEND + kernel_name);
        String qurl = queUrlMap.get(QUEUE_PREPEND + kernel_name);

        if (qurl == null) {
            System.out.println("Unknown que");
            issueError("Unknown queue: " + kernel_name, resp);
            return;
        }

        // TODO: Need to insure < 64K
        msg = gson.toJson(params);
        SQSEnqueueTask task = new SQSEnqueueTask(sqs, qurl, msg);
        threadPool.submit(task);

        Map<String, Object> result = new HashMap<String, Object>();

        result.put("result", "success");

        String json = gson.toJson(result);

        resp.setContentLength(json.length());
        resp.setStatus(HttpServletResponse.SC_OK);

        ServletOutputStream sos = resp.getOutputStream();
        byte[] ba = json.getBytes();
        sos.write(ba, 0, ba.length);

        resp.flushBuffer();

    }

    protected void handlePreview(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("application/json");
        Gson gson = new Gson();

        BufferedReader reader = req.getReader();
        String msg = IOUtils.toString(reader);
        System.out.println("Msg: " + msg);

        Map<String, Object> params = gson.fromJson(msg, Map.class);
        String url = req.getPathInfo();
        System.out.println("Path: " + url);

        params.put(QUEUE_PREPEND + "action", "preview");

        int idx = url.indexOf("/", 1);
        String kernel_name = url.substring(1, idx);

        System.out.println("kernel: " + kernel_name);
        System.out.println("que: " + QUEUE_PREPEND + kernel_name);
        String qurl = queUrlMap.get(QUEUE_PREPEND + kernel_name);

        if (qurl == null) {
            System.out.println("Unknown que");
            issueError("Unknown queue: " + kernel_name, resp);
            return;
        }

        // TODO: Need to insure < 64K
        msg = gson.toJson(params);
        SQSEnqueueTask task = new SQSEnqueueTask(sqs, qurl, msg);
        threadPool.submit(task);

        Map<String, Object> result = new HashMap<String, Object>();

        result.put("result", "success");

        String json = gson.toJson(result);

        resp.setContentLength(json.length());
        resp.setStatus(HttpServletResponse.SC_OK);

        ServletOutputStream sos = resp.getOutputStream();
        byte[] ba = json.getBytes();
        sos.write(ba, 0, ba.length);

        resp.flushBuffer();
    }

    private void issueError(String reason, HttpServletResponse resp) throws IOException {
        HashMap<String, Object> result = new HashMap<String, Object>();
        Gson gson = new Gson();

        result.put("result", "failure");
        result.put("reason", reason);

        String json = gson.toJson(result);

        resp.setContentLength(json.length());
        resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);

        ServletOutputStream sos = resp.getOutputStream();
        byte[] ba = json.getBytes();
        sos.write(ba, 0, ba.length);

        resp.flushBuffer();
    }

    /**
     * Get the public facing hostname for this machine.  Uses AWS metadata service.
     */
    protected String getInstanceMetadata(String name, String defValue) {
        ByteArrayOutputStream baos = null;
        BufferedOutputStream bout = null;
        String ret_val = null;
        InputStream is = null;

        try {
            URL url = new URL("http://169.254.169.254/latest/meta-data/" + name);

            URLConnection urlConn = url.openConnection();
            urlConn.setConnectTimeout(5000);
            urlConn.setReadTimeout(15000);
            urlConn.setAllowUserInteraction(false);
            urlConn.setDoOutput(true);
            is = new BufferedInputStream(urlConn.getInputStream());

            baos = new ByteArrayOutputStream();
            bout = new BufferedOutputStream(baos, 1024);

            int buffSize = 8 * 1024;
            byte data[] = new byte[buffSize];
            int count;

            while ((count = is.read(data, 0, buffSize)) >= 0) {
                baos.write(data, 0, count);
            }

            ret_val = baos.toString();
        } catch (Exception e) {
            // ignore
            //e.printStackTrace();
        } finally {
            try {
                bout.close();
                is.close();
            } catch (Exception e) {
                // ignore
            }
        }

        if (ret_val == null) {
            ret_val = defValue;
        }

        return ret_val;
    }
}