001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.core.stateless;
017:
018: import junit.framework.TestCase;
019: import org.apache.openejb.core.ivm.naming.InitContextFactory;
020: import org.apache.openejb.config.ConfigurationFactory;
021: import org.apache.openejb.config.EjbModule;
022: import org.apache.openejb.assembler.classic.Assembler;
023: import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
024: import org.apache.openejb.assembler.classic.TransactionServiceInfo;
025: import org.apache.openejb.assembler.classic.SecurityServiceInfo;
026: import org.apache.openejb.assembler.classic.StatelessSessionContainerInfo;
027: import org.apache.openejb.assembler.classic.EjbJarInfo;
028: import org.apache.openejb.jee.EjbJar;
029: import org.apache.openejb.jee.StatelessBean;
030: import org.apache.openejb.loader.SystemInstance;
031: import org.apache.openejb.spi.ContainerSystem;
032: import org.apache.openejb.DeploymentInfo;
033: import org.apache.openejb.RpcContainer;
034:
035: import javax.interceptor.AroundInvoke;
036: import javax.interceptor.InvocationContext;
037: import javax.interceptor.Interceptors;
038: import javax.jws.WebService;
039: import javax.xml.ws.handler.MessageContext;
040: import javax.xml.ws.WebServiceContext;
041: import javax.annotation.Resource;
042: import javax.ejb.SessionContext;
043: import java.util.ArrayList;
044: import java.util.Set;
045: import java.util.Collection;
046: import java.util.Map;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Arrays;
050: import java.lang.reflect.Method;
051:
052: /**
053: * The point of this test case is to verify that OpenEJB is accurately performing
054: * it's part of a WebServiceProvider to OpenEJB invocation as it relates to JAX-RPC.
055: *
056: * In the agreement between OpenEJB and the Web Service Provider, the Web Service Provider
057: * must supply the MessageContext and an Interceptor as the arguments of the standard
058: * container.invoke method call.
059: *
060: * OpenEJB must ensure the MessageContext is exposed via the SessionContext.getMessageContext
061: * and ensure that the interceptor is added to the chain just after the other interceptors and
062: * before the bean method itself is invoked.
063: *
064: * @version $Rev: 602704 $ $Date: 2007-12-09 09:58:22 -0800 $
065: */
066: public class JaxWsInvocationTest extends TestCase {
067:
068: public void testWsInvocations() throws Exception {
069: System.setProperty(
070: javax.naming.Context.INITIAL_CONTEXT_FACTORY,
071: InitContextFactory.class.getName());
072:
073: ConfigurationFactory config = new ConfigurationFactory();
074: Assembler assembler = new Assembler();
075:
076: assembler.createProxyFactory(config
077: .configureService(ProxyFactoryInfo.class));
078: assembler.createTransactionManager(config
079: .configureService(TransactionServiceInfo.class));
080: assembler.createSecurityService(config.configureService(
081: SecurityServiceInfo.class, "PseudoSecurityService",
082: null, "PseudoSecurityService", null));
083:
084: assembler.createContainer(config
085: .configureService(StatelessSessionContainerInfo.class));
086:
087: EjbJarInfo ejbJar = config.configureApplication(buildTestApp());
088:
089: assembler.createApplication(ejbJar);
090:
091: ContainerSystem containerSystem = SystemInstance.get()
092: .getComponent(ContainerSystem.class);
093:
094: DeploymentInfo deploymentInfo = containerSystem
095: .getDeploymentInfo("EchoBean");
096:
097: assertNotNull(deploymentInfo);
098:
099: assertEquals("ServiceEndpointInterface",
100: EchoServiceEndpoint.class, deploymentInfo
101: .getServiceEndpointInterface());
102:
103: // OK, Now let's fake a web serivce invocation coming from any random
104: // web service provider. The web serivce provider needs supply
105: // the MessageContext and an interceptor to do the marshalling as
106: // the arguments of the standard container.invoke signature.
107:
108: // So let's create a fake message context.
109: MessageContext messageContext = new FakeMessageContext();
110:
111: // Now let's create a fake interceptor as would be supplied by the
112: // web service provider. Instead of writing "fake" marshalling
113: // code that would pull the arguments from the soap message, we'll
114: // just give it the argument values directly.
115: Object wsProviderInterceptor = new FakeWsProviderInterceptor(
116: "Hello world");
117:
118: // Ok, now we have the two arguments expected on a JAX-RPC Web Service
119: // invocation as per the OpenEJB-specific agreement between OpenEJB
120: // and the Web Service Provider
121: Object[] args = new Object[] { messageContext,
122: wsProviderInterceptor };
123:
124: // Let's grab the container as the Web Service Provider would do and
125: // perform an invocation
126: RpcContainer container = (RpcContainer) deploymentInfo
127: .getContainer();
128:
129: Method echoMethod = EchoServiceEndpoint.class.getMethod("echo",
130: String.class);
131:
132: String value = (String) container.invoke("EchoBean", echoMethod
133: .getDeclaringClass(), echoMethod, args, null);
134:
135: assertCalls(Call.values());
136: calls.clear();
137: assertEquals("Hello world", value);
138:
139: }
140:
141: private void assertCalls(Call... expectedCalls) {
142: List expected = Arrays.asList(expectedCalls);
143: assertEquals(join("\n", expected), join("\n", calls));
144: }
145:
146: public static enum Call {
147: EjbInterceptor_Invoke_BEFORE, Bean_Invoke_BEFORE, WebServiceProvider_Invoke_BEFORE, Bean_Invoke, WebServiceProvider_Invoke_AFTER, Bean_Invoke_AFTER, EjbInterceptor_Invoke_AFTER,
148: }
149:
150: public static List<Call> calls = new ArrayList<Call>();
151:
152: public EjbModule buildTestApp() {
153: EjbJar ejbJar = new EjbJar();
154:
155: StatelessBean bean = ejbJar
156: .addEnterpriseBean(new StatelessBean(EchoBean.class));
157: bean.setServiceEndpoint(EchoServiceEndpoint.class.getName());
158:
159: return new EjbModule(this .getClass().getClassLoader(), this
160: .getClass().getSimpleName(), "test", ejbJar, null);
161: }
162:
163: @Interceptors({PlainEjbInterceptor.class})
164: @WebService
165: public static class EchoBean {
166:
167: @Resource
168: private SessionContext ctx;
169:
170: @Resource
171: private WebServiceContext wsContext;
172:
173: @AroundInvoke
174: public Object invoke(InvocationContext context)
175: throws Exception {
176:
177: /**
178: * For JAX-WS invocations context.getContextData() must return the
179: * JAX-WS MessageContex. As per the agreement between OpenEJB and the Web Service Provider
180: * the MessageContex should have been passed into the container.invoke method
181: * and the container should then ensure it's available via getContextData()
182: * for the duration of this call.
183: */
184: MessageContext messageContext = (MessageContext) context
185: .getContextData();
186:
187: junit.framework.Assert.assertNotNull(
188: "message context should not be null",
189: messageContext);
190: junit.framework.Assert
191: .assertTrue(
192: "the Web Service Provider's message context should be used",
193: messageContext instanceof FakeMessageContext);
194:
195: // Try to get JAX-RPC context, should throw an exception since it's JAX-WS
196: try {
197: ctx.getMessageContext();
198: junit.framework.Assert.fail("Did not throw exception");
199: } catch (IllegalStateException e) {
200: // that's expected since it's JAX-WS
201: }
202:
203: // test @Resource WebServiceContext injection
204: junit.framework.Assert
205: .assertNotNull(
206: "web service context should not be null",
207: wsContext);
208: junit.framework.Assert.assertEquals(
209: "msg context should be the smae", messageContext,
210: wsContext.getMessageContext());
211:
212: junit.framework.Assert.assertFalse("user in role 'foo'",
213: wsContext.isUserInRole("foo"));
214: junit.framework.Assert.assertNotNull("user principal",
215: wsContext.getUserPrincipal());
216:
217: calls.add(Call.Bean_Invoke_BEFORE);
218: Object o = context.proceed();
219: calls.add(Call.Bean_Invoke_AFTER);
220: return o;
221: }
222:
223: public String echo(String data) {
224: calls.add(Call.Bean_Invoke);
225: return data;
226: }
227: }
228:
229: @WebService
230: public static interface EchoServiceEndpoint {
231: String echo(String data);
232: }
233:
234: /**
235: * This interceptor is here to ensure that the container
236: * still invokes interceptors normally for web serivce
237: * invocations and to also guarantee that the Web Service
238: * Provider's interceptor (which is a special OpenEJB concept)
239: * is invoked *after* all the normal ejb interceptors.
240: */
241: public static class PlainEjbInterceptor {
242:
243: @AroundInvoke
244: public Object invoke(InvocationContext context)
245: throws Exception {
246: // Track this call so we can assert proper interceptor order
247: calls.add(Call.EjbInterceptor_Invoke_BEFORE);
248: Object o = context.proceed();
249: calls.add(Call.EjbInterceptor_Invoke_AFTER);
250: return o;
251: }
252: }
253:
254: private static String join(String delimeter, List items) {
255: StringBuffer sb = new StringBuffer();
256: for (Object item : items) {
257: sb.append(item.toString()).append(delimeter);
258: }
259: return sb.toString();
260: }
261:
262: /**
263: * This object would be implemented by the Web Service Provider per
264: * the JAX-WS spec and supplied to us in the container.invoke method
265: * per the OpenEJB-WebServiceProvider agreement
266: */
267: public static class FakeMessageContext implements MessageContext {
268:
269: private Map map = new HashMap();
270:
271: public void clear() {
272: map.clear();
273: }
274:
275: public boolean containsKey(Object key) {
276: return map.containsKey(key);
277: }
278:
279: public boolean containsValue(Object value) {
280: return map.containsValue(value);
281: }
282:
283: public Set<Entry<String, Object>> entrySet() {
284: return map.entrySet();
285: }
286:
287: public Object get(Object key) {
288: return map.get(key);
289: }
290:
291: public boolean isEmpty() {
292: return map.isEmpty();
293: }
294:
295: public Set<String> keySet() {
296: return map.keySet();
297: }
298:
299: public Object put(String key, Object value) {
300: return map.put(key, value);
301: }
302:
303: public void putAll(Map<? extends String, ? extends Object> t) {
304: map.putAll(t);
305: }
306:
307: public Object remove(Object key) {
308: return map.remove(key);
309: }
310:
311: public int size() {
312: return map.size();
313: }
314:
315: public Collection<Object> values() {
316: return map.values();
317: }
318:
319: public Scope getScope(String arg0) {
320: return null;
321: }
322:
323: public void setScope(String arg0, Scope arg1) {
324: }
325:
326: }
327:
328: /**
329: * This object would be supplied by the Web Service Provider
330: * as per the OpenEJB-WebServiceProvider agreement and serves
331: * two purposes:
332: *
333: * 1. Executing the Handler Chain (as required by
334: * the JAX-RPC specification) in the context of the EJB Container
335: * (as required by the EJB and J2EE WebServices specifications)
336: *
337: * 2. Unmarshalling the method arguments from the SOAP message
338: * after the handlers in the Handler Chain have had a chance
339: * to modify the argument values via the SAAJ tree.
340: *
341: * The Interceptor instance given to OpenEJB is constructed
342: * and created by the Web Service Provider and should contain
343: * all the data it requires to complete it's part of the agreement.
344: *
345: * OpenEJB will not perform any injection on this object and
346: * the interceptor will be discarded so that the Web Service
347: * Provider may pass in a new Interceptor instance on every
348: * web service invocation.
349: *
350: * The Web Service Provider may pass in any object to serve
351: * the roll of the Interceptor as long as it has an @AroundInvoke
352: * method using the method signature:
353: *
354: * public Object <METHOD-NAME> (InvocationContext ctx) throws Exception
355: *
356: * Unlike typical EJB Interceptor around invoke methods, the @AroundInvoke
357: * annotation must be used and is not optional, and the method must be public.
358: */
359: public static class FakeWsProviderInterceptor {
360:
361: /**
362: * These would normally come from the soap message
363: */
364: private final Object[] args;
365:
366: public FakeWsProviderInterceptor(Object... args) {
367: this .args = args;
368: }
369:
370: @AroundInvoke
371: public Object invoke(InvocationContext invocationContext)
372: throws Exception {
373: // The interceptor of the web serivce must set the
374: // arguments it marshalls from the soap message into
375: // the InvocationContext so we can invoke the bean.
376: invocationContext.setParameters(args);
377:
378: Object returnValue;
379: try {
380:
381: // Track this call so we can assert proper interceptor order
382: calls.add(Call.WebServiceProvider_Invoke_BEFORE);
383:
384: // handler chain "before advice" would happen here
385: returnValue = invocationContext.proceed();
386: // handler chain "after advice" would happen here
387:
388: // Track this call so we can assert proper interceptor order
389: calls.add(Call.WebServiceProvider_Invoke_AFTER);
390:
391: } catch (Exception e) {
392: // handler chain fault processing would happen here
393: throw e;
394: }
395: return returnValue;
396: }
397: }
398: }
|