/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.support;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.BaseMockResponseConfig;
import com.eviware.soapui.config.CompressedStringConfig;
import com.eviware.soapui.config.HeaderConfig;
import com.eviware.soapui.impl.wsdl.AbstractWsdlModelItem;
import com.eviware.soapui.impl.wsdl.MutableWsdlAttachmentContainer;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.mock.DispatchException;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockResponse;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunContext;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.AttachmentUtils;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.MimeMessageMockResponseEntity;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.MockResponseDataSource;
import com.eviware.soapui.impl.wsdl.support.CompressedStringSupport;
import com.eviware.soapui.impl.wsdl.support.CompressionSupport;
import com.eviware.soapui.impl.wsdl.support.MapTestPropertyHolder;
import com.eviware.soapui.impl.wsdl.support.MessageXmlObject;
import com.eviware.soapui.impl.wsdl.support.MessageXmlPart;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.TestPropertyHolder;
import com.eviware.soapui.model.iface.Operation;
import com.eviware.soapui.model.mock.MockOperation;
import com.eviware.soapui.model.mock.MockRequest;
import com.eviware.soapui.model.mock.MockResponse;
import com.eviware.soapui.model.mock.MockResult;
import com.eviware.soapui.model.mock.MockRunContext;
import com.eviware.soapui.model.mock.MockRunner;
import com.eviware.soapui.model.mock.MockService;
import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContainer;
import com.eviware.soapui.model.testsuite.TestProperty;
import com.eviware.soapui.model.testsuite.TestPropertyListener;
import com.eviware.soapui.settings.CommonSettings;
import com.eviware.soapui.support.scripting.ScriptEnginePool;
import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
import com.eviware.soapui.support.types.StringToStringMap;
import com.eviware.soapui.support.types.StringToStringsMap;
import com.eviware.soapui.support.xml.XmlUtils;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.ws.security.WSSecurityException;
import javax.activation.DataHandler;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.PreencodedMimeBodyPart;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public abstract class AbstractMockResponse<MockResponseConfigType extends BaseMockResponseConfig>
extends AbstractWsdlModelItem<MockResponseConfigType>
implements MockResponse, MutableWsdlAttachmentContainer, PropertyExpansionContainer, TestPropertyHolder {
public static final String AUTO_RESPONSE_COMPRESSION = "<auto>";
public static final String NO_RESPONSE_COMPRESSION = "<none>";
private MapTestPropertyHolder propertyHolder;
private String responseContent;
private MockResult mockResult;
private ScriptEnginePool scriptEnginePool;
public AbstractMockResponse(MockResponseConfigType config, MockOperation operation, String icon) {
super(config, operation, icon);
scriptEnginePool = new ScriptEnginePool(this);
scriptEnginePool.setScript(getScript());
propertyHolder = new MapTestPropertyHolder(this);
propertyHolder.addProperty("Request");
if (!config.isSetHttpResponseStatus()) {
config.setHttpResponseStatus("" + HttpStatus.SC_OK);
}
}
@Override
public void setConfig(MockResponseConfigType config) {
super.setConfig(config);
if (scriptEnginePool != null) {
scriptEnginePool.setScript(getScript());
}
}
public String getResponseContent() {
if (getConfig().getResponseContent() == null) {
getConfig().addNewResponseContent();
}
if (responseContent == null) {
responseContent = CompressedStringSupport.getString(getConfig().getResponseContent());
}
return responseContent;
}
public void setResponseContent(String responseContent) {
String oldContent = getResponseContent();
if (responseContent != null && responseContent.equals(oldContent)) {
return;
}
this.responseContent = responseContent;
setConfigResponseContent(responseContent);
notifyPropertyChanged(RESPONSE_CONTENT_PROPERTY, oldContent, responseContent);
}
private void setConfigResponseContent(String responseContent) {
CompressedStringConfig compressedResponseContent = CompressedStringConfig.Factory.newInstance();
compressedResponseContent.setStringValue(responseContent);
getConfig().setResponseContent(compressedResponseContent);
}
public StringToStringsMap getResponseHeaders() {
StringToStringsMap result = new StringToStringsMap();
List<HeaderConfig> headerList = getConfig().getHeaderList();
for (HeaderConfig header : headerList) {
result.add(header.getName(), header.getValue());
}
return result;
}
public void setResponseHttpStatus(int httpStatus) {
getConfig().setHttpResponseStatus("" + httpStatus);
}
public int getResponseHttpStatus() {
if (getConfig().getHttpResponseStatus() != null) {
return Integer.valueOf(getConfig().getHttpResponseStatus());
} else {
return HttpStatus.SC_OK;
}
}
public String getResponseCompression() {
if (getConfig().isSetCompression()) {
return getConfig().getCompression();
} else {
return AUTO_RESPONSE_COMPRESSION;
}
}
public void setMockResult(MockResult mockResult) {
MockResult oldResult = this.mockResult;
this.mockResult = mockResult;
notifyPropertyChanged(mockresultProperty(), oldResult, mockResult);
}
public MockResult getMockResult() {
return mockResult;
}
protected abstract String mockresultProperty();
public String getScript() {
return getConfig().isSetScript() ? getConfig().getScript().getStringValue() : null;
}
public void evaluateScript(MockRequest request) throws Exception {
String script = getScript();
if (script == null || script.trim().length() == 0) {
return;
}
MockService mockService = getMockOperation().getMockService();
MockRunner mockRunner = mockService.getMockRunner();
MockRunContext context =
mockRunner == null ? new WsdlMockRunContext(mockService, null) : mockRunner.getMockContext();
context.setMockResponse(this);
SoapUIScriptEngine scriptEngine = scriptEnginePool.getScriptEngine();
try {
scriptEngine.setVariable("context", context);
scriptEngine.setVariable("requestContext", request == null ? null : request.getRequestContext());
scriptEngine.setVariable("mockContext", context);
scriptEngine.setVariable("mockRequest", request);
scriptEngine.setVariable("mockResponse", this);
scriptEngine.setVariable("log", SoapUI.ensureGroovyLog());
scriptEngine.run();
} catch (RuntimeException e) {
throw new Exception(e.getMessage(), e);
} finally {
scriptEnginePool.returnScriptEngine(scriptEngine);
}
}
public void setScript(String script) {
String oldScript = getScript();
if (!script.equals(oldScript)) {
if (!getConfig().isSetScript()) {
getConfig().addNewScript();
}
getConfig().getScript().setStringValue(script);
scriptEnginePool.setScript(script);
}
}
@Override
public void release() {
super.release();
scriptEnginePool.release();
}
public MockResult execute(MockRequest request, MockResult result) throws DispatchException {
try {
getProperty("Request").setValue(request.getRequestContent());
long delay = getResponseDelay();
if (delay > 0) {
Thread.sleep(delay);
}
String script = getScript();
if (script != null && script.trim().length() > 0) {
evaluateScript(request);
}
String responseContent = getResponseContent();
// create merged context
WsdlMockRunContext context = new WsdlMockRunContext(request.getContext().getMockService(), null);
context.setMockResponse(this);
// casting below cause WsdlMockRunContext is both a MockRunContext AND a Map<String,Object>
context.putAll((WsdlMockRunContext) request.getContext());
context.putAll((WsdlMockRunContext) request.getRequestContext());
StringToStringsMap responseHeaders = getResponseHeaders();
for (Map.Entry<String, List<String>> headerEntry : responseHeaders.entrySet()) {
for (String value : headerEntry.getValue()) {
result.addHeader(headerEntry.getKey(), PropertyExpander.expandProperties(context, value));
}
}
responseContent = PropertyExpander.expandProperties(context, responseContent, isEntitizeProperties());
responseContent = executeSpecifics(request, responseContent, context);
if (!result.isCommitted()) {
responseContent = writeResponse(result, responseContent);
}
result.setResponseContent(responseContent);
setMockResult(result);
return result;
} catch (Throwable e) {
SoapUI.logError(e);
throw new DispatchException(e);
}
}
public String writeResponse(MockResult result, String responseContent) throws Exception {
MimeMultipart mp = null;
Operation operation = getMockOperation().getOperation();
// variables needed for both multipart sections....
boolean isXOP = isMtomEnabled() && isForceMtom();
StringToStringMap contentIds = new StringToStringMap();
// only support multipart for wsdl currently.....
if (operation instanceof WsdlOperation) {
if (operation == null) {
throw new IllegalStateException("Missing WsdlOperation for mock response");
}
// preprocess only if neccessary
if (isMtomEnabled() || isInlineFilesEnabled() || getAttachmentCount() > 0) {
try {
mp = new MimeMultipart();
WsdlOperation wsdlOperation = ((WsdlOperation) operation);
MessageXmlObject requestXmlObject = createMessageXmlObject(responseContent, wsdlOperation);
MessageXmlPart[] requestParts = requestXmlObject.getMessageParts();
for (MessageXmlPart requestPart : requestParts) {
if (prepareMessagePart(mp, contentIds, requestPart)) {
isXOP = true;
}
}
responseContent = requestXmlObject.getMessageContent();
} catch (Exception e) {
SoapUI.logError(e);
}
}
responseContent = removeEmptyContent(responseContent);
}
if (isStripWhitespaces()) {
responseContent = XmlUtils.stripWhitespaces(responseContent);
}
MockRequest request = result.getMockRequest();
request.getHttpResponse().setStatus(this.getResponseHttpStatus());
ByteArrayOutputStream outData = new ByteArrayOutputStream();
// non-multipart request?
String responseCompression = getResponseCompression();
if (!isXOP && (mp == null || mp.getCount() == 0) && getAttachmentCount() == 0) {
String encoding = getEncoding();
if (responseContent == null) {
responseContent = "";
}
byte[] content = encoding == null ? responseContent.getBytes() : responseContent.getBytes(encoding);
if (!result.getResponseHeaders().containsKeyIgnoreCase("Content-Type")) {
result.setContentType(getContentType());
}
String acceptEncoding = result.getMockRequest().getRequestHeaders().get("Accept-Encoding", "");
if (AUTO_RESPONSE_COMPRESSION.equals(responseCompression) && acceptEncoding != null
&& acceptEncoding.toUpperCase().contains("GZIP")) {
if (!headerExists("Content-Encoding", "gzip", result)) {
result.addHeader("Content-Encoding", "gzip");
}
outData.write(CompressionSupport.compress(CompressionSupport.ALG_GZIP, content));
} else if (AUTO_RESPONSE_COMPRESSION.equals(responseCompression) && acceptEncoding != null
&& acceptEncoding.toUpperCase().contains("DEFLATE")) {
result.addHeader("Content-Encoding", "deflate");
outData.write(CompressionSupport.compress(CompressionSupport.ALG_DEFLATE, content));
} else {
outData.write(content);
}
} else // won't get here if rest at the moment...
{
// make sure..
if (mp == null) {
mp = new MimeMultipart();
}
// init root part
initRootPart(responseContent, mp, isXOP);
// init mimeparts
AttachmentUtils.addMimeParts(this, Arrays.asList(getAttachments()), mp, contentIds);
// create request message
MimeMessage message = new MimeMessage(AttachmentUtils.JAVAMAIL_SESSION);
message.setContent(mp);
message.saveChanges();
MimeMessageMockResponseEntity mimeMessageRequestEntity
= new MimeMessageMockResponseEntity(message, isXOP, this);
result.addHeader("Content-Type", mimeMessageRequestEntity.getContentType().getValue());
result.addHeader("MIME-Version", "1.0");
mimeMessageRequestEntity.writeTo(outData);
}
if (outData.size() > 0) {
byte[] data = outData.toByteArray();
if (responseCompression.equals(CompressionSupport.ALG_DEFLATE)
|| responseCompression.equals(CompressionSupport.ALG_GZIP)) {
result.addHeader("Content-Encoding", responseCompression);
data = CompressionSupport.compress(responseCompression, data);
}
if (result.getResponseHeaders().get("Transfer-Encoding") == null) {
result.addHeader("Content-Length", "" + data.length);
}
result.writeRawResponseData(data);
}
return responseContent;
}
private boolean headerExists(String headerName, String headerValue, MockResult result) {
StringToStringsMap resultResponseHeaders = result.getResponseHeaders();
if (resultResponseHeaders.containsKeyIgnoreCase(headerName)) {
if (resultResponseHeaders.get(headerName).contains(headerValue)) {
return true;
}
}
return false;
}
public boolean prepareMessagePart(MimeMultipart mp, StringToStringMap contentIds, MessageXmlPart requestPart) throws Exception {
return AttachmentUtils.prepareMessagePart(this, mp, requestPart, contentIds);
}
public MessageXmlObject createMessageXmlObject(String responseContent, WsdlOperation wsdlOperation) {
return new MessageXmlObject(wsdlOperation, responseContent, false);
}
private void initRootPart(String requestContent, MimeMultipart mp, boolean isXOP) throws MessagingException {
MimeBodyPart rootPart = new PreencodedMimeBodyPart("8bit");
rootPart.setContentID(AttachmentUtils.ROOTPART_SOAPUI_ORG);
mp.addBodyPart(rootPart, 0);
DataHandler dataHandler = new DataHandler(new MockResponseDataSource(this, requestContent, isXOP));
rootPart.setDataHandler(dataHandler);
}
protected abstract String removeEmptyContent(String responseContent);
public void setResponseHeaders(StringToStringsMap headers) {
StringToStringsMap oldHeaders = getResponseHeaders();
getConfig().setHeaderArray(new HeaderConfig[0]);
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
for (String value : header.getValue()) {
HeaderConfig headerConfig = getConfig().addNewHeader();
headerConfig.setName(header.getKey());
headerConfig.setValue(value);
}
}
notifyPropertyChanged(WsdlMockResponse.HEADERS_PROPERTY, oldHeaders, headers);
}
protected abstract String executeSpecifics(MockRequest request, String responseContent, WsdlMockRunContext context) throws IOException, WSSecurityException;
public boolean isEntitizeProperties() {
return getSettings().getBoolean(CommonSettings.ENTITIZE_PROPERTIES);
}
public abstract long getResponseDelay();
public abstract boolean isForceMtom();
public abstract boolean isStripWhitespaces();
public void addTestPropertyListener(TestPropertyListener listener) {
propertyHolder.addTestPropertyListener(listener);
}
public ModelItem getModelItem() {
return propertyHolder.getModelItem();
}
public Map<String, TestProperty> getProperties() {
return propertyHolder.getProperties();
}
public TestProperty getProperty(String name) {
return propertyHolder.getProperty(name);
}
public String[] getPropertyNames() {
return propertyHolder.getPropertyNames();
}
public String getPropertyValue(String name) {
return propertyHolder.getPropertyValue(name);
}
public boolean hasProperty(String name) {
return propertyHolder.hasProperty(name);
}
public void removeTestPropertyListener(TestPropertyListener listener) {
propertyHolder.removeTestPropertyListener(listener);
}
public void setPropertyValue(String name, String value) {
propertyHolder.setPropertyValue(name, value);
}
public TestProperty getPropertyAt(int index) {
return propertyHolder.getPropertyAt(index);
}
public int getPropertyCount() {
return propertyHolder.getPropertyCount();
}
public List<TestProperty> getPropertyList() {
return propertyHolder.getPropertyList();
}
public String getEncoding() {
return getConfig().getEncoding();
}
public void setEncoding(String encoding) {
getConfig().setEncoding(encoding);
}
}