/*
* 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)
* neil.cronin (neil AT rackle DOT com)
* lorin.kobashigawa (lkb AT kgawa DOT com)
* rob.meyer (rob AT bigdis 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.model;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;
import org.json.JSONException;
import org.json.JSONObject;
import com.mockey.ClientExecuteProxy;
import com.mockey.ClientExecuteProxyException;
import com.mockey.OrderedMap;
import com.mockey.plugin.RequestInspectorDefinedByJson;
import com.mockey.storage.IMockeyStorage;
import com.mockey.storage.StorageRegistry;
import com.mockey.ui.Util;
/**
* A Service is a remote url that can be called.
*
* @author chad.lafontaine
*
*/
public class Service extends StatusCheck implements PersistableItem, ExecutableService {
public final static int SERVICE_RESPONSE_TYPE_PROXY = 0;
public final static int SERVICE_RESPONSE_TYPE_STATIC_SCENARIO = 1;
public final static int SERVICE_RESPONSE_TYPE_DYNAMIC_SCENARIO = 2;
private Long id;
private String serviceName;
private String description;
private Boolean transientState = new Boolean(false);
private Long defaultScenarioId;
private int defaultRealUrlIndex = 0;
private Long errorScenarioId;
private int hangTime = 0;
private String requestInspectorName;
private String requestInspectorJsonRules = "";
private boolean requestInspectorJsonRulesEnableFlag = false;
private OrderedMap<Scenario> scenarios = new OrderedMap<Scenario>();
private int serviceResponseType = SERVICE_RESPONSE_TYPE_PROXY;
private String httpMethod = "GET";
private String url = "";
private String responseSchema = "";
private boolean responseSchemaFlag = false;
private List<FulfilledClientRequest> fulfilledRequests;
private List<Url> realServiceUrlList = new ArrayList<Url>();
private boolean allowRedirectFollow = true;
private static Log logger = LogFactory.getLog(Service.class);
private static IMockeyStorage store = StorageRegistry.MockeyStorage;
public List<FulfilledClientRequest> getFulfilledRequests() {
return fulfilledRequests;
}
public void setFulfilledRequests(List<FulfilledClientRequest> transactions) {
this.fulfilledRequests = transactions;
}
// default constructor for xml.
// DO NOT REMOVE. DO NOT CALL.
public Service() {
this.setServiceName("");
}
public String getHttpMethod() {
return httpMethod;
}
public void setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
}
public Long getDefaultScenarioId() {
return this.defaultScenarioId;
}
/**
*
* @return null if no default scenario defined, otherwise, returns name
*/
public String getDefaultScenarioName() {
Scenario s = this.getScenario(this.defaultScenarioId);
if (s != null) {
return s.getScenarioName();
} else {
return null;
}
}
public void setDefaultScenarioId(Long did) {
this.defaultScenarioId = did;
}
/**
* Finds a service scenario with matching name. If a match is found, then
* its ID is set as the default scenario id. If no match is found, then no
* change. Name matching is case insensitive, and leading and ending
* whitespace is trimmed.
*
* @param scenarioName
*/
public void setDefaultScenarioByName(String scenarioName) {
if (scenarioName != null) {
for (Scenario scenario : this.scenarios.getOrderedList()) {
if (scenarioName.trim().equalsIgnoreCase((scenario.getScenarioName().trim()))) {
this.setDefaultScenarioId(scenario.getId());
break;
}
}
}
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String name) {
this.serviceName = name;
}
public int getHangTime() {
return hangTime;
}
public void setHangTime(int hangTime) {
this.hangTime = hangTime;
}
public List<Scenario> getScenarios() {
return Util.orderAlphabeticallyByScenarioName(scenarios.getOrderedList());
}
public Scenario getScenario(Long scenarioId) {
return (Scenario) scenarios.get(scenarioId);
}
public void deleteScenario(Long scenarioId) {
this.scenarios.remove(scenarioId);
}
public Scenario saveOrUpdateScenario(Scenario scenario) {
scenario.setServiceId(this.id);
return (Scenario) this.scenarios.save(scenario);
}
/**
* DO NOT REMOVE. This is needed by XML reader and has a reference to the
* method signature via reflection. Thank Digester.
*
* @param realServiceUrl
* @deprecated - this method will call
* <code>saveOrUpdateRealServiceUrl(Url)</code>
* @see #saveOrUpdateRealServiceUrl(Url)
*/
public void setRealServiceUrlByString(String realServiceUrl) {
this.saveOrUpdateRealServiceUrl(new Url(realServiceUrl));
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("Service name:").append(this.getServiceName()).append("\n");
sb.append("Real URL(s):\n");
if (this.realServiceUrlList != null && !this.realServiceUrlList.isEmpty()) {
Iterator<Url> iter = this.realServiceUrlList.iterator();
while (iter.hasNext()) {
sb.append(iter.next() + "\n");
}
} else {
sb.append("(no real urls defined for this service)\n");
}
sb.append("Default scenario ID:").append(this.getDefaultScenarioId()).append("\n");
sb.append("Hang time:");
sb.append(this.getHangTime());
sb.append("\n");
return sb.toString();
}
public void setId(Long id) {
this.id = id;
// Recursively set this ID to child Scenarios, if any exist.
for (Scenario scenario : getScenarios()) {
scenario.setServiceId(this.id);
this.saveOrUpdateScenario(scenario);
}
}
public Long getId() {
return id;
}
/**
*
* @deprecated
* @see #getRealServiceUrls()
*/
public String getRealServiceUrl() {
return "[DEPRECATED]";
}
/**
*
* @param serviceResponseType
* - 0 (proxy), 1 (static), or 2 (dynamic). Any other value will
* default to PROXY.
*/
public void setServiceResponseType(int serviceResponseType) {
if (serviceResponseType == 1 || serviceResponseType == 0 || serviceResponseType == 2) {
this.serviceResponseType = serviceResponseType;
} else {
this.serviceResponseType = SERVICE_RESPONSE_TYPE_PROXY;
}
validateDefaultScenarioId();
}
// HELPER method - let's validate the 'defaultScenarioId'. If
// defaultScenarioId doesn't equal any of the scenario IDs, then
// auto-set the defaultID to the 'first' scenario
private void validateDefaultScenarioId() {
boolean valid = false;
List<Scenario> orderedList = this.scenarios.getOrderedList();
for (Scenario s : orderedList) {
if (s.getId().equals(this.getDefaultScenarioId())) {
valid = true;
break;
}
}
if (!valid) {
if (this.scenarios.getOrderedList().size() > 0) {
this.setDefaultScenarioId(orderedList.get(0).getId());
} else {
// Reset
this.setDefaultScenarioId(null);
}
}
}
/**
* Takes 'proxy', 'static', or 'dynamic' arguments and translates them to
* appropriate 'int' values and then calls
* <code>setServiceResponseType</code>
*
* @see #setServiceResponseType(int)
*/
public void setServiceResponseTypeByString(String arg) {
if (arg != null) {
if ("proxy".trim().equalsIgnoreCase(arg.trim()) || "0".equalsIgnoreCase(arg.trim())) {
setServiceResponseType(Service.SERVICE_RESPONSE_TYPE_PROXY);
} else if ("static".trim().equalsIgnoreCase(arg.trim()) || "1".equalsIgnoreCase(arg.trim())) {
setServiceResponseType(Service.SERVICE_RESPONSE_TYPE_STATIC_SCENARIO);
} else if ("dynamic".trim().equalsIgnoreCase(arg.trim()) || "2".equalsIgnoreCase(arg.trim())) {
setServiceResponseType(Service.SERVICE_RESPONSE_TYPE_DYNAMIC_SCENARIO);
}
}
}
public int getServiceResponseType() {
// If no scenarios, then proxy is automatically on.
if (this.getScenarios().size() == 0) {
return SERVICE_RESPONSE_TYPE_PROXY;
} else {
return serviceResponseType;
}
}
public String getServiceResponseTypeAsString() {
int x = getServiceResponseType();
if (x == Service.SERVICE_RESPONSE_TYPE_PROXY) {
return "proxy";
} else if (x == Service.SERVICE_RESPONSE_TYPE_STATIC_SCENARIO) {
return "static";
} else if (x == Service.SERVICE_RESPONSE_TYPE_DYNAMIC_SCENARIO) {
return "dynamic";
} else {
return "";
}
}
public void setErrorScenarioId(Long errorScenarioId) {
this.errorScenarioId = errorScenarioId;
}
public Long getErrorScenarioId() {
return errorScenarioId;
}
public Scenario getErrorScenario() {
// FIND SERVICE ERROR, IF EXIST.
for (Scenario scenario : this.getScenarios()) {
if (scenario.getId().equals(this.getErrorScenarioId())) {
return scenario;
}
}
// No service error defined, therefore, let's use the universal
// error.
return StorageRegistry.MockeyStorage.getUniversalErrorScenario();
}
public Boolean isReferencedInAServicePlan() {
Boolean isReferenced = false;
for (ServicePlan plan : StorageRegistry.MockeyStorage.getServicePlans()) {
for (PlanItem planItem : plan.getPlanItemList()) {
if (planItem.getServiceName().equals(this.getServiceName())) {
isReferenced = true;
break;
}
}
}
return isReferenced;
}
/**
* The core method to execute the request as either a Proxy, Dynamic, or
* Static Scenario.
*/
public ResponseFromService execute(RequestFromClient request, Url realServiceUrl) {
this.setLastVisit(new Long(Calendar.getInstance().getTimeInMillis()));
ResponseFromService response = null;
if (this.getServiceResponseType() == Service.SERVICE_RESPONSE_TYPE_PROXY) {
response = proxyTheRequest(request, realServiceUrl);
} else if (this.getServiceResponseType() == Service.SERVICE_RESPONSE_TYPE_DYNAMIC_SCENARIO) {
response = executeDynamicScenario(request, realServiceUrl);
} else if (this.getServiceResponseType() == Service.SERVICE_RESPONSE_TYPE_STATIC_SCENARIO) {
response = executeStaticScenario(realServiceUrl);
}
return response;
}
private ResponseFromService proxyTheRequest(RequestFromClient request, Url realServiceUrl) {
logger.debug("proxying a moxie.");
// If proxy on, then
// 1) Capture request message.
// 2) Set up a connection to the real service URL
// 3) Forward the request message to the real service URL
// 4) Read the reply from the real service URL.
// 5) Save request + response as a historical scenario.
// There are 2 proxy things going on here:
// 1. Using Mockey as a 'proxy' to a real service.
// 2. The proxy server between Mockey and the real service.
//
// For the proxy server between Mockey and the real service,
// we do the following:
ProxyServerModel proxyServer = store.getProxy();
ClientExecuteProxy clientExecuteProxy = ClientExecuteProxy.getClientExecuteProxyInstance();
ResponseFromService response = null;
// If Twisting is on, then
// 1)
try {
logger.debug("Initiating request through proxy");
response = clientExecuteProxy.execute(proxyServer, realServiceUrl, allowRedirectFollow, request);
response.setScenarioName("");
} catch (ClientExecuteProxyException e) {
// We're here for various reasons.
// 1) timeout from calling real service.
// 2) unable to parse real response.
// 3) magic!
// Before we throw an exception, check:
// (A) does this mock service have a default error response. If
// no, then
// (B) see if Mockey has a universal error response
// If neither, then throw the exception.
response = new ResponseFromService();
response.setRequestUrl(e.getRequestUrl());
Scenario error = this.getErrorScenario();
if (error != null) {
response.setBody(error.getResponseMessage());
} else {
StringBuffer msg = new StringBuffer();
JSONObject jsonResponseObject = new JSONObject();
try {
jsonResponseObject
.put("fail",
"We encountered an error. Here's some information to help point out what may have gone wrong.");
if (proxyServer != null && proxyServer.isProxyEnabled()) {
if (proxyServer.getProxyHost() != null && proxyServer.getProxyHost().trim().length() > 0) {
jsonResponseObject.put("proxyInfo", "Internet proxy settings are ENABLED pointing to -->"
+ proxyServer.getProxyHost() + "<-- ");
} else {
jsonResponseObject.put("proxyInfo",
"Internet proxy settings are ENABLED but Internet Proxy Server value is EMPTY.");
}
} else {
jsonResponseObject.put("proxyInfo", "Proxy settings are NOT ENABLED. ");
}
msg.append(jsonResponseObject.toString());
} catch (Exception ae) {
logger.error("Nothing is going right here.", ae);
msg.append("Experiencing some difficulties. ");
}
response.setBody(msg.toString());
}
}
response.setScenarioName("(No name; proxy response)");
return response;
}
private ResponseFromService executeStaticScenario(Url realServiceUrl) {
logger.debug("mockeying a static scenario");
// Proxy is NOT on. Therefore we use a scenario to figure out a reply.
// Either:
// 1) Based on matching the request message to one of the scenarios
// or
// 2) Based on scenario selected.
//
Scenario scenario = this.getScenario(this.getDefaultScenarioId());
ResponseFromService response = new ResponseFromService();
if (scenario != null) {
response.setScenarioName(scenario.getScenarioName());
response.setScenarioTagsAsString(scenario.getTag());
response.setBody(scenario.getResponseMessage());
response.setHttpResponseStatusCode(scenario.getHttpResponseStatusCode());
scenario.setLastVisit(new Long(Calendar.getInstance().getTimeInMillis()));
Map<String, String> headerInfo = scenario.getHeaderInfoHelper();
List<Header> headerList = new ArrayList<Header>();
for (String k : headerInfo.keySet()) {
headerList.add(new BasicHeader(k, headerInfo.get(k)));
}
response.setHeaders(headerList.toArray(new Header[headerList.size()]));
} else {
response.setBody("NO SCENARIO SELECTED");
}
response.setRequestUrl(realServiceUrl);
return response;
}
private ResponseFromService executeDynamicScenario(RequestFromClient request, Url realServiceUrl) {
// To make things a little easy, we will
// concatenate request Parameters and Body (if one was posted)
// into 1 long String argument, and then evaluate for
// the existence of a match string argument.
// In addition, if this Service's mock URL is a
// RESTful pattern, then we'll also try to extract the
// token ID from the 'realServiceUrl' based on this
// service's pattern.
// STEP 1. "Build the request String to evaluate"
logger.debug("mockeying a dynamic scenario.");
StringBuffer rawRequestDataBuffer = new StringBuffer();
// Optional REST token from the URL
Url mockUrl = new Url(this.getUrl());
// Example: "http://example.com/hotels/{hotel}/bookings/{room}"
UriTemplate template = new UriTemplate(mockUrl.getFullUrl());
// Example: "http://example.com/hotels/1/bookings/42"
@SuppressWarnings("rawtypes")
Map restTokenResults = template.match(realServiceUrl.getFullUrl());
@SuppressWarnings("unchecked")
Iterator<String> tokenKeyIterator = restTokenResults.keySet().iterator();
while (tokenKeyIterator.hasNext()) {
String key = tokenKeyIterator.next();
rawRequestDataBuffer.append(restTokenResults.get(key));
}
// Optional parameters and body
try {
rawRequestDataBuffer.append(request.buildParameterRequest());
if (request.hasPostBody()) {
rawRequestDataBuffer.append(request.getBodyInfo());
}
} catch (UnsupportedEncodingException e) {
logger.debug("Unable to extract content from request", e);
}
String rawRequestData = "";
try {
rawRequestData = URLDecoder.decode(rawRequestDataBuffer.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("Unable to URL un-encode (or decode) the following: \n " + rawRequestDataBuffer.toString(), e);
}
// STEP 2. "We iterate through each Service Scenario and evaluate"
// TOPIC: 'Scenario argument matching'
// ===================================
// A few things to note on this logic loop. Let's say the incoming
// request has 'ABC123' in the request and we have Scenario A with
// match argument '123' and Scenario B with match argument 'ABC123'.
// The goal is to return Scenario B, in this case because there's a
// match with 'ABC123'. Unfortunately, Scenario A works too, because
// 'ABC123' contains '123'. Which should be returned?
// In this particular case, we want to return Scenario B, because it
// has the-longest-string match value, i.e. 6 character match vs.
// 3 characters.
ResponseFromService response = new ResponseFromService();
List<Scenario> scenarios = this.getScenarios();
Iterator<Scenario> iter = scenarios.iterator();
String messageMatchFound = null;
int httpResponseStatus = -1;
int matchArgLength = -1;
Scenario bestMatchedScenario = null;
// We must visit ALL Scenarios, without any short circuits.
while (iter.hasNext()) {
Scenario scenario = iter.next();
logger.debug("Checking: '" + scenario.getMatchStringArg() + "' in Scenario message: \n" + rawRequestData);
int indexValue = -1;
int tempTotalRuleSuccessfulEvaluationCount = -1;
// For RESTful support of VERB (method type), we check if a Scenario
// value is set, and if so, it matches incoming request method type.
// All TYPES will be allowed if Scenario's method type is 'empty', 'null', or '*' (wildcard).
// Otherwise, ONLY a matching type will be looked at.
String incomingRequestMethod = request.getMethod();
if (scenario.getHttpMethodType() == null || scenario.getHttpMethodType().trim().length() == 0
|| "*".equals(scenario.getHttpMethodType().trim())
|| scenario.getHttpMethodType().trim().equalsIgnoreCase(incomingRequestMethod)) {
if (scenario.hasMatchArgument()) {
if (scenario.isMatchStringArgEvaluationRulesFlag()) {
try {
RequestInspectorDefinedByJson jsonRulesInspector = new RequestInspectorDefinedByJson(
scenario.getMatchStringArg());
jsonRulesInspector.analyze(request);
if (jsonRulesInspector.hasAnySuccessForAtLeastOneRuleType()) {
// No errors, so we have a match.
indexValue = 1;
// Capture the number of _valid_ rules successfully processed.
tempTotalRuleSuccessfulEvaluationCount = jsonRulesInspector.getValidRuleCount();
} else {
logger.debug("No match. Reason: " + jsonRulesInspector.getPostAnalyzeResultMessage());
}
} catch (JSONException e) {
String msg = "Unable to parse JSON rules from scenario: " + scenario.getScenarioName();
logger.debug(msg, e);
// Unable to interpret this, so we assume
// no match
}
} else {
// Case insensitive
tempTotalRuleSuccessfulEvaluationCount = scenario.getMatchStringArg().trim().length();
indexValue = rawRequestData.toLowerCase().indexOf(scenario.getMatchStringArg().toLowerCase());
}
}
// OK, we have found a match-argument that is in the REQUEST,
// via 'indexValue > -1' but is it the longest matching argument
// via 'tempArgLength > matchArgLength'?
if ((indexValue > -1) && tempTotalRuleSuccessfulEvaluationCount > matchArgLength) {
matchArgLength = tempTotalRuleSuccessfulEvaluationCount;
bestMatchedScenario = scenario;
}
}
}
if (bestMatchedScenario != null) {
logger.debug("FOUND - matching '" + bestMatchedScenario.getMatchStringArg() + "' ");
messageMatchFound = bestMatchedScenario.getResponseMessage();
httpResponseStatus = bestMatchedScenario.getHttpResponseStatusCode();
// SET RULE_FOR_HEADERS
Map<String, String> headerInfo = bestMatchedScenario.getHeaderInfoHelper();
List<Header> headerList = new ArrayList<Header>();
for (String k : headerInfo.keySet()) {
headerList.add(new BasicHeader(k, headerInfo.get(k)));
}
response.setScenarioName(bestMatchedScenario.getScenarioName());
response.setHeaders(headerList.toArray(new Header[headerList.size()]));
response.setScenarioName(bestMatchedScenario.getScenarioName());
response.setScenarioTagsAsString(bestMatchedScenario.getTag());
}
// If we have no matches. Error handling is as follows:
// 1) Does service have a default service error defined? If yes, return
// message. If no...
// 2) Does Mockey have a universal error message defined? If yes,
// return, otherwise...
// 3) Return a error message.
if (messageMatchFound == null) {
response.setScenarioName("(No matching scenario)");
Scenario u = getErrorScenario();
if (u == null) {
u = store.getUniversalErrorScenario();
}
if (u != null) {
messageMatchFound = u.getResponseMessage();
httpResponseStatus = u.getHttpResponseStatusCode();
response.setScenarioName(u.getScenarioName());
response.setScenarioTagsAsString(u.getTag());
} else {
messageMatchFound = "Yikes, no love for you! Why? Well, it could be that this service setting "
+ "is set to Dynamic but there is no found matching scenario, nor is there a default "
+ "service-scenario-error defined, nor is there a universal-scenario-error defined "
+ "for this incoming request. In otherwords, Mockey doesn't know what to do.";
}
}
response.setRequestUrl(realServiceUrl);
response.setBody(messageMatchFound);
response.setHttpResponseStatusCode(httpResponseStatus);
return response;
}
private String getNiceNameForService(String arg) {
String name = arg;
// Remove parameters
int index = arg.indexOf("?");
if (index > 0) {
arg = arg.substring(0, index);
}
StringTokenizer st = new StringTokenizer(arg, "/");
while (st.hasMoreTokens()) {
// Eventually, we get the last token, and
// we use it as the name.
name = st.nextToken();
}
name = name + " (auto generated)";
return name;
}
public List<Url> getRealServiceUrls() {
return realServiceUrlList;
}
public void clearRealServiceUrls() {
realServiceUrlList = new ArrayList<Url>();
}
/**
*
* @param url
*/
public void saveOrUpdateRealServiceUrl(Url url) {
if (url != null) {
boolean found = this.hasRealServiceUrl(url);
if (!found && !url.getFullUrl().trim().isEmpty()) {
this.realServiceUrlList.add(url);
}
// BONUS
// If this service name is undefined, then we try to determine
// an informative name based on the url
if (this.serviceName != null && this.serviceName.trim().isEmpty()) {
this.setServiceName(this.getNiceNameForService(url.getFullUrl()));
}
}
}
/**
*
* @param otherService
* @return non null if _this_ and otherService both have non-empty list of
* <code>Url</code> objects with a matching <code>Url</code> object.
* Otherwise, returns false;
*/
public Url getFirstMatchingRealServiceUrl(Service otherService) {
Url matchUrl = null;
if (this.realServiceUrlList != null && otherService != null && !otherService.getRealServiceUrls().isEmpty()) {
for (Url otherUrl : otherService.getRealServiceUrls()) {
if (this.hasRealServiceUrl(otherUrl)) {
matchUrl = otherUrl;
break;
}
}
}
return matchUrl;
}
public boolean hasRealServiceUrl(Url url) {
boolean has = false;
try {
for (Url urlTmp : this.realServiceUrlList) {
if (urlTmp.getFullUrl().trim().equalsIgnoreCase(url.getFullUrl())) {
has = true;
break;
}
}
} catch (Exception e) {
// do nothing
}
return has;
}
/**
*
* @param url
* @see #getUrl()
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Mock URL. It's possible that this URL looks like one of the Real URLs.
* But, this value can be anything, but should be unique in the list of
* Services.
*
* @return
*/
public String getUrl() {
return url;
}
public int getDefaultRealUrlIndex() {
return this.defaultRealUrlIndex;
}
public void setDefaultRealUrlIndex(int i) {
this.defaultRealUrlIndex = i;
}
public Url getDefaultRealUrl() {
Url d = null;
try {
d = this.realServiceUrlList.get(this.defaultRealUrlIndex);
} catch (Exception e) {
// OK, let's try and be smart.
// Reset index.
this.defaultRealUrlIndex = 0;
if (!this.realServiceUrlList.isEmpty()) {
d = this.realServiceUrlList.get(0);
}
}
return d;
}
public void setTransientState(Boolean transientState) {
this.transientState = transientState;
}
public Boolean getTransientState() {
return transientState;
}
public boolean hasTag(String tag) {
boolean has = super.hasTag(tag);
if (!has) {
// Check scenarios...
for (Scenario s : this.getScenarios()) {
has = s.hasTag(tag);
if (has) {
break;
}
}
}
return has;
}
/**
*
* @return the Class Name of the Java class responsible for validation of
* all incoming requests.
* @see com.mockey.plugin.IRequestInspector
*/
public String getRequestInspectorName() {
return requestInspectorName;
}
public void setRequestInspectorName(String requestInspectorName) {
this.requestInspectorName = requestInspectorName;
}
/**
*
* @return request inspection/validation rules defined in JSON format.
*/
public String getRequestInspectorJsonRules() {
return requestInspectorJsonRules;
}
/**
*
* @param requestInspectorJsonRules
* can be null or empty or invalid JSON format to be able display
* to customers invalid input. Validation will be done elsewhere.
* If not null, argument will be trimmed prior to being set.
*/
public void setRequestInspectorJsonRules(String _requestInspectorJsonRules) {
if (_requestInspectorJsonRules != null) {
this.requestInspectorJsonRules = _requestInspectorJsonRules;
} else {
this.requestInspectorJsonRules = _requestInspectorJsonRules;
}
}
/**
*
* @return true if the request inspector's JSON rules should be processed
* per request.
*/
public boolean isRequestInspectorJsonRulesEnableFlag() {
return requestInspectorJsonRulesEnableFlag;
}
public void setRequestInspectorJsonRulesEnableFlag(boolean requestInspectorJsonRulesEnableFlag) {
this.requestInspectorJsonRulesEnableFlag = requestInspectorJsonRulesEnableFlag;
}
/**
* Response schema is used to validate a Service's Scenario format, to help
* developers quickly find out if their Service Scenario(s) are invalid.
*
* @return a string representing a schema.
*/
public String getResponseSchema() {
return responseSchema;
}
/**
*
* @param responseSchema
*/
public void setResponseSchema(String responseSchema) {
this.responseSchema = responseSchema;
}
public boolean isResponseSchemaFlag() {
return responseSchemaFlag;
}
public void setResponseSchemaFlag(boolean responseSchemaFlag) {
this.responseSchemaFlag = responseSchemaFlag;
}
}