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.xml.rpc.handler.MessageContext;
039: import javax.annotation.Resource;
040: import javax.ejb.SessionContext;
041: import java.util.ArrayList;
042: import java.util.List;
043: import java.util.Arrays;
044: import java.util.Iterator;
045: import java.lang.reflect.Method;
046:
047: /**
048: * The point of this test case is to verify that OpenEJB is accurately performing
049: * it's part of a WebServiceProvider to OpenEJB invocation as it relates to JAX-RPC.
050: *
051: * In the agreement between OpenEJB and the Web Service Provider, the Web Service Provider
052: * must supply the MessageContext and an Interceptor as the arguments of the standard
053: * container.invoke method call.
054: *
055: * OpenEJB must ensure the MessageContext is exposed via the SessionContext.getMessageContext
056: * and ensure that the interceptor is added to the chain just after the other interceptors and
057: * before the bean method itself is invoked.
058: *
059: * @version $Rev: 607045 $ $Date: 2007-12-27 04:10:23 -0800 $
060: */
061: public class JaxRpcInvocationTest extends TestCase {
062: public void testWebServiceInvocations() throws Exception {
063: System.setProperty(
064: javax.naming.Context.INITIAL_CONTEXT_FACTORY,
065: InitContextFactory.class.getName());
066:
067: ConfigurationFactory config = new ConfigurationFactory();
068: Assembler assembler = new Assembler();
069:
070: assembler.createProxyFactory(config
071: .configureService(ProxyFactoryInfo.class));
072: assembler.createTransactionManager(config
073: .configureService(TransactionServiceInfo.class));
074: assembler.createSecurityService(config.configureService(
075: SecurityServiceInfo.class, "PseudoSecurityService",
076: null, "PseudoSecurityService", null));
077:
078: assembler.createContainer(config
079: .configureService(StatelessSessionContainerInfo.class));
080:
081: EjbJarInfo ejbJar = config.configureApplication(buildTestApp());
082:
083: assembler.createApplication(ejbJar);
084:
085: ContainerSystem containerSystem = SystemInstance.get()
086: .getComponent(ContainerSystem.class);
087:
088: DeploymentInfo deploymentInfo = containerSystem
089: .getDeploymentInfo("EchoBean");
090:
091: assertNotNull(deploymentInfo);
092:
093: assertEquals("ServiceEndpointInterface",
094: EchoServiceEndpoint.class, deploymentInfo
095: .getServiceEndpointInterface());
096:
097: // OK, Now let's fake a web serivce invocation coming from any random
098: // web service provider. The web serivce provider needs supply
099: // the MessageContext and an interceptor to do the marshalling as
100: // the arguments of the standard container.invoke signature.
101:
102: // So let's create a fake message context.
103: MessageContext messageContext = new FakeMessageContext();
104:
105: // Now let's create a fake interceptor as would be supplied by the
106: // web service provider. Instead of writing "fake" marshalling
107: // code that would pull the arguments from the soap message, we'll
108: // just give it the argument values directly.
109: Object wsProviderInterceptor = new FakeWsProviderInterceptor(
110: "Hello world");
111:
112: // Ok, now we have the two arguments expected on a JAX-RPC Web Service
113: // invocation as per the OpenEJB-specific agreement between OpenEJB
114: // and the Web Service Provider
115: Object[] args = new Object[] { messageContext,
116: wsProviderInterceptor };
117:
118: // Let's grab the container as the Web Service Provider would do and
119: // perform an invocation
120: RpcContainer container = (RpcContainer) deploymentInfo
121: .getContainer();
122:
123: Method echoMethod = EchoServiceEndpoint.class.getMethod("echo",
124: String.class);
125:
126: String value = (String) container.invoke("EchoBean", echoMethod
127: .getDeclaringClass(), echoMethod, args, null);
128:
129: assertCalls(Call.values());
130: calls.clear();
131: assertEquals("Hello world", value);
132:
133: }
134:
135: private void assertCalls(Call... expectedCalls) {
136: List expected = Arrays.asList(expectedCalls);
137: assertEquals("Interceptor call stack", join("\n", expected),
138: join("\n", calls));
139: }
140:
141: public static enum Call {
142: EjbInterceptor_Invoke_BEFORE, Bean_Invoke_BEFORE, WebServiceProvider_Invoke_BEFORE, Bean_Invoke, WebServiceProvider_Invoke_AFTER, Bean_Invoke_AFTER, EjbInterceptor_Invoke_AFTER,
143: }
144:
145: public static List<Call> calls = new ArrayList<Call>();
146:
147: public EjbModule buildTestApp() {
148: EjbJar ejbJar = new EjbJar();
149:
150: StatelessBean bean = ejbJar
151: .addEnterpriseBean(new StatelessBean(EchoBean.class));
152: bean.setServiceEndpoint(EchoServiceEndpoint.class.getName());
153:
154: return new EjbModule(this .getClass().getClassLoader(), this
155: .getClass().getSimpleName(), "test", ejbJar, null);
156: }
157:
158: @Interceptors({PlainEjbInterceptor.class})
159: public static class EchoBean {
160:
161: @Resource
162: private SessionContext ctx;
163:
164: @AroundInvoke
165: public Object invoke(InvocationContext context)
166: throws Exception {
167:
168: /**
169: * For JAX-RPC invocations the JAX-RPC MessageContex must be
170: * available in the javax.ejb.SessionContext via the getMessageContext
171: * method. As per the agreement between OpenEJB and the Web Service Provider
172: * the MessageContex should have been passed into the container.invoke method
173: * and the container should then ensure it's available via the SessionContext
174: * for the duration of this call.
175: */
176: MessageContext messageContext = ctx.getMessageContext();
177:
178: junit.framework.Assert.assertNotNull(
179: "message context should not be null",
180: messageContext);
181: junit.framework.Assert
182: .assertTrue(
183: "the Web Service Provider's message context should be used",
184: messageContext instanceof FakeMessageContext);
185:
186: calls.add(Call.Bean_Invoke_BEFORE);
187: Object o = context.proceed();
188: calls.add(Call.Bean_Invoke_AFTER);
189: return o;
190: }
191:
192: public String echo(String data) {
193: calls.add(Call.Bean_Invoke);
194: return data;
195: }
196: }
197:
198: public static interface EchoServiceEndpoint extends java.rmi.Remote {
199: String echo(String data);
200: }
201:
202: /**
203: * This interceptor is here to ensure that the container
204: * still invokes interceptors normally for web serivce
205: * invocations and to also guarantee that the Web Service
206: * Provider's interceptor (which is a special OpenEJB concept)
207: * is invoked *after* all the normal ejb interceptors.
208: */
209: public static class PlainEjbInterceptor {
210:
211: @AroundInvoke
212: public Object invoke(InvocationContext context)
213: throws Exception {
214: // Track this call so we can assert proper interceptor order
215: calls.add(Call.EjbInterceptor_Invoke_BEFORE);
216: Object o = context.proceed();
217: calls.add(Call.EjbInterceptor_Invoke_AFTER);
218: return o;
219: }
220: }
221:
222: private static String join(String delimeter, List items) {
223: StringBuffer sb = new StringBuffer();
224: for (Object item : items) {
225: sb.append(item.toString()).append(delimeter);
226: }
227: return sb.toString();
228: }
229:
230: /**
231: * This object would be implemented by the Web Service Provider per
232: * the JAX-RPC spec and supplied to us in the container.invoke method
233: * per the OpenEJB-WebServiceProvider agreement
234: */
235: public static class FakeMessageContext implements MessageContext {
236: public void setProperty(String string, Object object) {
237: }
238:
239: public Object getProperty(String string) {
240: return null;
241: }
242:
243: public void removeProperty(String string) {
244: }
245:
246: public boolean containsProperty(String string) {
247: return false;
248: }
249:
250: public Iterator getPropertyNames() {
251: return null;
252: }
253:
254: }
255:
256: /**
257: * This object would be supplied by the Web Service Provider
258: * as per the OpenEJB-WebServiceProvider agreement and serves
259: * two purposes:
260: *
261: * 1. Executing the Handler Chain (as required by
262: * the JAX-RPC specification) in the context of the EJB Container
263: * (as required by the EJB and J2EE WebServices specifications)
264: *
265: * 2. Unmarshalling the method arguments from the SOAP message
266: * after the handlers in the Handler Chain have had a chance
267: * to modify the argument values via the SAAJ tree.
268: *
269: * The Interceptor instance given to OpenEJB is constructed
270: * and created by the Web Service Provider and should contain
271: * all the data it requires to complete it's part of the agreement.
272: *
273: * OpenEJB will not perform any injection on this object and
274: * the interceptor will be discarded so that the Web Service
275: * Provider may pass in a new Interceptor instance on every
276: * web service invocation.
277: *
278: * The Web Service Provider may pass in any object to serve
279: * the roll of the Interceptor as long as it has an @AroundInvoke
280: * method using the method signature:
281: *
282: * public Object <METHOD-NAME> (InvocationContext ctx) throws Exception
283: *
284: * Unlike typical EJB Interceptor around invoke methods, the @AroundInvoke
285: * annotation must be used and is not optional, and the method must be public.
286: */
287: public static class FakeWsProviderInterceptor {
288:
289: /**
290: * These would normally come from the soap message
291: */
292: private final Object[] args;
293:
294: public FakeWsProviderInterceptor(Object... args) {
295: this .args = args;
296: }
297:
298: @AroundInvoke
299: public Object invoke(InvocationContext invocationContext)
300: throws Exception {
301: // The interceptor of the web serivce must set the
302: // arguments it marshalls from the soap message into
303: // the InvocationContext so we can invoke the bean.
304: invocationContext.setParameters(args);
305:
306: Object returnValue;
307: try {
308:
309: // Track this call so we can assert proper interceptor order
310: calls.add(Call.WebServiceProvider_Invoke_BEFORE);
311:
312: // handler chain "before advice" would happen here
313: returnValue = invocationContext.proceed();
314: // handler chain "after advice" would happen here
315:
316: // Track this call so we can assert proper interceptor order
317: calls.add(Call.WebServiceProvider_Invoke_AFTER);
318:
319: } catch (Exception e) {
320: // handler chain fault processing would happen here
321: throw e;
322: }
323: return returnValue;
324: }
325: }
326: }
|