001: package org.mockejb.test;
002:
003: import java.rmi.RemoteException;
004: import java.security.Principal;
005:
006: import javax.rmi.PortableRemoteObject;
007:
008: import javax.naming.*;
009: import javax.ejb.*;
010:
011: import org.easymock.MockControl;
012: import org.mockejb.*;
013: import org.mockejb.interceptor.*;
014: import org.mockejb.jndi.*;
015:
016: /**
017: * Main MockEjb test case. Demonstrates the basic scenarios of MockEjb usage.
018: * This test runs in two modes. When Cactus mode is turned on (using
019: * "mockejb.cactus.mode" system property), it will run as the Cactus test (on the server).
020: * In this case, the test class will use EJBs deployed by the app server.
021: * Otherwise it will run outside of the app server under MockContainer.
022: * Cactus mode is supported by OptionalCactusTestCase superclass.
023: *
024: * @author Alexander Ananiev
025: */
026: public class FundamentalsTest extends OptionalCactusTestCase implements
027: Interceptor {
028:
029: // State of this test case. These variables are initialized by setUp method
030: private SampleService sampleService;
031: private SampleServiceHome sampleServiceHome;
032: private Context context;
033:
034: // Aspect system used by this test
035: private AspectSystem aspectSystem;
036:
037: private MockContainer mockContainer;
038:
039: /**
040: * Flag which is set by our custom interceptor if the method that
041: * it checks was called. See testCustomInterceptor.
042: */
043: private boolean wasInvoked = false;
044:
045: public FundamentalsTest(String name) {
046: super (name);
047: }
048:
049: /**
050: * Deploys and creates EJBs needed for our tests.
051: * Note that JUnit runs this method for every test method, but since
052: * MockEjb deployment is purely an in-memory operation,
053: * it does not have any performance penalty.
054: * During deployment, MockEjb simply re-binds EJBs to the same keys in the JNDI tree.
055: */
056: public void setUp() throws Exception {
057:
058: /*
059: * since some of the tests dynamically add interceptors,
060: * we need to initialize our AspectSystem
061: * You may want to use "interceptor.aspect.system.thread" system
062: * property in which case AspectSystem is create per thread so the
063: * tests can run concurrently.
064: */
065: aspectSystem = AspectSystemFactory.getAspectSystem();
066:
067: /* When the test runs on the server (in cactus mode) we want to be able
068: * to look up EJBs and resources already deployed in the container.
069: * This allows us to run the mix of "true" and mock EJBs in the same test.
070: */
071: if (isRunningOnServer()) {
072:
073: /* MockContext will look up objects in the tree of the
074: * app server if its environment is set as the delegate.
075: * Since we're already inside the app server, we assume that we can use
076: * its initial context to delegate to.
077: * Note that we must do it before we set MockContextFactory as initial.
078: */
079: MockContextFactory.setDelegateContext(new InitialContext());
080: }
081:
082: /* We need to set MockContextFactory as our provider.
083: * This method sets the necessary system properties.
084: * We can't simply pass hashtable to the InitialContext
085: * because our tested EJBs have "new InitialContext()" in their code.
086: * So we assume that all participants of this test don't use
087: * the constructor of the InitialContext that takes Hashtable.
088: */
089: MockContextFactory.setAsInitial();
090:
091: // create the initial context that will be used for binding EJBs
092: context = new InitialContext();
093:
094: /* Create an instance of the MockContainer.
095: * MockContainer also cleans all currently added aspects/interceptors and
096: * adds system interceptors that it needs, such as ExceptionHandler
097: * to make sure that we always start from a known state.
098: */
099: mockContainer = new MockContainer(context);
100:
101: // if the test runs outside of the container (app server)
102: if (!isRunningOnServer()) {
103:
104: /* Deploy EJBs to the MockContainer if we run outside of the app server
105: * In cactus mode all but one EJB are deployed by the app server, so we don't need to
106: * do it.
107: */
108:
109: /* Create deployment descriptor of our sample bean.
110: * MockEjb does not support XML descriptors.
111: */
112: SessionBeanDescriptor sampleServiceDescriptor = new SessionBeanDescriptor(
113: SampleService.JNDI_NAME, SampleServiceHome.class,
114: SampleService.class, new SampleServiceBean());
115: // Deploy operation simply creates Home and binds it to JNDI
116: mockContainer.deploy(sampleServiceDescriptor);
117:
118: // StatelessSampleBean calls SampleHelperBean, so we need to deploy it too
119:
120: /* MockEjb does not currently support true bean-scoped JNDI context, so
121: * every JNDI name must be unique within the test.
122: * Therefore we use JNDI name required by SampleBean (configured via ejb-ref in the real EJB)
123: * to access HelperBean
124: */
125: SessionBeanDescriptor helperBeanDescriptor = new SessionBeanDescriptor(
126: SampleServiceBean.HELPER_BEAN_JNDI_NAME,
127: SampleHelperHome.class, SampleHelper.class,
128: new SampleHelperBean());
129: mockContainer.deploy(helperBeanDescriptor);
130:
131: }
132:
133: /*
134: * Deploy mock implementation of the ExternalService session bean.
135: * ExternalService is not part of the unit we're testing (SampleBean),
136: * so we don't want to rely on its implementation (which might depend on
137: * other session beans or other resources).
138: * We use EasyMock framework to create an implementation that
139: * simply returns mock test data.
140: * If you run this class inside the container and real ExternalService session
141: * bean is deployed, our mock
142: * implementation will override it (just for the duration of the test).
143: */
144: // use easymock to program mock bean. Note that you don't need to worry about callback
145: // methods, MockEJB "understands" that the class is a mock class and won't try to
146: // call callback methods on it.
147: MockControl externalServiceControl = MockControl
148: .createControl(ExternalService.class);
149: ExternalService externalServiceBean = (ExternalService) externalServiceControl
150: .getMock();
151: externalServiceBean.sampleMethod();
152: externalServiceControl.setReturnValue("sample string");
153: externalServiceControl.replay();
154:
155: mockContainer.deploy(new SessionBeanDescriptor(
156: ExternalService.JNDI_NAME, ExternalServiceHome.class,
157: ExternalService.class, externalServiceBean));
158: // All EJBs are now deployed
159:
160: // To get the Sample bean we use the standard J2EE routine
161:
162: // Lookup the home
163: Object sampleHomeObj = context.lookup(SampleService.JNDI_NAME);
164:
165: // PortableRemoteObject does not do anything in our case but we can still call it
166: sampleServiceHome = (SampleServiceHome) PortableRemoteObject
167: .narrow(sampleHomeObj, SampleServiceHome.class);
168:
169: // create the bean
170: sampleService = sampleServiceHome.create();
171:
172: }
173:
174: /**
175: * Performs the necessary cleanup by restoring the system properties that
176: * were modified by MockContextFactory.setAsInitial().
177: * This is needed in case if the test runs inside the container, so it would
178: * not affect the tests that run after it.
179: */
180: public void tearDown() {
181:
182: // Inside the container this method does not do anything
183: MockContextFactory.revertSetAsInitial();
184: }
185:
186: /**
187: * Tests standard Home and EJBMetaData methods.
188: */
189: public void testHome() throws Exception {
190:
191: // Test the support of EJB metadata
192: EJBMetaData ejbMetaData = sampleServiceHome.getEJBMetaData();
193:
194: assertEquals(ejbMetaData.getHomeInterfaceClass(),
195: SampleServiceHome.class);
196:
197: // note how you can use simple "equals" with EJBs and their homes
198: assertEquals(sampleServiceHome, ejbMetaData.getEJBHome());
199:
200: // toString also supported
201: System.out.println(sampleServiceHome);
202:
203: // Test standard EJB methods
204: assertEquals(sampleService.getEJBHome(), sampleServiceHome);
205: assertTrue(sampleService.isIdentical(sampleService));
206:
207: /* make sure that we can serialize home and the bean
208: * in case if we run outside of the app server
209: */
210: if (!isRunningOnServer()) {
211: java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
212: java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(
213: baos);
214: oos.writeObject(sampleServiceHome);
215: oos.writeObject(sampleService);
216: oos.close();
217: }
218:
219: }
220:
221: /**
222: * Tests simple EJB invocations
223: */
224: public void testSimpleCalls() throws Exception {
225: // Call a simple business method
226: String s = sampleService.echoString("test");
227: assertEquals(s, "test");
228:
229: // now call the method that invokes another bean
230: assertNotNull(sampleService.invokeOtherBean());
231:
232: // now make a call to the external service
233: sampleService.invokeExternalService();
234:
235: /* We can even examine the internal state of the bean if there is a need.
236: * Any EJB created by MockEJB can be casted to the special interface that
237: * provides access to the bean and its context
238: */
239: SampleServiceBean sampleServiceBean = (SampleServiceBean) ((EjbBeanAccess) sampleService)
240: .getBean();
241: // we have no state to check for this test. Just making sure that it works
242: assertNotNull(sampleServiceBean);
243: }
244:
245: /**
246: * Test MockEJB exception handling.
247: * According to the EJB spec, system exception must be wrapped in
248: * RemoteException.
249: * The exception from the local interface must be wrapped in EJBException.
250: * MockEjb treats all runtime and transaction-related exceptions as system exceptions.
251: */
252: public void testExceptionHandling() throws Exception {
253:
254: try {
255: sampleService.throwSystemException();
256: } catch (RemoteException remoteEx) {
257: assertTrue(remoteEx.detail instanceof EJBException);
258: assertTrue(((EJBException) remoteEx.detail)
259: .getCausedByException() instanceof RuntimeException);
260: }
261:
262: // Example of the application exception.
263: // Container should pass this exception through without changes.
264: try {
265: sampleService.throwAppException();
266: fail("We did not get the application exception ");
267: } catch (Exception ex) {
268: }
269: }
270:
271: /**
272: * Demonstrates the use of custom interceptors.
273: * This test class implements Interceptor interface. It will verify
274: * that not-null parameter is passed to HelperBean.
275: * The same thing can be done using InvocationRecorder
276: * (see testInvocationRecorder).
277: * We can only run this test if it is being executed outside of the
278: * container.
279: */
280: public void testCustomInterceptor() throws Exception {
281:
282: // This won't work in the container since it does not support interceptors
283: if (isRunningOnServer())
284: return;
285:
286: // Set our custom interceptor so it would handle all calls to SampleHelper interface (w/o subclasses)
287: aspectSystem.add(new ClassPointcut(SampleHelper.class, false),
288: this );
289:
290: // flag indicating that the EJB under test was called
291: wasInvoked = false;
292:
293: // we can now invoke our test method
294: // In case of problems, "asserts" in the invoker will fail the test.
295: // This method calls HelperBean method
296: sampleService.invokeOtherBean();
297: // we need to make sure that the method was invoked
298: assertTrue(wasInvoked);
299:
300: }
301:
302: /**
303: * InvocationRecorder interceptor provides an easy way to inspect the
304: * data about the calls.
305: * If it added to the interceptor list, it will record the information about
306: * all calls to the bean.
307: * We'll use it to check that SampleBean called HelperBean and passed non-null
308: * parameter.
309: * We can only run this test if it is being executed outside of the
310: * container.
311: */
312: public void testInvocationRecorder() throws Exception {
313:
314: // This won't work in the container since it does not support interceptors
315: if (isRunningOnServer())
316: return;
317:
318: InvocationRecorder recorder = new InvocationRecorder();
319:
320: // Set the recorder so it would record all calls to SampleHelper interface (w/o subclasses)
321: aspectSystem.add(new ClassPointcut(SampleHelper.class, false),
322: recorder);
323:
324: /*
325: * We can also capture the calls to the create method of the SampleHelperHome
326: * We don't really need to do it here, it just demonstrates that you can intercept
327: * "create" methods.
328: */
329: aspectSystem.add(new MethodPatternPointcut(
330: "SampleHelperHome\\.create()"), recorder);
331:
332: /*
333: * Or how about intercepting JNDI lookups?
334: */
335: aspectSystem.add(new MethodPatternPointcut("Context\\.lookup"),
336: recorder);
337:
338: // recorder is now active, it will preserve the info about all calls to SampleHelper
339:
340: // we can now invoke our test method
341: // This method calls HelperBean method
342: sampleService.invokeOtherBean();
343:
344: // Make sure that the method of our interest was invoked
345:
346: // Tests that the JNDI lookup was called
347: assertNotNull(recorder.findByProxyMethod("Context\\.lookup"));
348:
349: // Test that the lifecycle methods were invoked by the container (triggered by "create")
350: assertNotNull(recorder
351: .findByProxyMethod("SampleHelperHome\\.create()"));
352:
353: // Test that the dummy method was invoked
354: InvocationContext dummyMethodInvocation = recorder
355: .findByTargetMethod("dummyMethod");
356: assertNotNull(dummyMethodInvocation);
357: // make sure that the parameter was not null
358: assertNotNull(dummyMethodInvocation.getParamVals()[0]);
359:
360: // since the aspect system is re-initialized in setUp we don't need to
361: // clean the recorder we just added
362: }
363:
364: /**
365: * This method demonstrates how to test beans that makes the use of
366: * use of the security-related methods of EJBContext (getCallerPrincipal, isCallerInRole).
367: */
368: public void testSecurity() throws Exception {
369:
370: /* Create the mock user and "login". Login method
371: * simply puts the user on the thread so EJBContext implementation
372: * can get to it.
373: * This will create user without any roles.
374: */
375: mockContainer.login(new MockUser("testuser"));
376: Principal principal = sampleService.getPrincipal();
377: assertEquals("testuser", principal.getName());
378:
379: // Now let's add roles
380: mockContainer.login(new MockUser("testuser", new String[] {
381: "role1", "role2" }));
382: // "hasRole" simply returns the result of "isCallerInRole"
383: assertTrue(sampleService.hasRole("role1"));
384: assertTrue(sampleService.hasRole("role2"));
385: assertFalse(sampleService.hasRole("fake_role"));
386:
387: // If you don't login, MockContainer logs in as anonymous user,
388: // which you can do as well:
389: mockContainer.login(MockUser.ANONYMOUS_USER);
390: }
391:
392: /**
393: * Implementation of the Interceptor interface that can be used
394: * for testing various preconditions/postconditions of the EJB call.
395: * Here we use it for very simple check to make sure that non-null parameter
396: * is passed to the HelperBean.
397: */
398: public void intercept(InvocationContext invocationContext)
399: throws Exception {
400:
401: // Make sure that we run the test for the right method
402: if (SampleHelperBean.class.isInstance(invocationContext
403: .getTargetObject())
404: && invocationContext.getTargetMethod().getName()
405: .equals("dummyMethod")) {
406: System.out.println("Running test invoker for "
407: + invocationContext.getTargetMethod());
408: // Test that SampleBean is passing parameter to the HelperBean
409: assertNotNull(invocationContext.getParamVals()[0]);
410: wasInvoked = true;
411: }
412:
413: // We're done, move to the next interceptor from the list
414: invocationContext.proceed();
415: }
416:
417: /**
418: * Example of a mock session bean implementing ExternalService interface.
419: * Note how MockEJB allows to specify a bean implementation without having to
420: * provide mandatory callback methods.
421: * This class is not used. It is here only for demo purposes.
422: * We use Easymock to create mock implementation
423: * classes.
424: */
425: public static class ExternalServiceMockBean {
426:
427: public String sampleMethod() {
428: return "sample result";
429: }
430:
431: }
432:
433: }
|