Java tutorial
/* * Copyright (c) 2013, Francis Galiegue <fgaliegue@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the Lesser GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 * Lesser GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.mockey.ui; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.fge.jsonschema.JsonValidators; import com.github.fge.jsonschema.constants.ParseError; import com.github.fge.jsonschema.constants.ValidateRequest; import com.github.fge.jsonschema.constants.ValidateResponse; import com.github.fge.jsonschema.main.JsonValidator; import com.github.fge.jsonschema.report.ProcessingReport; import com.github.fge.jsonschema.util.AsJson; import com.github.fge.jsonschema.util.JsonLoader; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; import com.google.common.io.Closeables; import com.google.common.net.MediaType; import com.mockey.model.Scenario; import com.mockey.model.Service; import com.mockey.storage.IMockeyStorage; import com.mockey.storage.StorageRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.Set; /** * Servlet responsible of validating a schema/data pair * * <p> * It returns a JSON Object as a result with the appropriate media type (thanks * Guava for providing {@link MediaType#JSON_UTF_8}!). * </p> * * <p> * This object has the following members: * </p> * * <ul> * <li>{@code invalidSchema}: boolean indicating whether the provided schema was * valid JSON;</li> * <li>{@code invalidData}: same, but for the data;</li> * <li>{@code valid} (only if the schema and data are valid): whether the * validation has succeeded;</li> * <li>{@code results} (only if the schema and data are valid): the result of * {@link ProcessingReport#getMessages()}.</li> * </ul> */ public final class JsonSchemaValidateServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 2625630469996134777L; private static final Logger logger = LoggerFactory.getLogger(JsonSchemaValidateServlet.class); private static IMockeyStorage store = StorageRegistry.MockeyStorage; public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { Long serviceId = new Long(req.getParameter("serviceId")); Long scenarioId = null; scenarioId = new Long(req.getParameter("scenarioId")); Service service = store.getServiceById(serviceId); Scenario scenario = service.getScenario(scenarioId); req.setAttribute("service", service); req.setAttribute("scenario", scenario); } catch (Exception e) { logger.debug("Unable to retrieve a Service of ID: " + req.getParameter("serviceId")); } // Get the service. RequestDispatcher dispatch = req.getRequestDispatcher("/jsonschemavalidate.jsp"); dispatch.forward(req, resp); } @Override public void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final Set<String> params = Sets.newHashSet(); final Enumeration<String> enumeration = req.getParameterNames(); // FIXME: no duplicates, it seems, but I cannot find the spec which // guarantees that while (enumeration.hasMoreElements()) params.add(enumeration.nextElement()); // We have required parameters if (!params.containsAll(ValidateRequest.REQUIRED_PARAMS)) { logger.warn("Missing parameters! Someone using me as a web service?"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing parameters"); return; } // We don't want extraneous parameters params.removeAll(ValidateRequest.VALID_PARAMS); if (!params.isEmpty()) { logger.warn("Invalid parameters! Someone using me as a web service?"); resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid parameters"); return; } final String rawSchema = req.getParameter(ValidateRequest.SCHEMA); final String data = req.getParameter(ValidateRequest.DATA); // Set correct content type resp.setContentType(MediaType.JSON_UTF_8.toString()); final boolean useV3 = Boolean.parseBoolean(req.getParameter(ValidateRequest.USE_V3)); final boolean useId = Boolean.parseBoolean(req.getParameter(ValidateRequest.USE_ID)); final JsonNode ret = JsonSchemaUtil.buildResult(rawSchema, data, useV3, useId); final OutputStream out = resp.getOutputStream(); try { out.write(ret.toString().getBytes(Charset.forName("UTF-8"))); out.flush(); } finally { Closeables.closeQuietly(out); } } // /* // * Build the response. When we arrive here, we are guaranteed that we have // * the needed elements. // */ // @VisibleForTesting // static JsonNode buildResult(final String rawSchema, final String rawData, // final boolean useV3, final boolean useId) throws IOException { // final ObjectNode ret = JsonNodeFactory.instance.objectNode(); // // final boolean invalidSchema = fillWithData(ret, // ValidateResponse.SCHEMA, ValidateResponse.INVALID_SCHEMA, // rawSchema); // final boolean invalidData = fillWithData(ret, ValidateResponse.DATA, // ValidateResponse.INVALID_DATA, rawData); // // final JsonNode schemaNode = ret.remove(ValidateResponse.SCHEMA); // final JsonNode data = ret.remove(ValidateResponse.DATA); // // if (invalidSchema || invalidData) // return ret; // // final JsonValidator validator = JsonValidators // .withOptions(useV3, useId); // final ProcessingReport report = validator.validateUnchecked(schemaNode, // data); // // final boolean success = report.isSuccess(); // ret.put(ValidateResponse.VALID, success); // ret.put(ValidateResponse.RESULTS, ((AsJson) report).asJson()); // return ret; // } // // /* // * We have to use that since Java is not smart enough to detect that // * sometimes, a variable is initialized in all paths. // * // * This returns true if the data is invalid. // */ // private static boolean fillWithData(final ObjectNode node, // final String onSuccess, final String onFailure, final String raw) // throws IOException { // try { // node.put(onSuccess, JsonLoader.fromString(raw)); // return false; // } catch (JsonProcessingException e) { // node.put(onFailure, buildParsingError(e, raw.contains("\r\n"))); // return true; // } // } // // private static JsonNode buildParsingError(final JsonProcessingException e, // final boolean crlf) { // final JsonLocation location = e.getLocation(); // final ObjectNode ret = JsonNodeFactory.instance.objectNode(); // // /* // * Unfortunately, for some reason, Jackson botches the column number in // * its JsonPosition -- I cannot figure out why exactly. However, it does // * have a correct offset into the buffer. // * // * The problem is that if the input has CR/LF line terminators, its // * offset will be "off" by the number of lines minus 1 with regards to // * what JavaScript sees as positions in text areas. Make the necessary // * adjustments so that the caret jumps at the correct position in this // * case. // */ // final int lineNr = location.getLineNr(); // int offset = (int) location.getCharOffset(); // if (crlf) // offset = offset - lineNr + 1; // ret.put(ParseError.LINE, lineNr); // ret.put(ParseError.OFFSET, offset); // // // Finally, put the message // ret.put(ParseError.MESSAGE, e.getOriginalMessage()); // return ret; // } }