This application note pertains to Mr Architecture 3.0. This note was last updated on 8th April 2005 to add this line and to recapitalize the headings. Good Practice for Mr Bean Event Handlers Due to the complexities of the Mr Architecture bean lifecycle, the following general guidelines will help prevent developers from being caught out from idiosyncrasies of which they may not be aware: 1. A lifecycle method should not rely on being called in sequence. That is, a bean should permit, for example, an ejbPassivate() call on a bean which has received neither an ejbActivate() call nor an ejbPostCreate() call should not fail. 2. A lifecycle method should allow itself to be called more than once in succession with no ill- effects or, ideally, any performance drain. There are exceptional circumstances where these situations may arise, and it is good practice for a bean to be able cope with these as or if they present themselves. However, there is no need for a bean to allow calls to its business methods before activation. If it is possible for such calls to occur, all well and good, but it is not mandated by good practice. Security Considerations for Mr Bean Event Handlers The methods setEntityContext(), ejbActivate(), ejbPassivate(), ejbStore() and ejbRemove() must not alter the database or throw an exception which may contain sensitive information. They are called by the ServletSecurity component before security checks are carried out. It is best to make these methods do nothing when activated on a "Thin" entity bean for this reason. The recommended code sequence to be used inside all event handlers which do operations which are potentially hazardous is: if (!this.getClass().getName().startsWith("Thin")) { // Bean is not Mr Architecture "Thin" entity bean ... security-sensitive code ... } This code sequence has the advantage of working on all possible future EJB containers, as it does not depend on the "Thin" classes existing. "Thin" entity beans are only called through their abstract business methods, EJB lifecycle methods and Mr Architecture specific lifecycle methods (such as containerChanged(),) so typically any non- sensitive initialization they might do is redundant anyway. Event Timing for Mr Beans Mr Architecture follows the EJB specification as much as is possible given its divergent bean life cycle. That is, the order of events on a bean is the same, and the meaning of events is the same, however because Mr Beans exist simultaneously at many locations, arrive there by the unique Mr Architecture transport, and exist in two forms at each location, the events must be delivered multiple times to ensure that all levels and locations are aware of the lifecycle. The following sections describe the timings of each possible event in detail, and although the exact order of events should not be relied on, it is important that beans be able to cope with the order of events as currently described. Create Event This event occurs before a bean has been assigned identity. Before this event, the bean does not exist in persistent or non-persistent form (in the local machine,) and does not have a primary key (i.e. is pooled.) This is complicated from the simple EJB case, because EntityBeans exist only in one place, and there is only one bean for each identity. MrEntityBeans can be "Thin" or bean object implementations, and can exist on either client or server. Therefore there are three create events before a client-side Mr Architecture application sees a bean. The create event first occurs on the server "Thin" bean, then on the client "Thin" bean (after post-create and passivate events on the server bean), and finally on the client bean object (after the post-create event on the client "Thin" bean.) Similarly there are two create events before a bean created through the action of an InvokeCommand, ServerDaemon or server-side Mr Architecture application is exposed. The create event first occurs on the server "Thin" bean, and then on the server bean object wrapper (after the post-create event on the server "Thin" bean.) There may be a passivate event on the server "Thin" bean before further events on the client in the case of an InvokeCommand. In this case the client "Thin" bean gets this event instead of an activate event, and then the same event occurs on the bean object (after a post-create event on the client "Thin" bean.) All of this is particularly important to note for the implementor of a ejbCreate() method. Mr Architecture, in keeping with the spirit if not the letter of the EJB specification, allows an object to veto its primary key at create time by providing an alternative key in its result, or by throwing (preferably) a CreateException. (An implementor may also throw an EJBException or other RuntimeException or Error, but must do so only when there is a systemic failure, not to veto a key.) But only the server "Thin" bean can be effective in vetoing the key in the former case (by returning an alternative key as a result), because in the bean's other lives, the key is already firmly set. Because of this, Mr Architecture requires that the ejbCreate() method be consistent in behaviour in the following respects: for a given key value, it must consistently return the same result or throw exactly the same CreateException (as determined by equals(), or the == operator in case of a primitive type;) and for a result value supplied as the key value, it must not object to the key in any way and return it unchanged. An ejbCreate() method is permitted to throw a RuntimeException or Error at any time to indicate a systemic failure, but not to veto a key. After this event, the object will be assigned identity (unless ejbCreate() throws a CreateException or EJBException.) This will in turn result in a post-create event. 1. Thin bean in ServerContainer receives create event first The ejbCreate() method of a "Thin" bean on the server is called at create time- that is, when the CreateCommand is executed by the server. The ejbPostCreate() method is called immediately on successful return of ejbCreate(). The method ejbPassivate() is called later if the object is to be transported over the network to the client. This is the only time the key can be rejected or changed. 2. Bean object in ServerContainer receives create event second This will occur after the post-create event for the wrapped "Thin" entity bean, at the time the bean is wrapped. Note that this event will only occur if the bean is to be manipulated by the server before being returned to the client (e.g. as the result of an InvokeCommand,) if it is being used during the execution of a ServerDaemon, or if the application is deployed on the server only (in which case, this is the last create event.) It is too late to change or reject the key at this point. 3. Thin bean in ClientContainer receives event next When the bean is reconstituted at the client end, the resulting "Thin" entity bean's ejbCreate() method is called. This is called instead of ejbActivate() for beans which are newly created. It is too late to change or reject the key at this point. Beans which are not MrEntityBeans will not receive an ejbCreate()/ejbPostCreate() event pair, and will instead simply receive ejbActivate(). 4. Bean object in ClientContainer receives event last This will occur after the post-create event for the wrapped "Thin" entity bean, at the time the bean is wrapped. This is called instead of ejbActivate() for beans which are newly created. It is too late to change or reject the key at this point. Post-create Event This event is called at once a bean has been assigned identity. The bean should be ready for use by clients subsequent to this method being called. There are three post-create events before a client-side Mr Architecture application sees a bean. The post-create event occurs on the server "Thin" bean, then on the client "Thin" bean (after a passivate event on the server bean), and finally on the client bean object. Similarly there are two create events before a bean created through the action of an InvokeCommand, ServerDaemon or server-side Mr Architecture application is exposed. The create event first occurs on the server "Thin" bean, and then on the server bean object wrapper (after the post-create event on the server "Thin" bean.) There may be a passivate event on the server "Thin" bean before further events on the client in the case of an InvokeCommand. In this case the client "Thin" bean gets this event instead of an activate event, and then the same event occurs on the bean object (after a post-create event on the client "Thin" bean.) 1. Thin bean in ServerContainer receives post-create event first The ejbPostCreate() method of a "Thin" bean on the server is called immediately after a successful call to ejbCreate(). The server object must be ready to receive other calls through its business methods after this method is invoked. It can neglect its obligations in this respect once ejbPassivate() has been called. 2. Bean object in ServerContainer receives post-create event second This will occur after the post-create event for the wrapped "Thin" bean, and the create event for this bean object. Note that this event will only occur if the bean is to be manipulated by the server before being returned to the client (e.g. as the result of an InvokeCommand,) if it is being used during the execution of a ServerDaemon, or if the application is deployed on the server only (in which case, this is the last create event.) 3. Thin bean in ClientContainer receives event next When the bean is reconstituted at the client end, and the resulting "Thin" entity bean's ejbCreate() method is called instead of ejbActivate(), then at that point ejbPostCreate() is called. The client object must be ready to receive other calls through its business methods after this method is invoked. It can neglect its obligations in this respect upon ejbPassivate() being called- this signals the beginning of a server round-trip. Beans which are not MrEntityBeans will not receive an ejbCreate()/ejbPostCreate() event pair, and will instead simply receive ejbActivate(). 4. Bean object in ClientContainer receives event last This will occur after the post-create event for the wrapped "Thin" bean, and the create event for this bean object. Activate Event This is an alternative beginning of life-cycle for an entity bean which previously existed in persistent storage, and is used to revive it. Before the event, the bean is a pooled bean which has no identity. After this event, the bean must be prepared to be called though any of its business methods. The activate event is not used when a freshly created bean is transported, only when a persistent form exists. A freshly created bean is revived after transport by using a create and post-create event pair. Like the foregoing (create and post-create) events, a single activation results in more than one (in this case two) ejbActivate() calls- first, one to the "Thin" bean as it is reconstituted, and then another to the bean object wrapper which wraps it. These calls may be separated in time, because a bean is only wrapped when it is specifically requested by the client application, but the original activation of a "Thin" bean after it arrives on a transport occurs immediately upon reception. The action of the ejbActivate() method should be to set up any internal data structures or resources required by the bean's business methods, bearing in mind that it will be the bean object wrapper that will be used to service external calls. To cope with this it is recommended that a bean ask itself whether it is a WrappedBean before making excessive resource demands. Also, all initialized fields should be transient to prevent bloating the transport stream with these resources (which may not even be transportable.) A "Thin" server bean should also recognise that it is highly unlikely that it will be called through its business methods in the server context, and therefore only lazily instantiate resources when specifically asked. Passivate Event This indicates the end of life-cycle for an entity bean, and is used to return it to the pooled state. Before the event, the bean is a entity bean which has identity. After this event, the bean will have no identity, and therefore will not be called though any of its business methods (excluding ejbHome() methods.) It should also be prepared to be garbage collected. A single passivation often results in more than one (and at most two) ejbPassivate() calls- first, one to the bean object wrapper, and then another to the "Thin" bean as it is either serialized ready for a round trip, or otherwise discarded. These calls typically occur in quick succession. The action of the ejbPassivate() method should be to set to null any internal data structures or release any resources that were required by the bean's business methods. If you use the transient keyword on your initialized fields, an empty implementation of ejbPassivate() will usually suffice, although you should release large data structures and in-demand resources at the earliest possibility and not necessarily wait for the garbage collector or the next transition to activated state. Load Event This event occurs once a bean is recovered from persistent storage. It allows it to post-process the data as recovered from that storage and use it to initialize internal constructs which must be maintained in synchrony with that data, or to somehow transform the data into a form which the bean needs. For example, if a developer defined business method returns the total of two columns, it may be neccessary to recalculate any stored value for this total upon receiving notification of a data change in this way. A single load() operation often results in one or more (and at most four) ejbLoad() calls: 1. The "Thin" bean in the server receives an ejbLoad() message. This occurs immediately upon the completion of the load() operation. 2. If the bean is used on the server, the server bean object receives an ejbLoad() message. 3. If the bean is transmitted to the client, the "Thin" bean on the client is next to receive an ejbLoad() message. It receives this ejbLoad() message immediately after activation. 4. Finally, where message 3 has been sent, the client bean object receives an ejbLoad() message. Store Event This event occurs before a bean is saved in persistent storage. It allows it to pre-process the data to be placed in that storage, perhaps by somehow transforming the data into a form which the persistent storage needs. For example, if a developer has an internal data column representing a calculated value based on other data values in the bean, this event could be used as a way to recognise that calculation should be done. A single store() operation often results in one or more (and at most three) ejbStore() calls: 1. The bean object in the local machine receives an ejbStore() message. This occurs immediately, outside the transaction, and does not therefore mean the transfer is guaranteed to proceed. If the bean is included multiple times in the transaction, the bean object may see more than one of these messages. 2. The "Thin" bean in the local machine receives an ejbStore() message. This occurs immediately, outside the transaction, and does not therefore mean the transfer is guaranteed to proceed. If the bean is included multiple times in the transaction, the "Thin" bean may see more than one of these messages. 3. If the store() occurred on a client machine, the "Thin" must go on a round trip to the server. When the bean reaches the server, it receives an ejbStore() message immediately after activation. Once again, this does not guarantee that a store will actually proceed. If the bean is included multiple times in the transaction, the "Thin" bean may see more than one of these messages. Remove Event This event occurs before a bean is deleted from persistent storage. It allows it to do some work before the remove occurs. After receiving this event, the bean is in pooled state, and it should not expect to be called through its business methods again (excepting ejbHome() methods.) Any tidy up carried out by ejbPassivate() should also be carried out here, therefore. (Mr Arcitecture does not require this for a client-side bean, because it will be sent on a round-trip to the server which will neccessarily passivate it, but EJB does not guarantee this. Of ocurse, Mr Architecture is divergent from EJB when it calls ejbPassivate() after ejbRemove().) A single remove() operation often results in one or more (and at most three) ejbRemove() calls: 1. The bean object in the local machine receives an ejbRemove() message. This occurs immediately, outside the transaction, and does not therefore mean the removal is guaranteed to proceed. If the bean is included multiple times in the transaction, the bean object may see more than one of these messages. 2. The "Thin" bean in the local machine receives an ejbRemove() message. This occurs immediately, outside the transaction, and does not therefore mean the removal is guaranteed to proceed. If the bean is included multiple times in the transaction, the "Thin" bean may see more than one of these messages. 3. If the remove() occurred on a client machine, the "Thin" must go on a round trip to the server. When the bean reaches the server, it receives an ejbRemove() message immediately after activation. Once again, this does not guarantee that a remove will actually proceed. If the bean is included multiple times in the transaction, the "Thin" bean may see more than one of these messages.