blob: 4a8eee285f40d6f3e1917ea18c4ff073cc538f48 [file] [log] [blame]
/*
* $Id$
*
* 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.struts2;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import junit.framework.TestCase;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.jasper.runtime.InstanceHelper;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringParser;
import org.apache.struts2.url.StrutsUrlDecoder;
import org.apache.tomcat.InstanceManager;
import org.easymock.EasyMock;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import static org.apache.struts2.ServletActionContext.STRUTS_VALUESTACK_KEY;
public class EmbeddedJSPResultTest extends TestCase {
private HttpServletRequest request;
private MockHttpServletResponse response;
private MockServletContext context;
private MockServletConfig config;
private EmbeddedJSPResult result;
public void testScriptlet() throws Exception {
result.setLocation("org/apache/struts2/scriptlet.jsp");
result.execute(null);
assertEquals("Saynotoscriptlets", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testEmbedded() throws Exception {
//the jsp is inside jsps.jar
result.setLocation("dir/all.jsp");
result.execute(null);
assertEquals("helloJGWhoamI?XXXXXXXXXXXYThissessionisnotsecure.", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testFilesAreReadOnlyOnce() throws Exception {
//make sure that files are not read multiple times
String jsp = "org/apache/struts2/dont-use.jsp";
CountingClassLoaderInterface classLoaderInterface = new CountingClassLoaderInterface(this.getClass().getClassLoader());
context.setAttribute(ClassLoaderInterface.CLASS_LOADER_INTERFACE, classLoaderInterface);
result.setLocation(jsp);
result.execute(null);
Integer counter0 = classLoaderInterface.counters.get(jsp);
assertNotNull(counter0);
result.execute(null);
Integer counter1 = classLoaderInterface.counters.get(jsp);
assertNotNull(counter1);
assertEquals(counter0, counter1);
}
public void testEmbeddedAbsolutePath() throws Exception {
//the jsp is inside jsps.jar
result.setLocation("/dir/all.jsp");
result.execute(null);
assertEquals("helloJGWhoamI?XXXXXXXXXXXYThissessionisnotsecure.", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testSimple() throws Exception {
result.setLocation("org/apache/struts2/simple0.jsp");
result.execute(null);
assertEquals("hello", response.getContentAsString().trim());
}
//ok i give up..i don't know why this doesn't work from maven
/* public void testKeyInContext() throws Exception {
result.setLocation("org/apache/struts2/scriptlet.jsp");
result.execute(null);
String key = JspApplicationContextImpl.class.getName() + "@" + this.getClass().getClassLoader().hashCode();
assertNotNull(context.getAttribute(key));
}*/
public void testEL() throws Exception {
result.setLocation("org/apache/struts2/el.jsp");
result.execute(null);
assertEquals("somethingelseText", response.getContentAsString().trim());
}
public void testAbsolutePath() throws Exception {
result.setLocation("/org/apache/struts2/simple0.jsp");
result.execute(null);
assertEquals("hello", response.getContentAsString().trim());
}
public void testTag0() throws Exception {
result.setLocation("org/apache/struts2/tag0.jsp");
result.execute(null);
assertEquals("Thissessionisnotsecure.OtherText", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testIncludeSimple() throws Exception {
result.setLocation("org/apache/struts2/includes0.jsp");
result.execute(null);
assertEquals("helloTest", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testIncludeSimpleWithDirective() throws Exception {
result.setLocation("org/apache/struts2/includes3.jsp");
result.execute(null);
assertEquals("helloTest", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testIncludeWithSubdir() throws Exception {
result.setLocation("org/apache/struts2/includes1.jsp");
result.execute(null);
assertEquals("subTest", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testIncludeWithParam() throws Exception {
result.setLocation("org/apache/struts2/includes2.jsp");
result.execute(null);
assertEquals("JGTest", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testBroken0() throws Exception {
try {
result.setLocation("org/apache/struts2/broken0.jsp");
result.execute(null);
fail("should have failed with broken jsp");
} catch (IllegalStateException ex) {
//ok
}
}
public void testJSTL() throws Exception {
result.setLocation("org/apache/struts2/jstl.jsp");
result.execute(null);
assertEquals("XXXXXXXXXXXY", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testCachedInstances() throws InterruptedException {
ServletCache cache = new ServletCache();
Servlet servlet1 = cache.get("org/apache/struts2/simple0.jsp");
Servlet servlet2 = cache.get("org/apache/struts2/simple0.jsp");
assertSame(servlet1, servlet2);
}
public void testCacheInstanceWithManyThreads() throws BrokenBarrierException, InterruptedException {
//start a bunch of thread at the same time using CyclicBarrier and hit the cache
//then wait for all the threads to end and check that they all got a reference to the same object
//and the cache size should be 1
DummyServletCache cache = new DummyServletCache();
int numThreads = 70;
CyclicBarrier startBarrier = new CyclicBarrier(numThreads + 1);
CyclicBarrier endBarrier = new CyclicBarrier(numThreads + 1);
List<ServletGetRunnable> runnables = new ArrayList<>(numThreads);
//create the threads
for (int i = 0; i < numThreads; i++) {
ServletGetRunnable runnable = new ServletGetRunnable(cache, startBarrier, endBarrier, ActionContext.getContext());
Thread thread = new Thread(runnable);
runnables.add(runnable);
thread.start();
}
startBarrier.await();
endBarrier.await();
Object servlet = cache.get("org/apache/struts2/simple0.jsp");
assertEquals(1, cache.size());
for (ServletGetRunnable runnable : runnables) {
assertSame(servlet, runnable.getObject());
}
}
public void testBeans() throws Exception {
result.setLocation("org/apache/struts2/beans.jsp");
result.execute(null);
assertEquals("WhoamI?", StringUtils.deleteWhitespace(response.getContentAsString()));
}
public void testNotURLClassLoader() throws Exception {
ClassLoader parentClassLoader = Thread.currentThread().getContextClassLoader();
NotURLClassLoader loader = new NotURLClassLoader(parentClassLoader);
Thread.currentThread().setContextClassLoader(loader);
try {
result.setLocation("org/apache/struts2/tag0.jsp");
result.execute(null);
assertEquals("Thissessionisnotsecure.OtherText", StringUtils.deleteWhitespace(response.getContentAsString()));
} finally {
Thread.currentThread().setContextClassLoader(parentClassLoader);
}
}
public void testComplex() throws Exception {
result.setLocation("org/apache/struts2/complex0.jsp");
result.execute(null);
String responseString = response.getContentAsString();
assertNotNull("result is null?", responseString);
int titleIndex = responseString.indexOf("<title>Struts2 Embedded JSP Plugin - Complex Test Page</title>");
int responseLength = responseString.length();
int testValue1Index = responseString.indexOf("testvalue1 set/if worked.");
int testValue5Index = responseString.indexOf("testvalue5 set/if worked.");
int lastGroupIndex = responseString.indexOf("End include tests<br/>");
int lastHtmlIndex = responseString.indexOf("</html>");
assertTrue("Did not find title index (" + titleIndex + ") ?", titleIndex > 0);
assertTrue("Test value 1 not present or index (" + testValue1Index + ") not > title index (" + titleIndex + ") ?",
testValue1Index > titleIndex);
assertTrue("Test value 5 not present or index (" + testValue5Index + ") not > test value 1 index (" + testValue1Index + ") ?",
testValue5Index > testValue1Index);
assertTrue("Last group index not present or index (" + lastGroupIndex + ") not > test value 5 index (" + testValue5Index + ") ?",
lastGroupIndex > testValue5Index);
assertTrue("Last html index not present or index (" + lastHtmlIndex + ") not > last group index (" + lastGroupIndex + ") ?",
lastHtmlIndex > lastGroupIndex);
// complex0.jsp length 3439 in Windows and estimated 3221 in Linux/Unix (with 218 lines, Windows has around 218 additional
// characters (crlf vs. lf). Test length larger than the min(Windows,Linux)), rounded down to the nearest 100.
assertTrue("Response length (" + responseLength + ") not at least length: 3200 ?", responseLength > 3200);
}
public void testInstanceHelper() throws Exception {
InstanceManager instanceManagerServlet = InstanceHelper.getServletInstanceManager(config);
InstanceManager instanceManagerClassLoader = InstanceHelper.getClassLoaderInstanceManager(context.getClassLoader());
assertNotNull("instanceManager (servlet) is null ?", instanceManagerServlet);
assertNotNull("instanceManager (classloader) is null ?", instanceManagerClassLoader);
assertEquals("instanceManager (servlet) is not equal to instanceManager (classloader) ?", instanceManagerServlet, instanceManagerClassLoader);
final Double instanceDouble = (double) 0;
final Long instanceLong = 0L;
final Object instanceObject = new Object();
final String instanceString = "test string";
final MockHttpServletRequest intanceMockHttpServletRequest = new MockHttpServletRequest();
intanceMockHttpServletRequest.setContextPath("context path");
InstanceHelper.postConstruct(instanceManagerServlet, instanceDouble);
InstanceHelper.postConstruct(instanceManagerServlet, instanceLong);
InstanceHelper.postConstruct(instanceManagerServlet, instanceObject);
InstanceHelper.postConstruct(instanceManagerServlet, instanceString);
InstanceHelper.postConstruct(instanceManagerServlet, intanceMockHttpServletRequest);
assertEquals("test string value changed after postConstruct ?", instanceString, "test string");
assertEquals("mock servlet request context path value changed after postConstruct ?",
intanceMockHttpServletRequest.getContextPath(), "context path");
InstanceHelper.preDestroy(instanceManagerServlet, instanceDouble);
InstanceHelper.preDestroy(instanceManagerServlet, instanceLong);
InstanceHelper.preDestroy(instanceManagerServlet, instanceObject);
InstanceHelper.preDestroy(instanceManagerServlet, instanceString);
InstanceHelper.preDestroy(instanceManagerServlet, intanceMockHttpServletRequest);
assertEquals("test string value changed after preDestroy ?", instanceString, "test string");
assertEquals("mock servlet request context path value changed after preDestroy ?",
intanceMockHttpServletRequest.getContextPath(), "context path");
}
@Override
protected void setUp() throws Exception {
super.setUp();
result = new EmbeddedJSPResult();
request = EasyMock.createNiceMock(HttpServletRequest.class);
response = new MockHttpServletResponse();
context = new MockServletContext();
config = new MockServletConfig(context);
final Map params = new HashMap();
HttpSession session = EasyMock.createNiceMock(HttpSession.class);
EasyMock.replay(session);
//mock value stack
ValueStack valueStack = EasyMock.createNiceMock(ValueStack.class);
EasyMock.expect(valueStack.getActionContext()).andReturn(ActionContext.getContext()).anyTimes();
EasyMock.replay(valueStack);
EasyMock.expect(request.getSession()).andReturn(session).anyTimes();
EasyMock.expect(request.getParameterMap()).andReturn(params).anyTimes();
EasyMock.expect(request.getParameter("username")).andAnswer(() -> ActionContext.getContext().getParameters().get("username").getValue());
EasyMock.expect(request.getAttribute(STRUTS_VALUESTACK_KEY)).andReturn(valueStack).anyTimes();
EasyMock.expect(request.getAttribute("something")).andReturn("somethingelse").anyTimes();
EasyMock.replay(request);
//mock converter
XWorkConverter converter = new DummyConverter();
DefaultFileManager fileManager = new DefaultFileManager();
fileManager.setReloadingConfigs(false);
//mock container
Container container = EasyMock.createNiceMock(Container.class);
EasyMock.expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes();
TextParser parser = new OgnlTextParser();
EasyMock.expect(container.getInstance(TextParser.class)).andReturn(parser).anyTimes();
EasyMock.expect(container.getInstanceNames(FileManager.class)).andReturn(new HashSet<>()).anyTimes();
EasyMock.expect(container.getInstance(FileManager.class)).andReturn(fileManager).anyTimes();
QueryStringParser queryStringParser = new StrutsQueryStringParser(new StrutsUrlDecoder());
EasyMock.expect(container.getInstance(QueryStringParser.class)).andReturn(queryStringParser).anyTimes();
FileManagerFactory fileManagerFactory = new DummyFileManagerFactory();
EasyMock.expect(container.getInstance(FileManagerFactory.class)).andReturn(fileManagerFactory).anyTimes();
EasyMock.replay(container);
ActionContext.of(new HashMap<>())
.withParameters(HttpParameters.create(params).build())
.withServletRequest(request)
.withServletResponse(response)
.withServletContext(context)
.withContainer(container)
.withValueStack(valueStack)
.bind();
}
}
//converter has a protected default constructor...meh
class DummyConverter extends XWorkConverter {
}
class DummyFileManagerFactory implements FileManagerFactory {
public void setReloadingConfigs(String reloadingConfigs) {
}
public FileManager getFileManager() {
return new DefaultFileManager();
}
}
class DummyServletCache extends ServletCache {
public int size() {
return cache.size();
}
}
class ServletGetRunnable implements Runnable {
private ServletCache servletCache;
private Object object;
private CyclicBarrier startBarrier;
private ActionContext actionContext;
private CyclicBarrier endBarrier;
ServletGetRunnable(ServletCache servletCache, CyclicBarrier startBarrier, CyclicBarrier endBarrier, ActionContext actionContext) {
this.servletCache = servletCache;
this.startBarrier = startBarrier;
this.endBarrier = endBarrier;
this.actionContext = actionContext;
}
public void run() {
actionContext = ActionContext.bind(actionContext);
//wait to start all threads at once..or try at least
try {
startBarrier.await();
object = servletCache.get("org/apache/struts2/simple0.jsp");
for (int i = 0; i < 10; i++) {
Object object2 = servletCache.get("org/apache/struts2/simple0.jsp");
if (object2 != object)
throw new RuntimeException("got different object from cache");
}
endBarrier.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public Object getObject() {
return object;
}
}
class CountingClassLoaderInterface extends ClassLoaderInterfaceDelegate {
public Map<String, Integer> counters = new HashMap<>();
public CountingClassLoaderInterface(ClassLoader classLoader) {
super(classLoader);
}
@Override
public InputStream getResourceAsStream(String name) {
Integer counter = counters.get(name);
counter = counter == null ? 1 : counter + 1;
counters.put(name, counter);
return super.getResourceAsStream(name);
}
}
class NotURLClassLoader extends ClassLoader {
NotURLClassLoader(ClassLoader parentClassLoader) {
super(parentClassLoader);
}
}