Java tutorial
/* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jayway.restassured.module.mockmvc.internal; import com.jayway.restassured.RestAssured; import com.jayway.restassured.authentication.NoAuthScheme; import com.jayway.restassured.builder.MultiPartSpecBuilder; import com.jayway.restassured.config.EncoderConfig; import com.jayway.restassured.filter.Filter; import com.jayway.restassured.filter.log.RequestLoggingFilter; import com.jayway.restassured.http.Method; import com.jayway.restassured.internal.RequestSpecificationImpl; import com.jayway.restassured.internal.ResponseParserRegistrar; import com.jayway.restassured.internal.ResponseSpecificationImpl; import com.jayway.restassured.internal.filter.FilterContextImpl; import com.jayway.restassured.internal.http.CharsetExtractor; import com.jayway.restassured.internal.log.LogRepository; import com.jayway.restassured.internal.support.PathSupport; import com.jayway.restassured.internal.util.SafeExceptionRethrower; import com.jayway.restassured.module.mockmvc.config.AsyncConfig; import com.jayway.restassured.module.mockmvc.config.RestAssuredMockMvcConfig; import com.jayway.restassured.module.mockmvc.intercept.MockHttpServletRequestBuilderInterceptor; import com.jayway.restassured.module.mockmvc.response.MockMvcResponse; import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestAsyncConfigurer; import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestAsyncSender; import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSender; import com.jayway.restassured.response.Cookie; import com.jayway.restassured.response.Cookies; import com.jayway.restassured.response.Header; import com.jayway.restassured.response.Headers; import com.jayway.restassured.specification.ResponseSpecification; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultHandler; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.RequestPostProcessor; import java.io.*; import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.security.Principal; import java.util.*; import java.util.concurrent.TimeUnit; import static com.jayway.restassured.filter.time.TimingFilter.RESPONSE_TIME_MILLISECONDS; import static com.jayway.restassured.internal.assertion.AssertParameter.notNull; import static com.jayway.restassured.internal.support.PathSupport.mergeAndRemoveDoubleSlash; import static com.jayway.restassured.module.mockmvc.internal.ConfigConverter.convertToRestAssuredConfig; import static com.jayway.restassured.module.mockmvc.internal.SpringSecurityClassPathChecker.isSpringSecurityInClasspath; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.springframework.http.HttpMethod.*; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request; class MockMvcRequestSenderImpl implements MockMvcRequestSender, MockMvcRequestAsyncConfigurer, MockMvcRequestAsyncSender { private static final String ATTRIBUTE_NAME_URL_TEMPLATE = "org.springframework.restdocs.urlTemplate"; private static final String CONTENT_TYPE = "Content-Type"; private static final String CHARSET = "charset"; private static final String LINE_SEPARATOR = "line.separator"; private final MockMvc mockMvc; private final Map<String, Object> params; private final Map<String, Object> queryParams; private final Map<String, Object> formParams; private final Map<String, Object> attributes; private final RestAssuredMockMvcConfig config; private final Object requestBody; private Headers headers; private final Cookies cookies; private final List<MockMvcMultiPart> multiParts; private final RequestLoggingFilter requestLoggingFilter; private final List<ResultHandler> resultHandlers; private final List<RequestPostProcessor> requestPostProcessors; private final MockHttpServletRequestBuilderInterceptor interceptor; private final String basePath; private final ResponseSpecification responseSpecification; private final Object authentication; private final LogRepository logRepository; private final boolean isAsyncRequest; MockMvcRequestSenderImpl(MockMvc mockMvc, Map<String, Object> params, Map<String, Object> queryParams, Map<String, Object> formParams, Map<String, Object> attributes, RestAssuredMockMvcConfig config, Object requestBody, Headers headers, Cookies cookies, List<MockMvcMultiPart> multiParts, RequestLoggingFilter requestLoggingFilter, List<ResultHandler> resultHandlers, List<RequestPostProcessor> requestPostProcessors, MockHttpServletRequestBuilderInterceptor interceptor, String basePath, ResponseSpecification responseSpecification, Object authentication, LogRepository logRepository) { this(mockMvc, params, queryParams, formParams, attributes, config, requestBody, headers, cookies, multiParts, requestLoggingFilter, resultHandlers, requestPostProcessors, interceptor, basePath, responseSpecification, authentication, logRepository, false); } private MockMvcRequestSenderImpl(MockMvc mockMvc, Map<String, Object> params, Map<String, Object> queryParams, Map<String, Object> formParams, Map<String, Object> attributes, RestAssuredMockMvcConfig config, Object requestBody, Headers headers, Cookies cookies, List<MockMvcMultiPart> multiParts, RequestLoggingFilter requestLoggingFilter, List<ResultHandler> resultHandlers, List<RequestPostProcessor> requestPostProcessors, MockHttpServletRequestBuilderInterceptor interceptor, String basePath, ResponseSpecification responseSpecification, Object authentication, LogRepository logRepository, boolean isAsyncRequest) { this.mockMvc = mockMvc; this.params = params; this.queryParams = queryParams; this.formParams = formParams; this.attributes = attributes; this.config = config; this.requestBody = requestBody; this.headers = headers; this.cookies = cookies; this.multiParts = multiParts; this.requestLoggingFilter = requestLoggingFilter; this.resultHandlers = resultHandlers; this.requestPostProcessors = requestPostProcessors; this.interceptor = interceptor; this.basePath = basePath; this.responseSpecification = responseSpecification; this.authentication = authentication; this.logRepository = logRepository; this.isAsyncRequest = isAsyncRequest; } private Object assembleHeaders(MockHttpServletResponse response) { Collection<String> headerNames = response.getHeaderNames(); List<Header> headers = new ArrayList<Header>(); for (String headerName : headerNames) { List<String> headerValues = response.getHeaders(headerName); for (String headerValue : headerValues) { headers.add(new Header(headerName, headerValue)); } } return new Headers(headers); } @SuppressWarnings("unchecked") private MockMvcResponse performRequest(MockHttpServletRequestBuilder requestBuilder) { MockHttpServletResponse response; if (interceptor != null) { interceptor.intercept(requestBuilder); } if (isSpringSecurityInClasspath() && authentication instanceof org.springframework.security.core.Authentication) { org.springframework.security.core.context.SecurityContextHolder.getContext() .setAuthentication((org.springframework.security.core.Authentication) authentication); } else if (authentication instanceof Principal) { requestBuilder.principal((Principal) authentication); } for (RequestPostProcessor requestPostProcessor : requestPostProcessors) { requestBuilder.with(requestPostProcessor); } MockMvcRestAssuredResponseImpl restAssuredResponse; try { final long start = System.currentTimeMillis(); ResultActions perform = mockMvc.perform(requestBuilder); final long responseTime = System.currentTimeMillis() - start; if (!resultHandlers.isEmpty()) { for (ResultHandler resultHandler : resultHandlers) { perform.andDo(resultHandler); } } MvcResult mvcResult = getMvcResult(perform, isAsyncRequest); response = mvcResult.getResponse(); restAssuredResponse = new MockMvcRestAssuredResponseImpl(perform, logRepository); restAssuredResponse.setConfig(convertToRestAssuredConfig(config)); restAssuredResponse.setContent(response.getContentAsString()); restAssuredResponse.setContentType(response.getContentType()); restAssuredResponse.setHasExpectations(false); restAssuredResponse.setStatusCode(response.getStatus()); restAssuredResponse.setResponseHeaders(assembleHeaders(response)); restAssuredResponse.setRpr(getRpr()); restAssuredResponse.setStatusLine(assembleStatusLine(response, mvcResult.getResolvedException())); restAssuredResponse.setFilterContextProperties(new HashMap() { { put(RESPONSE_TIME_MILLISECONDS, responseTime); } }); if (responseSpecification != null) { responseSpecification.validate(ResponseConverter.toStandardResponse(restAssuredResponse)); } } catch (Exception e) { return SafeExceptionRethrower.safeRethrow(e); } finally { if (isSpringSecurityInClasspath()) { org.springframework.security.core.context.SecurityContextHolder.clearContext(); } } return restAssuredResponse; } private MvcResult getMvcResult(ResultActions perform, boolean isAsyncRequest) throws Exception { MvcResult mvcResult; if (isAsyncRequest) { MvcResult startedAsyncRequestProcessing = perform.andExpect(request().asyncStarted()).andReturn(); startedAsyncRequestProcessing.getAsyncResult(config.getAsyncConfig().timeoutInMs()); mvcResult = mockMvc.perform(asyncDispatch(startedAsyncRequestProcessing)).andReturn(); } else { mvcResult = perform.andReturn(); } return mvcResult; } private ResponseParserRegistrar getRpr() { if (responseSpecification != null && responseSpecification instanceof ResponseSpecificationImpl) { return ((ResponseSpecificationImpl) responseSpecification).getRpr(); } return new ResponseParserRegistrar(); } private String assembleStatusLine(MockHttpServletResponse response, Exception resolvedException) { StringBuilder builder = new StringBuilder(); builder.append(response.getStatus()); if (isNotBlank(response.getErrorMessage())) { builder.append(" ").append(response.getErrorMessage()); } else if (resolvedException != null) { builder.append(" ").append(resolvedException.getMessage()); } return builder.toString(); } private Object[] mapToArray(Map<String, ?> map) { if (map == null) { return new Object[0]; } return map.values().toArray(new Object[map.values().size()]); } private MockMvcResponse sendRequest(HttpMethod method, String path, Object[] pathParams) { notNull(path, "Path"); if (requestBody != null && !multiParts.isEmpty()) { throw new IllegalStateException( "You cannot specify a request body and a multi-part body in the same request. Perhaps you want to change the body to a multi part?"); } String uri; if (isNotBlank(basePath)) { uri = mergeAndRemoveDoubleSlash(basePath, path); } else { uri = path; } final MockHttpServletRequestBuilder request; if (multiParts.isEmpty()) { request = MockMvcRequestBuilders.request(method, uri, pathParams); } else if (method != POST) { throw new IllegalArgumentException("Currently multi-part file data uploading only works for " + POST); } else { request = MockMvcRequestBuilders.fileUpload(uri, pathParams); } String requestContentType = findContentType(); if (!params.isEmpty()) { new ParamApplier(params) { @Override protected void applyParam(String paramName, String[] paramValues) { request.param(paramName, paramValues); } }.applyParams(); if (StringUtils.isBlank(requestContentType) && method == POST && !isInMultiPartMode(request)) { setContentTypeToApplicationFormUrlEncoded(request); } } if (!queryParams.isEmpty()) { new ParamApplier(queryParams) { @Override protected void applyParam(String paramName, String[] paramValues) { // Spring MVC cannot distinguish query from params afaik. request.param(paramName, paramValues); } }.applyParams(); } if (!formParams.isEmpty()) { if (method == GET) { throw new IllegalArgumentException("Cannot use form parameters in a GET request"); } new ParamApplier(formParams) { @Override protected void applyParam(String paramName, String[] paramValues) { request.param(paramName, paramValues); } }.applyParams(); boolean isInMultiPartMode = isInMultiPartMode(request); if (StringUtils.isBlank(requestContentType) && !isInMultiPartMode) { setContentTypeToApplicationFormUrlEncoded(request); } } if (!attributes.isEmpty()) { new ParamApplier(attributes) { @Override protected void applyParam(String paramName, String[] paramValues) { request.requestAttr(paramName, paramValues[0]); } }.applyParams(); } if (RestDocsClassPathChecker.isSpringRestDocsInClasspath() && config.getMockMvcConfig().shouldAutomaticallyApplySpringRestDocsMockMvcSupport()) { request.requestAttr(ATTRIBUTE_NAME_URL_TEMPLATE, PathSupport.getPath(uri)); } if (StringUtils.isNotBlank(requestContentType)) { request.contentType(MediaType.parseMediaType(requestContentType)); } if (headers.exist()) { for (Header header : headers) { request.header(header.getName(), header.getValue()); } } if (cookies.exist()) { for (Cookie cookie : cookies) { javax.servlet.http.Cookie servletCookie = new javax.servlet.http.Cookie(cookie.getName(), cookie.getValue()); if (cookie.hasComment()) { servletCookie.setComment(cookie.getComment()); } if (cookie.hasDomain()) { servletCookie.setDomain(cookie.getDomain()); } if (cookie.hasMaxAge()) { servletCookie.setMaxAge(cookie.getMaxAge()); } if (cookie.hasPath()) { servletCookie.setPath(cookie.getPath()); } if (cookie.hasVersion()) { servletCookie.setVersion(cookie.getVersion()); } request.cookie(servletCookie); } } if (!multiParts.isEmpty()) { MockMultipartHttpServletRequestBuilder multiPartRequest = (MockMultipartHttpServletRequestBuilder) request; for (MockMvcMultiPart multiPart : multiParts) { MockMultipartFile multipartFile; String fileName = multiPart.getFileName(); String controlName = multiPart.getControlName(); String mimeType = multiPart.getMimeType(); if (multiPart.isByteArray()) { multipartFile = new MockMultipartFile(controlName, fileName, mimeType, (byte[]) multiPart.getContent()); } else if (multiPart.isFile() || multiPart.isInputStream()) { InputStream inputStream; if (multiPart.isFile()) { try { inputStream = new FileInputStream((File) multiPart.getContent()); } catch (FileNotFoundException e) { return SafeExceptionRethrower.safeRethrow(e); } } else { inputStream = (InputStream) multiPart.getContent(); } try { multipartFile = new MockMultipartFile(controlName, fileName, mimeType, inputStream); } catch (IOException e) { return SafeExceptionRethrower.safeRethrow(e); } } else { // String multipartFile = new MockMultipartFile(controlName, fileName, mimeType, ((String) multiPart.getContent()).getBytes()); } multiPartRequest.file(multipartFile); } } if (requestBody != null) { if (requestBody instanceof byte[]) { request.content((byte[]) requestBody); } else if (requestBody instanceof File) { byte[] bytes = toByteArray((File) requestBody); request.content(bytes); } else { request.content(requestBody.toString()); } } logRequestIfApplicable(method, uri, path, pathParams); return performRequest(request); } // TODO Extract content-type from headers and apply charset if needed! private String findContentType() { String requestContentType = headers.getValue(CONTENT_TYPE); if (StringUtils.isBlank(requestContentType) && !multiParts.isEmpty()) { requestContentType = "multipart/" + config.getMultiPartConfig().defaultSubtype(); } EncoderConfig encoderConfig = config.getEncoderConfig(); if (requestContentType != null && encoderConfig.shouldAppendDefaultContentCharsetToContentTypeIfUndefined() && !StringUtils.containsIgnoreCase(requestContentType, CHARSET)) { // Append default charset to request content type requestContentType += "; charset="; if (encoderConfig.hasDefaultCharsetForContentType(requestContentType)) { requestContentType += encoderConfig.defaultCharsetForContentType(requestContentType); } else { requestContentType += encoderConfig.defaultContentCharset(); } } return requestContentType; } private static byte[] toByteArray(File file) { ByteArrayOutputStream ous = null; InputStream ios = null; try { byte[] buffer = new byte[4096]; ous = new ByteArrayOutputStream(); ios = new FileInputStream(file); int read = 0; while ((read = ios.read(buffer)) != -1) { ous.write(buffer, 0, read); } } catch (IOException e) { throw new RuntimeException(e); } finally { try { if (ous != null) { ous.close(); } if (ios != null) { ios.close(); } } catch (IOException ignored) { } } return ous.toByteArray(); } private void setContentTypeToApplicationFormUrlEncoded(MockHttpServletRequestBuilder request) { String contentType = APPLICATION_FORM_URLENCODED_VALUE; EncoderConfig encoderConfig = config.getEncoderConfig(); if (encoderConfig.shouldAppendDefaultContentCharsetToContentTypeIfUndefined()) { contentType += "; charset="; if (encoderConfig.hasDefaultCharsetForContentType(contentType)) { contentType += encoderConfig.defaultCharsetForContentType(contentType); } else { contentType += encoderConfig.defaultContentCharset(); } } MediaType mediaType = MediaType.parseMediaType(contentType); request.contentType(mediaType); List<Header> newHeaders = new ArrayList<Header>(headers.asList()); newHeaders.add(new Header(CONTENT_TYPE, mediaType.toString())); headers = new Headers(newHeaders); } private boolean isInMultiPartMode(MockHttpServletRequestBuilder request) { return request instanceof MockMultipartHttpServletRequestBuilder; } private void logRequestIfApplicable(HttpMethod method, String uri, String originalPath, Object[] unnamedPathParams) { if (requestLoggingFilter == null) { return; } final RequestSpecificationImpl reqSpec = new RequestSpecificationImpl("http://localhost", RestAssured.UNDEFINED_PORT, "", new NoAuthScheme(), Collections.<Filter>emptyList(), null, true, convertToRestAssuredConfig(config), logRepository, null); reqSpec.setMethod(method.toString()); reqSpec.path(uri); reqSpec.buildUnnamedPathParameterTuples(unnamedPathParams); if (params != null) { new ParamLogger(params) { protected void logParam(String paramName, Object paramValue) { reqSpec.param(paramName, paramValue); } }.logParams(); } if (queryParams != null) { new ParamLogger(queryParams) { protected void logParam(String paramName, Object paramValue) { reqSpec.queryParam(paramName, paramValue); } }.logParams(); } if (formParams != null) { new ParamLogger(formParams) { protected void logParam(String paramName, Object paramValue) { reqSpec.formParam(paramName, paramValue); } }.logParams(); } if (headers != null) { for (Header header : headers) { reqSpec.header(header); } } if (cookies != null) { for (Cookie cookie : cookies) { reqSpec.cookie(cookie); } } if (requestBody != null) { if (requestBody instanceof byte[]) { reqSpec.body((byte[]) requestBody); } else if (requestBody instanceof File) { String contentType = findContentType(); String charset = null; if (StringUtils.isNotBlank(contentType)) { charset = CharsetExtractor.getCharsetFromContentType(contentType); } if (charset == null) { charset = Charset.defaultCharset().toString(); } String string = fileToString((File) requestBody, charset); reqSpec.body(string); } else { reqSpec.body(requestBody); } } if (multiParts != null) { for (MockMvcMultiPart multiPart : multiParts) { reqSpec.multiPart( new MultiPartSpecBuilder(multiPart.getContent()).controlName(multiPart.getControlName()) .fileName(multiPart.getFileName()).mimeType(multiPart.getMimeType()).build()); } } String uriPath = PathSupport.getPath(uri); String originalUriPath = PathSupport.getPath(originalPath); requestLoggingFilter.filter(reqSpec, null, new FilterContextImpl(uri, originalUriPath, uriPath, uri, uri, new Object[0], Method.valueOf(method.toString()), null, Collections.<Filter>emptyList().iterator(), new HashMap<String, Object>())); } private String fileToString(File file, String charset) { StringBuilder fileContents = new StringBuilder((int) file.length()); Scanner scanner; try { scanner = new Scanner(file, charset); } catch (FileNotFoundException e) { throw new RuntimeException(e); } String lineSeparator = System.getProperty(LINE_SEPARATOR); try { while (scanner.hasNextLine()) { fileContents.append(scanner.nextLine()).append(lineSeparator); } return fileContents.toString(); } finally { scanner.close(); } } public MockMvcResponse get(String path, Object... pathParams) { return sendRequest(GET, path, pathParams); } public MockMvcResponse get(String path, Map<String, ?> pathParams) { return get(path, mapToArray(pathParams)); } public MockMvcResponse post(String path, Object... pathParams) { return sendRequest(POST, path, pathParams); } public MockMvcResponse post(String path, Map<String, ?> pathParams) { return post(path, mapToArray(pathParams)); } public MockMvcResponse put(String path, Object... pathParams) { return sendRequest(PUT, path, pathParams); } public MockMvcResponse put(String path, Map<String, ?> pathParams) { return put(path, mapToArray(pathParams)); } public MockMvcResponse delete(String path, Object... pathParams) { return sendRequest(DELETE, path, pathParams); } public MockMvcResponse delete(String path, Map<String, ?> pathParams) { return delete(path, mapToArray(pathParams)); } public MockMvcResponse head(String path, Object... pathParams) { return sendRequest(HEAD, path, pathParams); } public MockMvcResponse head(String path, Map<String, ?> pathParams) { return head(path, mapToArray(pathParams)); } public MockMvcResponse patch(String path, Object... pathParams) { return sendRequest(PATCH, path, pathParams); } public MockMvcResponse patch(String path, Map<String, ?> pathParams) { return patch(path, mapToArray(pathParams)); } public MockMvcResponse options(String path, Object... pathParams) { return sendRequest(OPTIONS, path, pathParams); } public MockMvcResponse options(String path, Map<String, ?> pathParams) { return options(path, mapToArray(pathParams)); } public MockMvcResponse get(URI uri) { return get(uri.toString()); } public MockMvcResponse post(URI uri) { return post(uri.toString()); } public MockMvcResponse put(URI uri) { return put(uri.toString()); } public MockMvcResponse delete(URI uri) { return delete(uri.toString()); } public MockMvcResponse head(URI uri) { return head(uri.toString()); } public MockMvcResponse patch(URI uri) { return patch(uri.toString()); } public MockMvcResponse options(URI uri) { return options(uri.toString()); } public MockMvcResponse get(URL url) { return get(url.toString()); } public MockMvcResponse post(URL url) { return post(url.toString()); } public MockMvcResponse put(URL url) { return put(url.toString()); } public MockMvcResponse delete(URL url) { return delete(url.toString()); } public MockMvcResponse head(URL url) { return head(url.toString()); } public MockMvcResponse patch(URL url) { return patch(url.toString()); } public MockMvcResponse options(URL url) { return options(url.toString()); } public MockMvcResponse get() { return get(""); } public MockMvcResponse post() { return post(""); } public MockMvcResponse put() { return put(""); } public MockMvcResponse delete() { return delete(""); } public MockMvcResponse head() { return head(""); } public MockMvcResponse patch() { return patch(""); } public MockMvcResponse options() { return options(""); } public MockMvcRequestAsyncConfigurer with() { return this; } public MockMvcRequestAsyncConfigurer and() { return this; } public MockMvcRequestAsyncConfigurer timeout(long duration, TimeUnit timeUnit) { RestAssuredMockMvcConfig newConfig = config.asyncConfig(new AsyncConfig(duration, timeUnit)); return new MockMvcRequestSenderImpl(mockMvc, params, queryParams, formParams, attributes, newConfig, requestBody, headers, cookies, multiParts, requestLoggingFilter, resultHandlers, requestPostProcessors, interceptor, basePath, responseSpecification, authentication, logRepository, isAsyncRequest); } public MockMvcRequestAsyncConfigurer timeout(long durationInMs) { return timeout(durationInMs, TimeUnit.MILLISECONDS); } public MockMvcRequestSender then() { return this; } public MockMvcRequestAsyncConfigurer async() { return new MockMvcRequestSenderImpl(mockMvc, params, queryParams, formParams, attributes, config, requestBody, headers, cookies, multiParts, requestLoggingFilter, resultHandlers, requestPostProcessors, interceptor, basePath, responseSpecification, authentication, logRepository, true); } private abstract static class ParamApplier { private Map<String, Object> map; protected ParamApplier(Map<String, Object> parameters) { this.map = parameters; } public void applyParams() { for (Map.Entry<String, Object> listEntry : map.entrySet()) { Object value = listEntry.getValue(); String[] stringValues; if (value instanceof Collection) { Collection col = (Collection) value; stringValues = new String[col.size()]; int index = 0; for (Object val : col) { stringValues[index] = val == null ? null : val.toString(); index++; } } else { stringValues = new String[1]; stringValues[0] = value == null ? null : value.toString(); } applyParam(listEntry.getKey(), stringValues); } } protected abstract void applyParam(String paramName, String[] paramValues); } private abstract static class ParamLogger { private Map<String, Object> map; protected ParamLogger(Map<String, Object> parameters) { this.map = parameters; } public void logParams() { for (Map.Entry<String, Object> stringListEntry : map.entrySet()) { Object value = stringListEntry.getValue(); Collection<Object> values; if (value instanceof Collection) { values = (Collection<Object>) value; } else { values = new ArrayList<Object>(); values.add(value); } for (Object theValue : values) { logParam(stringListEntry.getKey(), theValue); } } } protected abstract void logParam(String paramName, Object paramValue); } }