/* * This file is part of Mockey, a tool for testing application * interactions over HTTP, with a focus on testing web services, * specifically web applications that consume XML, JSON, and HTML. * * Copyright (C) 2009-2010 Authors: * * chad.lafontaine (chad.lafontaine AT gmail DOT com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ package com.mockey.ui; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.json.JSONObject; import com.mockey.model.ApiDocAttribute; import com.mockey.model.ApiDocFieldValue; import com.mockey.model.ApiDocRequest; import com.mockey.model.ApiDocResponse; import com.mockey.model.ApiDocService; import com.mockey.model.Service; import com.mockey.storage.IApiStorage; import com.mockey.storage.IApiStorageInMemory; import com.mockey.storage.IMockeyStorage; import com.mockey.storage.StorageRegistry; /** * Management of Service configuration, in addition to HTTP Documentation. * * @author chadlafontaine * */ public class ServiceConfigurationServlet extends HttpServlet { private static final long serialVersionUID = 7762196322218894996L; private Logger log = Logger.getLogger(ServiceConfigurationServlet.class); private IMockeyStorage store = StorageRegistry.MockeyStorage; private IApiStorage apiStore = IApiStorageInMemory.getInstance(); /** * Loads up the HTTP API Documentation in memory for this service. The HTTP * API information to describe this servlet's REQUEST and RESPONSE messaging * is displayed to the end user via the Service API help page. */ public void init() throws ServletException { // ***************************** // THIS SERVICE API DESCRIPTION CONTRACT // ***************************** // This information is used in the API JSP document, used to describe // how to make setting changes from a head-less client. if (apiStore .getApiDocServiceByName(ServiceConfigurationAPI.API_SERVICE_CONFIGURATION_NAME) == null) { ApiDocService apiDocService = new ApiDocService(); apiDocService .setName(ServiceConfigurationAPI.API_SERVICE_CONFIGURATION_NAME); // TODO: We need to use a pattern matching replace e.g. ${0} ${1} // with array ["a", "b"] for VALUES apiDocService.setServicePath("/config/service"); apiDocService .setDescription("If you need to configure Mockey services without a web browser (e.g. bots), then this API may serve your needs. "); // ***************************** // REQUEST DEFINITION // ***************************** ApiDocRequest apiDocRequest = new ApiDocRequest(); ApiDocAttribute reqServiceId = new ApiDocAttribute(); reqServiceId.setFieldName(ServiceConfigurationAPI.API_SERVICE_ID); reqServiceId.addFieldValues(new ApiDocFieldValue("[identifier]", "A valid service identifier.")); reqServiceId.setExample("123"); apiDocRequest.addAttribute(reqServiceId); ApiDocAttribute reqServiceName = new ApiDocAttribute(); reqServiceName .setFieldName(ServiceConfigurationAPI.API_SERVICE_NAME); reqServiceName.addFieldValues(new ApiDocFieldValue("[string]", "A valid service name.")); reqServiceName.setExample("My Service Name"); apiDocRequest.addAttribute(reqServiceName); // SCHEMA ApiDocAttribute reqServiceSchema = new ApiDocAttribute(); reqServiceSchema .setFieldName(ServiceConfigurationAPI.API_SERVICE_SCHEMA); reqServiceSchema.addFieldValues(new ApiDocFieldValue("[string]", "A valid JSON Schema definition.")); reqServiceSchema .setExample("{\"type\":\"object\",\"$schema\": \"http://json-schema.org/draft-03/schema\",\"id\": \"#\",\"required\":false,\"properties\":{ \"address\": { \"type\":\"object\", \"id\": \"address\", \"required\":false, \"properties\":{ \"streetAddress\": { \"type\":\"string\", \"id\": \"streetAddress\", \"required\":false } } } }}"); apiDocRequest.addAttribute(reqServiceSchema); ApiDocAttribute reqServiceSchemaEnableFlag = new ApiDocAttribute(); reqServiceSchemaEnableFlag .setFieldName(ServiceConfigurationAPI.API_SERVICE_SCHEMA_ENABLE_FLAG); reqServiceSchemaEnableFlag .addFieldValues(new ApiDocFieldValue( "[boolean]", "Set to true for the service to validate each Service Scenario JSON response with the provided JSON Schema.")); reqServiceSchemaEnableFlag.setExample("true"); apiDocRequest.addAttribute(reqServiceSchemaEnableFlag); // REQUEST INSPECTOR RULES ApiDocAttribute reqInspectorRules = new ApiDocAttribute(); reqInspectorRules .setFieldName(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_RULES); reqInspectorRules.addFieldValues(new ApiDocFieldValue("[string]", "Request evaluation rules in JSON. ")); reqInspectorRules.setExample(""); apiDocRequest.addAttribute(reqInspectorRules); ApiDocAttribute reqInspectorRulesEnableFlag = new ApiDocAttribute(); reqInspectorRulesEnableFlag .setFieldName(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_RULES_ENABLE_FLAG); reqInspectorRulesEnableFlag .addFieldValues(new ApiDocFieldValue( "[boolean]", "Set to true for the service to validate each incoming request to ensure the appropriate parameters are being passed.")); reqInspectorRulesEnableFlag.setExample("true"); apiDocRequest.addAttribute(reqInspectorRulesEnableFlag); ApiDocAttribute reqScenarioId = new ApiDocAttribute(); reqScenarioId .setFieldName(ServiceConfigurationAPI.API_SERVICE_SCENARIO_ID); reqScenarioId.addFieldValues(new ApiDocFieldValue("[identifier]", "A valid service scenario identifier.")); reqScenarioId.setExample("123"); apiDocRequest.addAttribute(reqScenarioId); ApiDocAttribute reqScenarioName = new ApiDocAttribute(); reqScenarioName .setFieldName(ServiceConfigurationAPI.API_SERVICE_NAME); reqScenarioName.addFieldValues(new ApiDocFieldValue("[string]", "A valid service scenario name.")); reqScenarioName.setExample("My Service Scenario Name"); apiDocRequest.addAttribute(reqScenarioName); ApiDocAttribute reqHangtime = new ApiDocAttribute(); reqHangtime .setFieldName(ServiceConfigurationAPI.API_SERVICE_HANGTIME); reqHangtime.addFieldValues(new ApiDocFieldValue("[int]", "Hang time in milliseconds.")); reqHangtime.setExample("500"); apiDocRequest.addAttribute(reqHangtime); ApiDocAttribute transientSet = new ApiDocAttribute(); transientSet .setFieldName(ServiceConfigurationAPI.API_TRANSIENT_STATE); transientSet .addFieldValues(new ApiDocFieldValue( "[boolean]", "If available and set to 'true', then all settings in this call will be in-memory only, not persisted to the file system. Otherwise, state settings will be written to the file system.")); transientSet.setExample("true"); apiDocRequest.addAttribute(transientSet); ApiDocAttribute reqAttributeAction = new ApiDocAttribute(); reqAttributeAction .setFieldName(ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE); reqAttributeAction .addFieldValues(new ApiDocFieldValue( ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE_VALUE_DYNAMIC, "Sets service to respond as dynamic.")); reqAttributeAction .addFieldValues(new ApiDocFieldValue( ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE_VALUE_PROXY, "Sets service to act as a proxy.")); reqAttributeAction .addFieldValues(new ApiDocFieldValue( ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE_VALUE_STATIC, "Sets service to respond with a static response")); apiDocRequest.addAttribute(reqAttributeAction); apiDocService.setApiRequest(apiDocRequest); // ***************************** // RESPONSE DEFINITION // ***************************** ApiDocResponse apiResponse = new ApiDocResponse(); // Building a JSON RESPONSE example try { JSONObject jsonResponseObject = new JSONObject(); JSONObject jsonResultObject = new JSONObject(); jsonResultObject .put("success", "Some informative coaching message. If success isn't a value, then maybe you have a 'fail' message."); jsonResultObject.put(ServiceConfigurationAPI.API_SERVICE_ID, "1234"); jsonResultObject.put(ServiceConfigurationAPI.API_SERVICE_NAME, "Some service name"); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_SCHEMA, "JSON Schema"); jsonResultObject .put(ServiceConfigurationAPI.API_SERVICE_SCENARIO_ID, "5678"); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_SCENARIO_NAME, "Some scenario name"); jsonResultObject .put(ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE, ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE_VALUE_PROXY); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_HANGTIME, "500"); jsonResultObject.put( ServiceConfigurationAPI.API_TRANSIENT_STATE, "true"); jsonResponseObject.put("result", jsonResultObject); apiResponse.setExample(jsonResponseObject.toString()); } catch (Exception e) { log.error("Unabel to build a sample JSON message. ", e); } // Response attribute 'planId' ApiDocAttribute resAttributePlanId = new ApiDocAttribute(); resAttributePlanId .setFieldName(ServiceConfigurationAPI.API_SERVICE_ID); resAttributePlanId.setFieldDescription("Identifier of a Service"); apiResponse.addAttribute(resAttributePlanId); // Response attribute 'planName' ApiDocAttribute resAttributePlanName = new ApiDocAttribute(); resAttributePlanName .setFieldName(ServiceConfigurationAPI.API_SERVICE_NAME); resAttributePlanName.setFieldDescription("Name of a Service"); apiResponse.addAttribute(resAttributePlanName); // Response attribute 'success' ApiDocAttribute resAttributeSuccess = new ApiDocAttribute(); resAttributeSuccess.setFieldName("success"); resAttributeSuccess .setFieldDescription("Successfully set, deleted, or saved a plan. You get 'fail' or 'success', not both."); apiResponse.addAttribute(resAttributeSuccess); ApiDocAttribute resAttributeFail = new ApiDocAttribute(); resAttributeFail.setFieldName("fail"); resAttributeFail .setFieldDescription("Failed to set, delete, or save a plan. You get 'fail' or 'success', not both."); apiResponse.addAttribute(resAttributeFail); apiDocService.setApiResponse(apiResponse); apiStore.saveOrUpdateService(apiDocService); } } /** * */ public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String serviceId = req .getParameter(ServiceConfigurationAPI.API_SERVICE_ID); String serviceName = req .getParameter(ServiceConfigurationAPI.API_SERVICE_NAME); // SCHEMA String serviceResponseSchema = req .getParameter(ServiceConfigurationAPI.API_SERVICE_SCHEMA); String serviceResponseSchemaEnableFlag = req .getParameter(ServiceConfigurationAPI.API_SERVICE_SCHEMA_ENABLE_FLAG); // REQUEST Evaluations String reqInspectorRules = req .getParameter(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_RULES); String reqInspectorRulesEnableFlag = req .getParameter(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_RULES_ENABLE_FLAG); String hangTime = req .getParameter(ServiceConfigurationAPI.API_SERVICE_HANGTIME); String scenarioId = req .getParameter(ServiceConfigurationAPI.API_SERVICE_SCENARIO_ID); String scenarioName = req .getParameter(ServiceConfigurationAPI.API_SERVICE_SCENARIO_NAME); String requestInspectorName = req .getParameter(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_NAME); String serviceResponseType = req .getParameter(ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE); String defaultUrlIndex = req.getParameter("defaultUrlIndex"); String transientState = req .getParameter(ServiceConfigurationAPI.API_TRANSIENT_STATE); Service service = null; JSONObject jsonResultObject = new JSONObject(); if (serviceId != null) { service = store.getServiceById(new Long(serviceId)); } else { service = store.getServiceByName(serviceName); } try { service.setServiceResponseTypeByString(serviceResponseType); } catch (Exception e) { log.debug("Updating service without a 'service response type' value"); } try { int index = Integer.parseInt(defaultUrlIndex); service.setDefaultRealUrlIndex(index - 1); } catch (Exception e) { } try { if (requestInspectorName != null) { service.setRequestInspectorName(requestInspectorName); } } catch (Exception e) { log.debug("Updating service without a 'request Inspector Name' value although, one was given:" + requestInspectorName); } // SCHEMA try { if (serviceResponseSchemaEnableFlag != null) { service.setResponseSchemaFlag(Boolean .valueOf(serviceResponseSchemaEnableFlag)); } } catch (Exception e) { log.debug("Unable to set the Service JSON Schema enable flag. Non-null value given: " + serviceResponseSchemaEnableFlag); } try { if (serviceResponseSchema != null) { service.setResponseSchema(serviceResponseSchema); } } catch (Exception e) { // Do nothing. } // ****************************** // REQUEST Evaluation Rules // ****************************** try { if (reqInspectorRulesEnableFlag != null) { service.setRequestInspectorJsonRulesEnableFlag(Boolean .valueOf(reqInspectorRulesEnableFlag)); } } catch (Exception e) { log.debug("Unable to set the Service JSON Schema enable flag. Non-null value given: " + serviceResponseSchemaEnableFlag); } try { if (reqInspectorRules != null) { service.setRequestInspectorJsonRules(reqInspectorRules); } } catch (Exception e) { // TODO: we should add JSON Schema to evaluate the rules. Right? } try { if (hangTime != null) { service.setHangTime((new Integer(hangTime).intValue())); } } catch (Exception e) { log.debug("Updating service without a 'hang time' value"); } try { if (transientState != null) { service.setTransientState((new Boolean(transientState))); } } catch (Exception e) { log.debug("Updating service without a 'transient state' value"); } try { if (scenarioId != null) { service.setDefaultScenarioId(new Long(scenarioId)); } else { service.setDefaultScenarioByName(scenarioName); } } catch (Exception e) { // Do nothing. log.debug("Updating service without a 'default scenario ID' value"); } service = store.saveOrUpdateService(service); resp.setContentType("application/json"); PrintWriter out = resp.getWriter(); JSONObject jsonResponseObject = new JSONObject(); try { if (service != null) { jsonResultObject.put("success", "updated"); jsonResultObject.put(ServiceConfigurationAPI.API_SERVICE_NAME, service.getServiceName()); jsonResultObject.put(ServiceConfigurationAPI.API_SERVICE_ID, service.getId()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_SCENARIO_ID, service.getDefaultScenarioId()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_SCHEMA_ENABLE_FLAG, service.isResponseSchemaFlag()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_RULES_ENABLE_FLAG, service.isRequestInspectorJsonRulesEnableFlag()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_SCENARIO_NAME, service.getDefaultScenarioName()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE, service.getServiceResponseTypeAsString()); jsonResultObject.put( ServiceConfigurationAPI.API_SERVICE_HANGTIME, service.getHangTime()); jsonResultObject .put(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_NAME, service.getRequestInspectorName()); jsonResponseObject.put("result", jsonResultObject); } else { StringBuffer outputInfo = new StringBuffer(); outputInfo.append(ServiceConfigurationAPI.API_SERVICE_ID + ":" + serviceId + " "); outputInfo.append(ServiceConfigurationAPI.API_SERVICE_NAME + ":" + serviceName + " "); outputInfo.append(ServiceConfigurationAPI.API_SERVICE_HANGTIME + ":" + hangTime + " "); outputInfo .append(ServiceConfigurationAPI.API_SERVICE_REQUEST_INSPECTOR_NAME + ":" + requestInspectorName); outputInfo .append(ServiceConfigurationAPI.API_SERVICE_SCENARIO_ID + ":" + scenarioId + " "); outputInfo .append(ServiceConfigurationAPI.API_SERVICE_SCENARIO_NAME + ":" + scenarioName + " "); outputInfo .append(ServiceConfigurationAPI.API_SERVICE_RESPONSE_TYPE + ":" + serviceResponseType + " "); outputInfo.append("defaultUrlIndex" + ":" + defaultUrlIndex + " "); outputInfo.append(ServiceConfigurationAPI.API_TRANSIENT_STATE + ":" + transientState + " "); jsonResultObject.put("fail", "Unable to update service configuration. "); jsonResultObject.put("info", outputInfo.toString()); } out.println(jsonResponseObject.toString()); } catch (Exception e) { log.error("Unable to build a JSON response. ", e); try { jsonResultObject.put("fail", "Unable to configure service."); jsonResponseObject.put("result", jsonResultObject); out.println(jsonResponseObject.toString()); } catch (Exception ee) { log.error( "Unable to again build an informative error JSON message response.", e); } } out.flush(); out.close(); } }