001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tctest.spring.integrationtests.tests;
006:
007: import org.apache.commons.httpclient.HostConfiguration;
008: import org.apache.commons.httpclient.HttpClient;
009: import org.apache.commons.httpclient.HttpException;
010: import org.apache.commons.httpclient.HttpMethod;
011: import org.apache.commons.httpclient.HttpState;
012: import org.apache.commons.logging.LogFactory;
013: import org.springframework.beans.factory.BeanFactory;
014: import org.springframework.beans.factory.BeanFactoryAware;
015: import org.springframework.beans.factory.ObjectFactory;
016: import org.springframework.beans.factory.config.Scope;
017: import org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter;
018: import org.springframework.web.context.request.HttpRequestAccessor;
019: import org.springframework.web.context.request.RequestAttributes;
020: import org.springframework.web.context.request.RequestContextHolder;
021: import org.springframework.web.context.request.ServletRequestAttributes;
022: import org.springframework.web.context.request.SessionScope;
023: import org.springframework.web.servlet.DispatcherServlet;
024:
025: import com.tc.test.TestConfigObject;
026: import com.tc.test.server.appserver.AppServerFactory;
027: import com.tc.test.server.appserver.deployment.AbstractTwoServerDeploymentTest;
028: import com.tc.test.server.appserver.deployment.DeploymentBuilder;
029: import com.tc.test.server.appserver.deployment.ProxyBuilder;
030: import com.tcspring.ComplexBeanId;
031: import com.tcspring.DistributableBeanFactory;
032: import com.tctest.spring.bean.ISimpleBean;
033: import com.tctest.spring.integrationtests.SpringTwoServerTestSetup;
034:
035: import java.io.IOException;
036: import java.util.Collections;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.Map;
040:
041: import javax.servlet.http.HttpSessionBindingListener;
042:
043: import junit.framework.Test;
044:
045: /**
046: * Test clustering custom scoped bean. This custom scope is a subtype of SessionScope with finer granularity driven by
047: * CONVERSATION parameter of the http request
048: */
049: public class CustomScopedBeanTest extends
050: AbstractTwoServerDeploymentTest {
051: private static final String APP_NAME = "test-customscope";
052:
053: private static final String FASADE_NAME = "TestFasadeService";
054:
055: private ITestFacade beanN1C1; // node1 session1 conv1
056: private ITestFacade beanN1C2; // node1 session1 conv2
057:
058: private ITestFacade beanN2C1; // node2 session1 conv1
059: private ITestFacade beanN2C2; // node2 session1 conv2
060:
061: public CustomScopedBeanTest() {
062: // MNK-352, MNK-353
063: if (AppServerFactory.getCurrentAppServerId() == AppServerFactory.WEBLOGIC) {
064: disableAllUntil("2008-12-01");
065: }
066:
067: disableVariant(TestConfigObject.SPRING_VARIANT, "128");
068: }
069:
070: protected void setUp() throws Exception {
071: try {
072: super .setUp();
073:
074: Map initCtx = new HashMap();
075: initCtx.put(ProxyBuilder.EXPORTER_TYPE_KEY,
076: HttpInvokerServiceExporter.class);
077:
078: HttpClient clientS1C1 = new HttpClientWithParams(
079: Collections.singletonMap(
080: ConversationScope.CONV_KEY, "(1)"));
081: initCtx.put(ProxyBuilder.HTTP_CLIENT_KEY, clientS1C1);
082:
083: beanN1C1 = (ITestFacade) server0.getProxy(
084: ITestFacade.class, APP_NAME + "/http/"
085: + FASADE_NAME, initCtx);
086: beanN2C1 = (ITestFacade) server1.getProxy(
087: ITestFacade.class, APP_NAME + "/http/"
088: + FASADE_NAME, initCtx);
089:
090: HttpClient clientS1C2 = new HttpClientWithParams(
091: Collections.singletonMap(
092: ConversationScope.CONV_KEY, "(2)"));
093: clientS1C2.setState(clientS1C1.getState()); // share state across the clients, they should be in the same session
094: // now
095: initCtx.put(ProxyBuilder.HTTP_CLIENT_KEY, clientS1C2);
096:
097: beanN1C2 = (ITestFacade) server0.getProxy(
098: ITestFacade.class, APP_NAME + "/http/"
099: + FASADE_NAME, initCtx);
100: beanN2C2 = (ITestFacade) server1.getProxy(
101: ITestFacade.class, APP_NAME + "/http/"
102: + FASADE_NAME, initCtx);
103: } catch (Exception e) {
104: e.printStackTrace();
105: throw e;
106: }
107: }
108:
109: public void testSharedFields() throws Exception {
110: beanN1C1.setField("newVal1");
111: beanN2C2.setField("newVal2");
112:
113: assertEquals("Failed to share", "newVal1", beanN2C1.getField());
114: assertEquals("Failed to share", "newVal2", beanN1C2.getField());
115: }
116:
117: public void testScopeId() throws Exception {
118: String id11 = beanN1C1.getConversationId();
119: String id12 = beanN1C2.getConversationId();
120:
121: String id21 = beanN2C1.getConversationId();
122: String id22 = beanN2C2.getConversationId();
123:
124: assertEquals("Unexpected scope", id11, id21);
125: assertEquals("Unexpected scope", id12, id22);
126: }
127:
128: public void testDestructionCallbacks() throws Exception {
129: String conversationId11 = beanN1C1.getConversationId();
130: String conversationId21 = beanN1C2.getConversationId();
131: String conversationId12 = beanN2C1.getConversationId();
132: String conversationId22 = beanN2C2.getConversationId();
133:
134: beanN1C1.setField("newVal11");
135: beanN1C2.setField("newVal12");
136: beanN2C1.setField("newVal21");
137: beanN2C2.setField("newVal22");
138:
139: assertTrue("Failed to create scoped bean", beanN1C1
140: .isInClusteredSingletonCache(conversationId11));
141: assertTrue("Failed to create scoped bean", beanN1C2
142: .isInClusteredSingletonCache(conversationId12));
143: assertTrue("Failed to create scoped bean", beanN2C1
144: .isInClusteredSingletonCache(conversationId21));
145: assertTrue("Failed to create scoped bean", beanN2C2
146: .isInClusteredSingletonCache(conversationId22));
147:
148: beanN1C1.invokeDestructionCallback();
149: beanN2C2.invokeDestructionCallback();
150:
151: assertFalse("Failed to destruct scoped bean", beanN1C1
152: .isInClusteredSingletonCache(conversationId11));
153: assertFalse("Failed to destruct scoped bean", beanN1C2
154: .isInClusteredSingletonCache(conversationId12));
155: assertFalse("Failed to destruct scoped bean", beanN2C1
156: .isInClusteredSingletonCache(conversationId21));
157: assertFalse("Failed to destruct scoped bean", beanN2C2
158: .isInClusteredSingletonCache(conversationId22));
159: }
160:
161: public void testTransientFields() throws Exception {
162: assertEquals("Failed to initialize transient field",
163: "transient-val", beanN1C1.getTransientField());
164: assertEquals("Failed to initialize transient field",
165: "transient-val", beanN1C2.getTransientField());
166: assertEquals("Failed to initialize transient field",
167: "transient-val", beanN2C1.getTransientField());
168: assertEquals("Failed to initialize transient field",
169: "transient-val", beanN2C2.getTransientField());
170:
171: beanN1C1.setTransientField("newVal11");
172: beanN1C2.setTransientField("newVal12");
173: beanN2C1.setTransientField("newVal21");
174: beanN2C2.setTransientField("newVal22");
175:
176: assertEquals("Unexpected sharing", "newVal11", beanN1C1
177: .getTransientField());
178: assertEquals("Unexpected sharing", "newVal12", beanN1C2
179: .getTransientField());
180: assertEquals("Unexpected sharing", "newVal21", beanN2C1
181: .getTransientField());
182: assertEquals("Unexpected sharing", "newVal22", beanN2C2
183: .getTransientField());
184: }
185:
186: private static class InnerTestSetup extends
187: SpringTwoServerTestSetup {
188:
189: private InnerTestSetup() {
190: super (CustomScopedBeanTest.class,
191: "/tc-config-files/customscoped-tc-config.xml",
192: APP_NAME);
193: }
194:
195: protected void configureWar(DeploymentBuilder builder) {
196: builder
197: .addBeanDefinitionFile("classpath:/com/tctest/spring/beanfactory-customscope.xml");
198:
199: builder.addRemoteService(HttpInvokerServiceExporter.class,
200: FASADE_NAME, "testFacade", ITestFacade.class);
201:
202: builder.setDispatcherServlet("httpinvoker", "/http/*",
203: DispatcherServlet.class, null, true);
204: builder
205: .addDirectoryOrJARContainingClass(net.sf.cglib.core.Constants.class);
206: }
207: }
208:
209: public static Test suite() {
210: return new InnerTestSetup();
211: }
212:
213: /**
214: * Custom scope and client
215: */
216: public static class ConversationScope extends SessionScope
217: implements Scope {
218: public static final String CONV_KEY = "CONVERSATION";
219:
220: public String getConversationId() {
221: String conversation = getWebConversationName();
222: return conversation == null ? null : super
223: .getConversationId()
224: + conversation;
225: }
226:
227: public Object get(String name, ObjectFactory objectFactory) {
228: return super .get(getWebConversationName() + name,
229: objectFactory);
230: }
231:
232: public Object remove(String name) {
233: return super .remove(getWebConversationName() + name);
234: }
235:
236: public void registerDestructionCallback(String name,
237: Runnable callback) {
238: super .registerDestructionCallback(getWebConversationName()
239: + name, callback);
240: }
241:
242: public Object getDestructionCallback(String name) {
243: return RequestContextHolder
244: .currentRequestAttributes()
245: .getAttribute(
246: ServletRequestAttributes.DESTRUCTION_CALLBACK_NAME_PREFIX
247: + getWebConversationName() + name,
248: RequestAttributes.SCOPE_SESSION);
249: }
250:
251: private String getWebConversationName() {
252: return HttpRequestAccessor.getRequest(
253: (ServletRequestAttributes) RequestContextHolder
254: .currentRequestAttributes()).getParameter(
255: CONV_KEY);
256: }
257: }
258:
259: public static class HttpClientWithParams extends HttpClient {
260: private Map stickyParameters = null;
261:
262: HttpClientWithParams(Map attributes) {
263: this .stickyParameters = attributes;
264: }
265:
266: public int executeMethod(HostConfiguration hostconfig,
267: final HttpMethod httpMethod, final HttpState state)
268: throws IOException, HttpException {
269: httpMethod.setQueryString(appendParams(httpMethod
270: .getQueryString()));
271: return super .executeMethod(hostconfig, httpMethod, state);
272: }
273:
274: private String appendParams(String queryStr) {
275: StringBuffer sb = new StringBuffer(queryStr == null ? ""
276: : queryStr);
277: if (stickyParameters != null) {
278: for (Iterator iter = this .stickyParameters.entrySet()
279: .iterator(); iter.hasNext();) {
280: if (sb.length() > 0) {
281: sb.append('&');
282: }
283: Map.Entry entry = (Map.Entry) iter.next();
284: sb.append(entry.getKey()).append('=').append(
285: entry.getValue());
286: }
287: }
288: return sb.toString();
289: }
290: }
291:
292: public static interface ITestFacade {
293: public String getConversationId();
294:
295: public String getTransientField();
296:
297: public void setTransientField(String string);
298:
299: public String getField();
300:
301: public void setField(String value);
302:
303: public void invokeDestructionCallback();
304:
305: public boolean isInClusteredSingletonCache(String conversationId);
306: }
307:
308: public static class ConversationScopeTestFacade implements
309: ITestFacade, BeanFactoryAware {
310: private BeanFactory factory;
311: private ConversationScope scope;
312: private ISimpleBean bean;
313: private String beanName;
314:
315: public void setBeanFactory(BeanFactory factory) {
316: this .factory = factory;
317: }
318:
319: public void setScope(ConversationScope scope) {
320: this .scope = scope;
321: }
322:
323: public void setBean(ISimpleBean bean) {
324: this .bean = bean;
325: }
326:
327: public void setField(String value) {
328: bean.setField(value);
329: }
330:
331: public String getField() {
332: return bean.getField();
333: }
334:
335: public String getTransientField() {
336: return bean.getTransientField();
337: }
338:
339: public void setTransientField(String value) {
340: bean.setTransientField(value);
341: }
342:
343: public String getConversationId() {
344: return scope.getConversationId();
345: }
346:
347: public void invokeDestructionCallback() {
348: LogFactory.getLog(getClass()).info(
349: "#### invokeDestructionCallback() " + getBeanName()
350: + " " + scope.getConversationId());
351: HttpSessionBindingListener listener = (HttpSessionBindingListener) scope
352: .getDestructionCallback(getBeanName());
353: listener.valueUnbound(null); // cause unbound
354: }
355:
356: public boolean isInClusteredSingletonCache(String conversationId) {
357: ComplexBeanId beanId = new ComplexBeanId(conversationId,
358: getBeanName());
359: boolean res = ((DistributableBeanFactory) factory)
360: .getBeanContainer(beanId) != null;
361: LogFactory.getLog(getClass()).info(
362: "#### isInClusteredSingletonCache() " + beanId
363: + " " + res);
364: return res;
365: }
366:
367: private String getBeanName() {
368: if (beanName == null) {
369: beanName = bean.getBeanName();
370: }
371: return beanName;
372: }
373:
374: }
375:
376: }
|