Java tutorial
/* * 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-2013 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. * */ /* * 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.plugin; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.mockey.model.RequestFromClient; /** * Given a JSON string containing rules to validate the HTTP request, this will * provide the logic to inspect and validate HTTP request and build an * informative message for results. A few things to note: * * All rules per TYPE will be treated as 'AND'. For example, all key/value * pairs in 'parameters' must exist. * * All rules between TYPEs will be treated as 'OR'. For example, all key/value * pair rules must be TRUE in 'parameters' OR all key/value rules must be true * for 'headers'. * * * <pre> * { * "parameters": [ * { * "key": "ticker", * "desc": "A value must be provided with the 'ticker' parameter, and it must contain the letter 'g'. Providing 'GOOG' is valid, but 'FB' will flag an error.", * "value_rule_arg": "g", * "value_rule_type": "string_required" * }, * { * "key": "date", * "desc": "Optional date value, but if provided, must satisfy mm/DD/yyyy format.", * "value_rule_arg": "^(((0[1-9]|[12]\\d|3[01])\\/(0[13578]|1[02])\\/((19|[2-9]\\d)\\d{2}))|((0[1-9]|[12]\\d|30)\\/(0[13456789]|1[012])\\/((19|[2-9]\\d)\\d{2}))|((0[1-9]|1\\d|2[0-8])\\/02\\/((19|[2-9]\\d)\\d{2}))|(29\\/02\\/((1[6-9]|[2-9]\\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$", * "value_rule_type": "regex_optional" * } * ], * "headers": [ * { * "key": "page_id", * "desc": "A page_id value MUST be provided, any non-empty string value.", * "value_rule_arg": "", * "value_rule_type": "string_required" * } * ], * "body": [ * { * "desc": "The text 'username' is required to be present in the POST body.", * "value_rule_arg": "username", * "value_rule_type": "string_required" * } * ], * "url": [ * { * "desc": "The value '123' is required to be present in the RESTful URL.", * "value_rule_arg": "\\b123\\b", * "value_rule_type": "regex_required" * } * ] * } * </pre> * * @author chadlafontaine * */ public class RequestInspectorDefinedByJson implements IRequestInspector { private JSONObject json = null; private Logger logger = Logger.getLogger(RequestInspectorDefinedByJson.class); private Map<RequestRuleType, List<String>> errorMapByKey = new HashMap<RequestRuleType, List<String>>(); private Map<RequestRuleType, Boolean> rulesDefinedForType = new HashMap<RequestRuleType, Boolean>(); private int totalRuleCount = 0; private int validRuleCount = 0; /** * * @param json * - request inspection rules * @throws JSONException */ public RequestInspectorDefinedByJson(String json) throws JSONException { this.json = new JSONObject(json); } /** * * @return the number of rules processed post analysis. */ public int getTotalRuleCount() { return this.totalRuleCount; } /** * * @return the number of rules that had a positive outcome. */ public int getValidRuleCount() { return this.validRuleCount; } /** * Will apply request inspection rules as defined in JSON, only looking at * parameters and headers, not Body. * * @param request * - HTTP request to analyze. */ public void analyze(RequestFromClient request) { // Since we apply the same evaluation logic to parameters and headers, // we'll move the key-value pairs into a Map, and process the rules // accordingly. // ************* // Parameters // ************* analyze(RequestRuleType.RULE_TYPE_FOR_PARAMETERS, request.getParameters()); // ************* // Headers // ************* analyze(RequestRuleType.RULE_TYPE_FOR_HEADERS, request.getHeaderInfoAsMap()); // ************* // RULE_FOR_BODY ?? // ************* Map<String, String[]> postBodyMap = new HashMap<String, String[]>(); if (request.hasPostBody()) { postBodyMap.put(RequestRuleType.RULE_TYPE_FOR_BODY.toString(), new String[] { request.getBodyInfo() }); } analyze(RequestRuleType.RULE_TYPE_FOR_BODY, postBodyMap); // ************* // Url // ************* Map<String, String[]> urlMap = new HashMap<String, String[]>(); urlMap.put(RequestRuleType.RULE_TYPE_FOR_URL.toString(), new String[] { request.getRequestURL() }); analyze(RequestRuleType.RULE_TYPE_FOR_URL, urlMap); } /** * Based on type, method will extra validation rules and evaluate the * keyValues mapping. * * @param type * - Valid values are RULE_FOR_HEADERS or RULE_FOR_PARAMETERS * @param keyValues * - An array of possible values associated to a key * @see #RULE_FOR_HEADERS * @see #RULE_FOR_PARAMETERS */ private void analyze(RequestRuleType ruleType, Map<String, String[]> keyValues) { // Validate parameters. try { // FOR PARAMETERs and HEADERs JSONArray parameterArray = this.json.getJSONArray(ruleType.toString()); for (int i = 0; i < parameterArray.length(); i++) { JSONObject jsonRule = parameterArray.getJSONObject(i); this.totalRuleCount++; try { RequestRule requestRule = new RequestRule(jsonRule, ruleType); this.rulesDefinedForType.put(ruleType, new Boolean(true)); if (RequestRuleType.RULE_TYPE_FOR_BODY.equals(ruleType)) { String[] values = keyValues.get(RequestRuleType.RULE_TYPE_FOR_BODY.toString()); if (requestRule.evaluate(values)) { addErrorMessage(ruleType, requestRule); } else { this.validRuleCount++; } } else if (RequestRuleType.RULE_TYPE_FOR_URL.equals(ruleType)) { String[] values = keyValues.get(RequestRuleType.RULE_TYPE_FOR_URL.toString()); if (requestRule.evaluate(values)) { addErrorMessage(ruleType, requestRule); } else { this.validRuleCount++; } } else { // For HEADERS and PARAMETERS if (requestRule.getKey() != null && requestRule.getKey().contains("*")) { // We treat this as a wild-card. Iterator<String> allKeys = keyValues.keySet().iterator(); List<String> allValues = new ArrayList<String>(); while (allKeys.hasNext()) { String key = allKeys.next(); String[] vals = keyValues.get(key); for (String v : vals) { allValues.add(v); } } // Get ALL values from all parameters, and evaluate. if (requestRule.evaluate(allValues.toArray(new String[allValues.size()]))) { addErrorMessage(ruleType, requestRule); } else { this.validRuleCount++; } } else { // We have non-null, and non-empty keys. // Apply specific rules. // Keys in RULES and INCOMING maps should // be case insensitive! Iterator<String> allKeys = keyValues.keySet().iterator(); while (allKeys.hasNext()) { String key = allKeys.next(); if (key.equalsIgnoreCase(requestRule.getKey())) { String[] values = keyValues.get(key); if (requestRule.evaluate(values)) { addErrorMessage(ruleType, requestRule); } else { this.validRuleCount++; } } } } } } catch (RequestRuleException e) { addErrorMessage(ruleType, "Invalid JSON rule setup. " + e.getMessage()); } } } catch (JSONException e) { // Not necessarily an error. Could be missing logger.debug( "Request Inspection JSON rules does not have rule defined for '" + ruleType.toString() + "'"); } } /** * * @param type * @param error */ private void addErrorMessage(RequestRuleType type, RequestRule requestRule) { List<String> errorListByKeyType = this.errorMapByKey.get(type); if (errorListByKeyType == null) { errorListByKeyType = new ArrayList<String>(); } // Build StringBuilder sb = new StringBuilder(); sb.append("ISSUE: Rule of type '" + type + "'. "); if (!RequestRuleType.RULE_TYPE_FOR_BODY.equals(type) && !RequestRuleType.RULE_TYPE_FOR_URL.equals(type)) { sb.append(" Belonging to key name of '" + requestRule.getKey() + "'. "); } for (String issue : requestRule.getIssues()) { sb.append(issue + " "); } errorListByKeyType.add(sb.toString() + " RULE DESC: " + requestRule.getDesc()); this.errorMapByKey.put(type, errorListByKeyType); } /** * * @param type * @param error */ private void addErrorMessage(RequestRuleType type, String msg) { List<String> errorListByKeyType = this.errorMapByKey.get(type); if (errorListByKeyType == null) { errorListByKeyType = new ArrayList<String>(); } errorListByKeyType.add(msg); this.errorMapByKey.put(type, errorListByKeyType); } // /** // * Method should be called post analysis. // * // * @return true if one or more errors exist regardless of type. // */ // public boolean hasErrors_() { // if (this.errorMapByKey.isEmpty()) { // return false; // } else { // return true; // } // } /** * * @return true if ALL rules pass for PARAMETERS or BODY or HEADERS or URL */ public boolean hasAnySuccessForAtLeastOneRuleType() { boolean success = false; Iterator<RequestRuleType> iter = rulesDefinedForType.keySet().iterator(); while (iter.hasNext()) { RequestRuleType type = iter.next(); List<String> errors = this.errorMapByKey.get(type); if (errors == null || errors.size() == 0) { success = true; break; } } return success; } /** * If errors exists, this method will build 1 long string representation of * all broken rules, inserting a counter i.e. 1, 2, 3, etc. in front of each * message. * * @return the result of the validate rules, can be an empty string, but * never null. */ public String getPostAnalyzeResultMessage() { StringBuffer sb = new StringBuffer(); int i = 1; for (RequestRuleType key : this.errorMapByKey.keySet()) { for (String value : this.errorMapByKey.get(key)) { sb.append(i++ + ") " + value + " \n"); } } return sb.toString(); } @Override public boolean isGlobal() { // TODO Auto-generated method stub return false; } public static void main_(String[] args) { // String valueRuleArg = // "^((1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])/(?:[0-9]{2})?[0-9]{2})?$"; // String value = "10/23/1972"; // try { // // Pattern pattern = Pattern.compile(valueRuleArg); // Matcher matcher = pattern.matcher(value); // if (!matcher.find()) { // System.out.println("No match"); // } else { // System.out.println("yes, Match"); // } // } catch (Exception e) { // e.printStackTrace(); // } //Map<String, String[]> test = new HashMap<String, String[]>(); } }