001: package org.mockejb.test.entity;
002:
003: import java.util.*;
004: import javax.naming.*;
005:
006: import junit.framework.TestCase;
007: import org.easymock.MockControl;
008:
009: import org.mockejb.*;
010: import org.mockejb.interceptor.*;
011: import org.mockejb.jndi.*;
012:
013: /**
014: * Demonstrates CMP Entity bean support.
015: * MockEJB uses CGLIB to dynamically generate implementation classes
016: * for abstract entity bean classes.
017: * MockEJB fully supports CMP field getters/setters.
018: * The rest of the container's support (such as finders and CMR) has to be implemented using
019: * Aspect/Interceptor framework as shown below.
020: */
021: public class EntityBeanTest extends TestCase {
022:
023: // JNDI context and MockContainer instance used by all tests in this class
024: private MockContainer mockContainer;
025: private Context context;
026: private AspectSystem aspectSystem;
027:
028: // We use PersonHome in all tests
029: private PersonHome personHome;
030:
031: public EntityBeanTest(String testName) {
032: super (testName);
033: }
034:
035: /**
036: * Sets up mock container, JNDI context and deploy EJBs that we need.
037: */
038: public void setUp() throws Exception {
039:
040: // we use aspects in most of the tests here
041: aspectSystem = AspectSystemFactory.getAspectSystem();
042:
043: // make MockContextFactory the primary JNDI provider
044: MockContextFactory.setAsInitial();
045: context = new InitialContext();
046:
047: // Creating MockContainer deletes all aspects from AspectSystem and clears EntityDatabase
048: mockContainer = new MockContainer(context);
049:
050: // Deploy the person bean
051: EntityBeanDescriptor personDescriptor = new EntityBeanDescriptor(
052: Person.JNDI_NAME, PersonHome.class, Person.class,
053: PersonBean.class);
054:
055: mockContainer.deploy(personDescriptor);
056:
057: /* Create aspect that will handle automatic PK generation for the PersonBean.
058: * PersonBean has auto-sequencing PK that would be handled by the container
059: * (although it is not part of the spec, almost all EJB containers support it).
060: */
061: aspectSystem.add(new PersonCreateAspect());
062:
063: // Deploy the address bean. In this simple example person has many addresses.
064: EntityBeanDescriptor addressDescriptor = new EntityBeanDescriptor(
065: Address.JNDI_NAME, AddressHome.class, Address.class,
066: AddressBean.class);
067:
068: mockContainer.deploy(addressDescriptor);
069:
070: // Obtain personHome for use in the tests
071: personHome = (PersonHome) context.lookup(Person.JNDI_NAME);
072:
073: }
074:
075: public void tearDown() {
076: // cleanup JNDI settings
077: MockContextFactory.revertSetAsInitial();
078: }
079:
080: /**
081: * Demonstrates the create/ejbCreate support
082: */
083: public void testCreate() throws Exception {
084:
085: // Create a Person instance
086: Person person = personHome.create("John", "Doe");
087: // Make sure that CMP fields were set correctly
088: assertEquals("John", person.getFirstName());
089: assertEquals("Doe", person.getLastName());
090:
091: // Check that id (PK) was generated by the PersonCreateAspect
092: assertTrue(person.getId() > 0);
093: // Check that PK was set correctly
094: assertEquals(new Long(person.getId()), person.getPrimaryKey());
095: }
096:
097: /**
098: * Demonstrates the usage of a finder (other than findByPrimaryKey)
099: */
100: public void testFinder() throws Exception {
101:
102: // Create aspect that returns mock data for the finder
103: aspectSystem.add(new PersonFinderAspect());
104: // call the finder
105: Person person = personHome.findByName("John", "Doe");
106: assertNotNull(person);
107: assertEquals("Doe", person.getLastName());
108: }
109:
110: /**
111: * Demonstrates the use of findByprimaryKey
112: */
113: public void testFindByPrimaryKey() throws Exception {
114:
115: /* MockEJB provides a primitive in-memory database implementation (EntityDatabase class)
116: * that automatically handles findByPrimaryKey calls.
117: * It can be populated directly (see "testEasyMockWithFindByPrimaryKey")
118: * or by simply calling "create". If ejbCreate is intercepted and returns
119: * a PK (without interception it would return null because of the spec),
120: * MockEJB automatically adds the newly created bean to the EntityDatabase.
121: * Since our CreateAspect is active, it will make sure that the PK
122: * is returned from the ejbCreate method.
123: */
124: Person createdPerson = personHome.create("John", "Doe");
125:
126: // find person -- EntityDatabase is used
127: Person foundPerson = personHome.findByPrimaryKey(createdPerson
128: .getId());
129: // make sure that we got the same person
130: assertEquals(createdPerson, foundPerson);
131: }
132:
133: /**
134: * Demonstrates how mock entities can be setup using
135: * EasyMock and EntityDatabase.
136: */
137: public void testEasyMockWithFindByPrimaryKey() throws Exception {
138:
139: // Create mock entity. Note that since this object is created outside of
140: // MockEJB, you won't be able to intercept calls to it or to use MockEJB-provided
141: // exception translation and CMT transaction support.
142: // Here we just need test data, we don't care about container's services
143: MockControl personBeanControl = MockControl
144: .createControl(Person.class);
145: Person createdPerson = (Person) personBeanControl.getMock();
146:
147: createdPerson.getId();
148: personBeanControl.setReturnValue(1L);
149: createdPerson.getFirstName();
150: personBeanControl.setReturnValue("John");
151: createdPerson.getLastName();
152: personBeanControl.setReturnValue("Doe");
153:
154: personBeanControl.replay();
155:
156: // Add mock entity to the EntityDatabase so we can find it using findByPrimaryKey
157: mockContainer.getEntityDatabase().add(PersonHome.class,
158: new Long(1), createdPerson);
159:
160: // now we can call findByPrimaryKey
161: Person foundPerson = personHome.findByPrimaryKey(1);
162:
163: assertNotNull(foundPerson);
164: assertEquals(createdPerson, foundPerson);
165:
166: }
167:
168: /**
169: * Demonstrates the use of CMR using standard EJB API.
170: * First, we populate CMR collection using "create" and "set"
171: * methods, after that we can begin using it.
172: * For example, you can setup CMR this way in the "setUp" method of a test class and
173: * then call Session facade that manipulates CMR collections in the test.
174: * Alternatively, you can setup CMR using interceptors ( see "testCMRUsingInterceptor").
175: *
176: */
177: public void testCMRUsingAdd() throws Exception {
178: // Find the person we need
179:
180: // add aspect that handles finders
181: aspectSystem.add(new PersonFinderAspect());
182:
183: Person person = personHome.findByName("John", "Doe");
184: // now we can create Address for the person
185: AddressHome addressHome = (AddressHome) context
186: .lookup(Address.JNDI_NAME);
187:
188: // Get the current list of addresses for this person
189: // Note that MockEJB returns an empty collection (not null) if the
190: // collection is not initialized (as per the spec)
191: // MockEJB also supports java.util.Set.
192: Collection addresses = person.getAddresses();
193: Address address = addressHome.create("1001 Main St",
194: "Washington", "DC", "22222", "USA", person);
195:
196: /* We need to set the ID of the address. Real container would do it for us
197: * if we define "id" field as auto-sequence in the deployment descriptor,
198: * but with MockEJB it is the responsibility of the setup code.
199: * In our case, setId is not even part of the business interface, so we
200: * need to get to the implementation bean object.
201: * In MockEJB any business interface can be cast to EjbBeanAccess which
202: * provides access to the bean and its context.
203: */
204: AddressBean addressBean = (AddressBean) ((EjbBeanAccess) address)
205: .getBean();
206: // now we can set the ID directly
207: addressBean.setId(123);
208: addresses.add(address);
209:
210: /* Since MockEJB does everything in memory, you don't need to call setter.
211: * But it won't hurt either.
212: */
213: person.setAddresses(addresses);
214:
215: // CMR is now setup, we can begin using it. In the real life,
216: // the code above will probably be in the setUp method (unless you're
217: // testing the CMR creation functionality)
218:
219: // make sure that the address was added
220: assertEquals(1, person.getAddresses().size());
221: // make sure that M:1 is set
222: address = (Address) person.getAddresses().iterator().next();
223: assertNotNull(address.getPerson());
224: // check the PK of the address
225: assertEquals(123, address.getId());
226:
227: }
228:
229: /**
230: * Demonstrates how to set up CMR using interceptors/aspects.
231: */
232: public void testCMRUsingInterceptor() throws Exception {
233:
234: // Add aspect that handles CMR methods on the Person bean
235: aspectSystem.add(new PersonCMRAspect());
236:
237: // Aspect will do all the work for us, the setup code is now in the interceptor
238:
239: // add aspect that handles finders so we can find the person
240: aspectSystem.add(new PersonFinderAspect());
241:
242: Person person = personHome.findByName("John", "Doe");
243:
244: // make sure that the address exists
245: assertEquals(1, person.getAddresses().size());
246: // make sure that M:1 is set
247: Address address = (Address) person.getAddresses().iterator()
248: .next();
249: assertNotNull(address.getPerson());
250: // check the PK of the address
251: assertEquals(123, address.getId());
252:
253: }
254:
255: /**
256: * Demonstrates the support of ejbHome and ejbSelect methods
257: */
258: public void testEjbHomeEjbSelect() throws Exception {
259:
260: // Create interceptor for the ejbSelectAll method of PersonBean
261: SelectAllAspect selectAllAspect = new SelectAllAspect();
262: // Intercept calls to the "ejbSelectAll" method of the personBean
263: aspectSystem.add(selectAllAspect);
264:
265: // Call the home method which in turn calls ejbSelectAll method
266: personHome.updateNames();
267: // make sure that the data has been updated
268: List people = selectAllAspect.getPeople();
269: assertEquals("Smith", ((Person) people.get(0)).getLastName());
270:
271: }
272:
273: // *** Interceptors used by this test case
274:
275: /**
276: * Supports auto-sequenced PK for the Person bean
277: */
278: class PersonCreateAspect implements Aspect {
279:
280: private long pkSequence = 0;
281:
282: /**
283: * Intercept all calls to the ejbCreate method of the PersonBean.
284: * Notice how the pointcut can match interface methods as well as the methods
285: * of the implementation class.
286: */
287: public Pointcut getPointcut() {
288:
289: /* Note that we have to use ".*" in the regexp since
290: * the actual concrete class (subclass) is provided by CGLIB, so its name is different from
291: * the is the PersonBean (the actual name is PersonBean$Enhanced...)
292: */
293: return new MethodPatternPointcut("PersonBean.*ejbCreate");
294: }
295:
296: public void intercept(InvocationContext invocationContext)
297: throws Exception {
298: // Proceed to call ejbCreate method of the PersonBean
299: invocationContext.proceed();
300: // generate the id for this bean. The real container can do it using Oracle sequence, for instance.
301: long id = ++pkSequence;
302: PersonBean personBean = (PersonBean) invocationContext
303: .getTargetObject();
304: // Set the id
305: personBean.setId(id);
306:
307: // return the PK value. Without this interceptor, this method would return null.
308: invocationContext.setReturnObject(new Long(id));
309: }
310: }
311:
312: /**
313: * Handles findByName calls
314: */
315: class PersonFinderAspect implements Aspect {
316:
317: /**
318: * Intercept findByName method
319: */
320: public Pointcut getPointcut() {
321: return new MethodPatternPointcut("PersonHome\\.findByName");
322: }
323:
324: public void intercept(InvocationContext invocationContext)
325: throws Exception {
326:
327: Object[] paramVals = invocationContext.getParamVals();
328: invocationContext.setReturnObject(create(
329: (String) paramVals[0], (String) paramVals[1]));
330: // We don't need to proceed to the next interceptor since we're done with the finder
331: }
332:
333: /**
334: * Creates Person entity using its "create" method
335: */
336: private Person create(String firstName, String lastName)
337: throws Exception {
338: Context context = new InitialContext();
339:
340: PersonHome personHome = (PersonHome) context
341: .lookup(Person.JNDI_NAME);
342:
343: Person person = personHome.create(firstName, lastName);
344:
345: return person;
346: }
347: }
348:
349: /**
350: * Intercepts calls to the CMR methods of the Person bean (getAddresses),
351: * and populates and returns the collection of address objects.
352: */
353: class PersonCMRAspect implements Aspect {
354:
355: public Pointcut getPointcut() {
356: return new MethodPatternPointcut("Person\\.getAddresses()");
357: }
358:
359: public void intercept(InvocationContext invocationContext)
360: throws Exception {
361:
362: // note that since this aspect only handles one method, we don't need to check
363: // the method name that we intercepted
364:
365: // Create the addresses we want using AddressHome
366: AddressHome addressHome = (AddressHome) context
367: .lookup(Address.JNDI_NAME);
368: // Create empty collection
369: Collection addresses = new ArrayList();
370: /*
371: * Create the address and add it to the collection.
372: * We need to pass the parent object (person), this is the
373: * Person interface that we intercepted.
374: */
375: Address address = addressHome.create("1001 Main St",
376: "Washington", "DC", "22222", "USA",
377: (Person) invocationContext.getProxyObject());
378: // Set the PK of the address by getting the address bean and calling its setter
379: AddressBean addressBean = (AddressBean) ((EjbBeanAccess) address)
380: .getBean();
381: addressBean.setId(123);
382: addresses.add(address);
383:
384: invocationContext.setReturnObject(addresses);
385: }
386: }
387:
388: /**
389: * Intercepts ejbSelectAll method and populates and returns
390: * the data for this method.
391: */
392: class SelectAllAspect implements Aspect {
393:
394: private List people;
395:
396: /**
397: * Intercepts ejbSelectAll of the PersonBean
398: * @see org.mockejb.interceptor.Aspect#getPointcut()
399: */
400: public Pointcut getPointcut() {
401: // Note that the pointcut here is applied to the bean, not to the interface, since ejbSelect is only defined in the bean
402: return new MethodPatternPointcut(
403: "PersonBean\\.ejbSelectAll()");
404: }
405:
406: public void intercept(InvocationContext invocationContext)
407: throws Exception {
408: // we don't need to call "proceed" since the PersonBean does not know
409: // how to handle it anyway.
410:
411: PersonHome personHome = (PersonHome) context
412: .lookup(Person.JNDI_NAME);
413: people = new ArrayList();
414: people.add(personHome.create("John", "Smit"));
415: // Return the created collection
416: invocationContext.setReturnObject(people);
417: }
418:
419: /**
420: * Returns the collection of people that was returned to the client
421: * so we can check against it in the test method.
422: * @return collection of people
423: */
424: List getPeople() {
425: return people;
426: }
427: }
428: }
|