/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.olingo.odata2.core.servlet; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.Collections; import java.util.HashSet; import java.util.Random; import javax.servlet.GenericServlet; import javax.servlet.ServletConfig; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import junit.framework.Assert; import org.apache.olingo.odata2.api.ODataService; import org.apache.olingo.odata2.api.ODataServiceFactory; import org.apache.olingo.odata2.api.commons.HttpHeaders; import org.apache.olingo.odata2.api.commons.HttpStatusCodes; import org.apache.olingo.odata2.api.processor.ODataContext; import org.apache.olingo.odata2.api.processor.ODataProcessor; import org.apache.olingo.odata2.api.processor.ODataResponse; import org.apache.olingo.odata2.core.ODataResponseImpl; import org.apache.olingo.odata2.core.rest.ODataServiceFactoryImpl; import org.junit.Test; import org.mockito.Mockito; /** * */ public class ODataServletTest { private HttpServletRequest reqMock; private HttpServletResponse respMock; private ServletConfig configMock; public ODataServletTest() { reqMock = Mockito.mock(HttpServletRequest.class); respMock = Mockito.mock(HttpServletResponse.class); configMock = Mockito.mock(ServletConfig.class); } @Test public void handleRedirect() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock); servlet.service(reqMock, respMock); Mockito.verify(respMock).setStatus(HttpStatusCodes.TEMPORARY_REDIRECT.getStatusCode()); Mockito.verify(respMock).setHeader(HttpHeaders.LOCATION, "/context-path/servlet-path/"); } @Test public void handleRedirectWoServletPath() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock, "/context-path", null); servlet.service(reqMock, respMock); Mockito.verify(respMock).setStatus(HttpStatusCodes.TEMPORARY_REDIRECT.getStatusCode()); Mockito.verify(respMock).setHeader(HttpHeaders.LOCATION, "/context-path/"); } @Test public void handleRedirectWoContextPath() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock, null, "/servlet-path"); servlet.service(reqMock, respMock); Mockito.verify(respMock).setStatus(HttpStatusCodes.TEMPORARY_REDIRECT.getStatusCode()); Mockito.verify(respMock).setHeader(HttpHeaders.LOCATION, "/servlet-path/"); } @Test public void handleRedirectWoPath() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock, null, null); servlet.service(reqMock, respMock); Mockito.verify(respMock).setStatus(HttpStatusCodes.TEMPORARY_REDIRECT.getStatusCode()); Mockito.verify(respMock).setHeader(HttpHeaders.LOCATION, "/"); } @Test public void contentLengthCalculatedString() throws Exception { final Method createResponse = ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class); createResponse.setAccessible(true); final ODataServlet servlet = new ODataServlet(); final String content = "Test\r\n"; final ODataResponse response = ODataResponseImpl.status(HttpStatusCodes.OK).entity(content).build(); prepareResponseMockToWrite(respMock); prepareServlet(servlet); createResponse.invoke(servlet, respMock, response); Mockito.verify(respMock).setContentLength(content.getBytes("utf-8").length); } @Test public void contentLengthCalculatedStream() throws Exception { final Method createResponse = ODataServlet.class.getDeclaredMethod("createResponse", HttpServletResponse.class, ODataResponse.class); createResponse.setAccessible(true); final ODataServlet servlet = new ODataServlet(); final String content = "Test\r\n"; final ODataResponse response = ODataResponseImpl.status(HttpStatusCodes.OK).entity(new ByteArrayInputStream(content.getBytes("utf-8"))) .build(); prepareResponseMockToWrite(respMock); prepareServlet(servlet); createResponse.invoke(servlet, respMock, response); Mockito.verify(respMock).setContentLength(content.getBytes("utf-8").length); } @Test public void testInputStreamResponse() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final ServletOutputStream out = new ServletOutputStream() { @Override public void write(int i) throws IOException { bout.write(i); } }; Mockito.when(respMock.getOutputStream()).thenReturn(out); HttpServletResponse servletResponse = Mockito.mock(HttpServletResponse.class); Mockito.when(servletResponse.getOutputStream()).thenReturn(out); ODataResponse odataResponse = Mockito.mock(ODataResponse.class); Mockito.when(odataResponse.getStatus()).thenReturn(HttpStatusCodes.ACCEPTED); Mockito.when(odataResponse.getHeaderNames()).thenReturn(new HashSet<String>()); InputStream input = new ByteArrayInputStream("SomeData".getBytes()); Mockito.when(odataResponse.getEntity()).thenReturn(input); servlet.createResponse(servletResponse, odataResponse, true); String outputContent = new String(bout.toByteArray()); Assert.assertEquals("", outputContent); } @Test public void inputStreamResponse() throws Exception { testInputStreamResponse("123", "utf-8", null); testInputStreamResponse("1234567890", "utf-8", "5"); testInputStreamResponse(testData(200000), "utf-8", null); testInputStreamResponse(testData(200000), "utf-8", "8192"); testInputStreamResponse(testData(200000), "utf-8", "32768"); // testInputStreamResponse("üäö", "iso-8859-1", "8192"); testInputStreamResponse(testData(200000), "iso-8859-1", "32768"); testInputStreamResponse(testData(200000), "iso-8859-1", "8192"); testInputStreamResponse(testData(200000), "iso-8859-1", "32768"); // testInputStreamResponse("1234567890", "utf-8", "5"); testInputStreamResponse("1234567890", "utf-8", "ABD"); testInputStreamResponse("1234567890", "utf-8", ""); testInputStreamResponse("1234567890", "utf-8", "-29"); } private void testInputStreamResponse(String content, String encoding, String bufferSize) throws Exception { ODataServlet servlet = new ODataServlet(); Mockito.when(configMock.getInitParameter( "org.apache.olingo.odata2.core.servlet.buffer.size")).thenReturn(bufferSize); prepareServlet(servlet); final Charset charset = Charset.forName(encoding); final ByteArrayOutputStream bout = new ByteArrayOutputStream(); final ServletOutputStream out = new ServletOutputStream() { @Override public void write(int i) throws IOException { bout.write(i); } }; Mockito.when(respMock.getOutputStream()).thenReturn(out); HttpServletResponse servletResponse = Mockito.mock(HttpServletResponse.class); Mockito.when(servletResponse.getOutputStream()).thenReturn(out); ODataResponse odataResponse = Mockito.mock(ODataResponse.class); Mockito.when(odataResponse.getStatus()).thenReturn(HttpStatusCodes.ACCEPTED); Mockito.when(odataResponse.getHeaderNames()).thenReturn(new HashSet<String>()); InputStream input = new ByteArrayInputStream(content.getBytes(charset)); Mockito.when(odataResponse.getEntity()).thenReturn(input); servlet.createResponse(servletResponse, odataResponse); String outputContent = new String(bout.toByteArray(), charset); Assert.assertEquals(content, outputContent); } private String testData(int amount) { StringBuilder result = new StringBuilder(); Random r = new Random(); for (int i = 0; i < amount; i++) { result.append((char)(r.nextInt(26) + 'a')); } return result.toString(); } @Test public void serviceInstance() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock, "", "/servlet-path"); Mockito.when(reqMock.getPathInfo()).thenReturn("/request-path-info"); Mockito.when(reqMock.getRequestURI()).thenReturn("http://localhost:8080/servlet-path/request-path-info"); ODataServiceFactory factory = Mockito.mock(ODataServiceFactory.class); ODataService service = Mockito.mock(ODataService.class); Mockito.when(factory.createService(Mockito.any(ODataContext.class))).thenReturn(service); ODataProcessor processor = Mockito.mock(ODataProcessor.class); Mockito.when(service.getProcessor()).thenReturn(processor); Mockito.when(reqMock.getAttribute(ODataServiceFactory.FACTORY_INSTANCE_LABEL)).thenReturn(factory); Mockito.when(respMock.getOutputStream()).thenReturn(Mockito.mock(ServletOutputStream.class)); servlet.service(reqMock, respMock); Mockito.verify(factory).createService(Mockito.any(ODataContext.class)); } @Test public void serviceClassloader() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequest(reqMock, "", "/servlet-path"); Mockito.when(reqMock.getPathInfo()).thenReturn("/request-path-info"); Mockito.when(reqMock.getRequestURI()).thenReturn("http://localhost:8080/servlet-path/request-path-info"); Mockito.when(respMock.getOutputStream()).thenReturn(Mockito.mock(ServletOutputStream.class)); servlet.service(reqMock, respMock); Mockito.verify(configMock).getInitParameter(ODataServiceFactory.FACTORY_LABEL); Mockito.verify(reqMock).getAttribute(ODataServiceFactory.FACTORY_CLASSLOADER_LABEL); Assert.assertEquals(ODataServiceFactoryImpl.class, servlet.getServiceFactory(reqMock).getClass()); } private void prepareResponseMockToWrite(final HttpServletResponse response) throws IOException { Mockito.when(response.getOutputStream()).thenReturn(new ServletOutputStream() { @Override public void write(int b) throws IOException {} }); } private void prepareRequest(final HttpServletRequest req, final String contextPath, final String servletPath) { Mockito.when(req.getMethod()).thenReturn("GET"); Mockito.when(req.getContextPath()).thenReturn(contextPath); Mockito.when(req.getServletPath()).thenReturn(servletPath); Mockito.when(req.getHeaderNames()).thenReturn(Collections.enumeration(Collections.emptyList())); } private void prepareRequest(final HttpServletRequest req) { prepareRequest(req, "/context-path", "/servlet-path"); } private void prepareServlet(final GenericServlet servlet) throws Exception { // private transient ServletConfig config; Field configField = GenericServlet.class.getDeclaredField("config"); configField.setAccessible(true); configField.set(servlet, configMock); String factoryClassName = ODataServiceFactoryImpl.class.getName(); Mockito.when(configMock.getInitParameter(ODataServiceFactory.FACTORY_LABEL)).thenReturn(factoryClassName); } @Test public void testWithNoPathAfterServletPath() throws Exception { ODataServlet servlet = new ODataServlet(); prepareServlet(servlet); prepareRequestWithNoPathAfterServletPath(reqMock, "", "/servlet-path"); Mockito.when(reqMock.getPathInfo()).thenReturn("/Collection"); Mockito.when(reqMock.getRequestURI()).thenReturn("http://localhost:8080/servlet-path;v=1/Collection"); Mockito.when(servlet.getInitParameter("org.apache.olingo.odata2.path.split")).thenReturn("1"); Mockito.when(respMock.getOutputStream()).thenReturn(Mockito.mock(ServletOutputStream.class)); servlet.service(reqMock, respMock); Mockito.verify(configMock).getInitParameter(ODataServiceFactory.FACTORY_LABEL); Mockito.verify(reqMock).getAttribute(ODataServiceFactory.FACTORY_CLASSLOADER_LABEL); Assert.assertEquals(ODataServiceFactoryImpl.class, servlet.getServiceFactory(reqMock).getClass()); } private void prepareRequestWithNoPathAfterServletPath(final HttpServletRequest req, final String contextPath, final String servletPath) { Mockito.when(req.getMethod()).thenReturn("GET"); Mockito.when(req.getContextPath()).thenReturn(contextPath); Mockito.when(req.getServletPath()).thenReturn(servletPath); Mockito.when(req.getRequestURI()).thenReturn(servletPath + ";v=1" + "/Collection"); Mockito.when(req.getHeaderNames()).thenReturn(Collections.enumeration(Collections.emptyList())); } }