uk.ac.imperial.presage2.web.SimulationServlet.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.imperial.presage2.web.SimulationServlet.java

Source

/**
 *    Copyright (C) 2011-2012 Sam Macbeth <sm1106 [at] imperial [dot] ac [dot] uk>
 *
 *    This file is part of Presage2.
 *
 *     Presage2 is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Presage2 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 Lesser Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser Public License
 *     along with Presage2.  If not, see <http://www.gnu.org/licenses/>.
 */
package uk.ac.imperial.presage2.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

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

import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import uk.ac.imperial.presage2.core.db.DatabaseService;
import uk.ac.imperial.presage2.core.db.StorageService;
import uk.ac.imperial.presage2.core.db.persistent.PersistentSimulation;

import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class SimulationServlet extends GenericPresageServlet {

    private static final long serialVersionUID = 1L;
    private static final int _CACHE_TTL = 10000;
    private final Logger logger = Logger.getLogger(SimulationServlet.class);

    private List<PersistentSimulation> cachedSimulations = null;
    private long cacheTime = 0;

    private final static String[] simulationFields = { "name", "classname", "state", "currentTime", "created",
            "started", "finished", "id" };

    private final static Pattern ID_REGEX = Pattern.compile("/(\\d+)$");

    @Inject
    public SimulationServlet(DatabaseService db, StorageService sto) throws Exception {
        super(db, sto);
    }

    /**
     * REST CREATE simulation
     */
    @Override
    protected synchronized void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        logRequest(req);
        try {
            // parse posted simulation json object
            JSONObject request = new JSONObject(new JSONTokener(req.getReader()));
            // validate data
            String name = request.getString("name");
            String classname = request.getString("classname");
            String state = request.getString("state");
            int finishTime = request.getInt("finishTime");
            PersistentSimulation sim = sto.createSimulation(name, classname, state, finishTime);
            resp.setStatus(201);
            if (!state.equalsIgnoreCase("GROUP")) {
                // add finishTime as a parameter
                sim.addParameter("finishTime", Integer.toString(finishTime));
            }
            if (request.has("parameters")) {
                JSONObject parameters = request.getJSONObject("parameters");
                for (@SuppressWarnings("unchecked")
                Iterator<String> iterator = parameters.keys(); iterator.hasNext();) {
                    String key = (String) iterator.next();
                    sim.addParameter(key, parameters.getString(key));
                }
            }
            // set simulation parent
            if (request.has("parent")) {
                try {
                    long parentId = request.getLong("parent");
                    PersistentSimulation parent = sto.getSimulationById(parentId);
                    if (parent != null)
                        sim.setParentSimulation(parent);
                } catch (JSONException e) {
                    // ignore non number parent.
                }
            }

            // clear sim cache
            this.cachedSimulations = null;

            // return created simulation object
            JSONObject response = new JSONObject();
            response.put("success", true);
            response.put("message", "Created simulation");
            response.put("data", simulationToJSON(sim));
            resp.getWriter().write(response.toString());
        } catch (JSONException e) {
            // bad request
            resp.setStatus(400);
            logger.warn("Couldn't create new simulation", e);
        }
    }

    /**
     * REST UPDATE simulation
     */
    @Override
    protected synchronized void doPut(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        logRequest(req);
        String path = req.getPathInfo();
        Matcher matcher = ID_REGEX.matcher(path);
        if (matcher.matches()) {
            long simId = Integer.parseInt(matcher.group(1));
            try {
                // get data sent to us
                JSONObject input = new JSONObject(new JSONTokener(req.getReader()));
                // validate fields
                if (Integer.parseInt(input.get("id").toString()) != simId || input.getString("state").length() < 1
                        || input.getInt("currentTime") < 0 || input.getInt("finishTime") < 0) {
                    resp.setStatus(400);
                    return;
                }

                PersistentSimulation sim = sto.getSimulationById(simId);
                String state = input.getString("state");
                if (!(sim.getState().equals(state))) {
                    sim.setState(state);
                }
                int currentTime = input.getInt("currentTime");
                if (!(sim.getCurrentTime() == currentTime)) {
                    sim.setCurrentTime(currentTime);
                }
                int finishTime = input.getInt("finishTime");
                if (!(sim.getFinishTime() == finishTime)) {
                    sim.setCurrentTime(finishTime);
                }
                JSONObject parameters = input.getJSONObject("parameters");
                for (@SuppressWarnings("unchecked")
                Iterator<String> iterator = parameters.keys(); iterator.hasNext();) {
                    String key = (String) iterator.next();
                    sim.addParameter(key, parameters.getString(key));
                }
                // parent simulation
                try {
                    long parentId = input.getLong("parent");
                    PersistentSimulation parent = sim.getParentSimulation();
                    if (parentId == 0 && parent != null) {
                        sim.setParentSimulation(null);
                    } else if (parentId > 0 && parent.getID() != parentId) {
                        parent = sto.getSimulationById(parentId);
                        if (parent != null) {
                            sim.setParentSimulation(parent);
                        }
                    }
                } catch (JSONException e) {
                }

                JSONObject response = new JSONObject();
                response.put("success", true);
                response.put("data", simulationToJSON(sim));
                resp.getWriter().write(response.toString());
                // clear sim cache
                this.cachedSimulations = null;
            } catch (JSONException e) {
                resp.setStatus(400);
            }
        } else {
            resp.setStatus(400);
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        logRequest(req);
        // GET switchboard: route rest gets and get lists to correct function
        String path = req.getPathInfo();

        if (path != null) {
            Matcher matcher = ID_REGEX.matcher(path);
            if (matcher.matches()) {
                doGetSimulation(req, resp, Long.parseLong(matcher.group(1)));
                return;
            }
        }
        doListSimulations(req, resp);
    }

    private void doGetSimulation(HttpServletRequest req, HttpServletResponse resp, long simId) throws IOException {
        if (simId > 0) {
            JSONObject jsonResp = new JSONObject();
            try {
                jsonResp.put("success", true);
                jsonResp.put("data", simulationToJSON(sto.getSimulationById(simId)));
                resp.getWriter().write(jsonResp.toString());
            } catch (JSONException e) {
                resp.setStatus(500);
            } catch (NullPointerException e) {
                resp.setStatus(400);
            }
        } else {
            resp.setStatus(400);
        }
    }

    /**
     * List simulations.
     * 
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    protected synchronized void doListSimulations(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        // paging parameters
        final int start = getIntegerParameter(req, "start", 0);
        final int limit = getIntegerParameter(req, "limit", 30);
        // sorting parameters
        String sort = req.getParameter("sort");
        String direction = req.getParameter("dir");
        if (sort == null || sort.isEmpty()) {
            sort = "id";
            direction = "ASC";
        } else if (direction == null || direction.isEmpty()) {
            direction = "ASC";
        }

        // init response object
        JSONObject jsonResp = new JSONObject();
        try {
            this.ensureCache();

            jsonResp.put("totalCount", this.cachedSimulations.size());

            // sort simulations
            Collections.sort(this.cachedSimulations, new SimulationComparator(sort, direction));

            // extract sims to JSON
            JSONArray simulations = new JSONArray();
            int count = 0;
            for (ListIterator<PersistentSimulation> it = this.cachedSimulations.listIterator(start); it
                    .hasNext();) {
                PersistentSimulation sim = it.next();
                simulations.put(simulationToJSON(sim));
                count++;
                if (count > limit)
                    break;
            }
            jsonResp.put("data", simulations);

        } catch (JSONException e) {
            logger.error("JSON write error.", e);
            resp.setStatus(500);
        } finally {
            try {
                jsonResp.put("success", true);
                resp.setStatus(200);
                resp.getWriter().write(jsonResp.toString());
            } catch (JSONException e) {
                logger.error("Failed to write JSON success", e);
                resp.setStatus(500);
            }
        }

    }

    public static JSONObject simulationToJSON(PersistentSimulation sim) throws JSONException {
        JSONObject jsonSim = new JSONObject();
        jsonSim.put("id", sim.getID());
        jsonSim.put("name", sim.getName());
        jsonSim.put("classname", sim.getClassName());
        jsonSim.put("state", sim.getState());
        jsonSim.put("finishTime", sim.getFinishTime());
        jsonSim.put("currentTime", sim.getCurrentTime());
        jsonSim.put("createdAt", sim.getCreatedAt());
        jsonSim.put("startedAt", sim.getStartedAt());
        jsonSim.put("finishedAt", sim.getFinishedAt());
        JSONObject parameters = new JSONObject();
        for (Entry<String, String> param : sim.getParameters().entrySet()) {
            parameters.put(param.getKey(), param.getValue());
        }
        jsonSim.put("parameters", parameters);
        // for tree representations
        if (sim.getChildren().size() == 0 && !sim.getState().equalsIgnoreCase("GROUP")) {
            jsonSim.put("leaf", true);
        } else {
            jsonSim.put("leaf", false);
            jsonSim.put("expanded", false);
        }
        // parent
        PersistentSimulation parent = sim.getParentSimulation();
        jsonSim.put("parent", parent != null ? parent.getID() : 0L);

        return jsonSim;
    }

    /**
     * Check the validity of the simulation cache and update it if neccessary.
     */
    private void ensureCache() {
        // check sim cache (30s ttl)
        if (this.cachedSimulations == null || this.cacheTime < System.currentTimeMillis() - _CACHE_TTL) {
            logger.info("Refreshing simulation cache");
            // update cache from db
            List<Long> simulationIds = sto.getSimulations();
            this.cachedSimulations = new ArrayList<PersistentSimulation>(simulationIds.size());
            for (Long simId : simulationIds) {
                this.cachedSimulations.add(sto.getSimulationById(simId));
            }
            this.cacheTime = System.currentTimeMillis();
        }
    }

    private class SimulationComparator implements Comparator<PersistentSimulation> {

        int field = simulationFields.length - 1;
        final int direction;
        final Comparator<String> stringComparator = String.CASE_INSENSITIVE_ORDER;

        SimulationComparator(String field, String direction) {
            super();
            for (int i = 0; i < simulationFields.length; i++) {
                if (field.equalsIgnoreCase(simulationFields[i])) {
                    this.field = i;
                    break;
                }
            }
            if (direction.equalsIgnoreCase("DESC"))
                this.direction = -1;
            else
                this.direction = 1;
        }

        @Override
        public int compare(PersistentSimulation s1, PersistentSimulation s2) {
            int diff = 0;
            switch (field) {
            case 0: // name
                diff = stringComparator.compare(s1.getName(), s1.getName());
                break;
            case 1: // classname
                diff = stringComparator.compare(s1.getClassName(), s2.getClassName());
                break;
            case 2: // state
                diff = stringComparator.compare(s1.getState(), s2.getState());
                break;
            case 3: // currentTime
                diff = s1.getCurrentTime() - s2.getCurrentTime();
                break;
            case 4: // created at
                diff = (int) (s1.getCreatedAt() - s2.getCreatedAt());
                break;
            case 5: // started at
                diff = (int) (s1.getStartedAt() - s2.getStartedAt());
                break;
            case 6: // finished at
                diff = (int) (s1.getFinishedAt() - s2.getFinishedAt());
                break;
            // id
            default:
                diff = (int) (s1.getID() - s2.getID());
                break;
            }
            diff *= this.direction;
            return diff;
        }

    }

}