Observer Pattern - An Enterprise JavaBean Implementation

原创 2001年09月19日 12:33:00

Observer Pattern - An Enterprise JavaBean Implementation

Greg Comeau

Download: EJBObserverPattern.zip

Motivation

I recently needed an infrastructure that would allow an arbitrary number of Enterprise JavaBeans to observe changes to a collection of central business entities. The application environment in which I am developing consists of a number of EJB applications running on more than one host. All of the applications are designed to work together as a single, integrated suite. The database environment is distributed. A central store of data is shared by the entire suite while each application maintains a separate store of data that is specific to that application.

One part of the suite is responsible for accepting transactions from the outside world. Each transaction arrives in the form of an XML document. A typical transaction might require changes to the central data store as well as one or more application data stores. All changes must be done within the scope of a single transaction, i.e. if one application aborts the transaction then all participants must abort the transaction. 

Also, there are some data constraints that cross application boundaries. It is possible for one application to abort a transaction because a change made to the central repository is unacceptable within the context of some application specific data. e.g. Application X may require that a customer has a fax number. If a transaction attempts to grant Customer A permission to use Application X and Customer A does not have a fax number then Application X must abort the transaction.

It was the enforcement of these distributed data constraints that motivated my use of the observer pattern. Each application needs to observe changes in the shared repository as they occur -- and within the transaction in which they occur. If a proposed change to the shared data is unacceptable to any given application then the transaction must be aborted before the change is made permanent.

Implementation

What follows is essentially the process that I went through to implement this pattern. I have omitted some of the more glaring mistakes to protect the stupid guilty.

Note: All of the source files are available for download. The java classes are contained within a package called EJBObserverPattern. 

The java.util.Observable class and the java.util.Observer interface work great within the scope of a single Java VM. But I discovered that they aren't much use in implementing the observer pattern across VMs with EJBs as implementations of Observer. The first step in creating an EJB observer might be to extend java.util.Observer to create a remote interface:

If you did this you'd quickly find out that java.util.Observer.update(...) does not declare java.rmi.RemoteException, something required of all methods of a remote interface. At this point I found it necessary to create a new observer interface and observable class that would work in the EJB universe. I created my own observer and observable classes which parallel the respective classes in java.util. 

The EJBObserver Interface

A new interface is required that I called EJBObserver:

Note: A common pattern in EJB implementations is to define a superinterface to be extended by both the remote interface and the bean itself. This is commonly called a business interface. EJBObserver is a business interface.

Yet Another Note: My development environment adheres to the EJB 1.0 specification. Thus, I have to explicitly declare javax.ejb.EJBException. EJB 1.1 and later redefines EJBException as a subclass of RuntimeException making it unnecessary to explicitly declare it in a method signature.

The EJBObservable Class

My new class EJBObservable defines all the same methods as java.util.Observable (why change a paradigm that works.) Thus, my EJBObserver update method takes an EJBObservable object; this mirrors java.util.Observer, which takes a java.util.Observable object. I marked EJBObservable as serializable by implementing java.io.Serializable. This is necessary since instances of this class will be sent to remote EJB observers:

Foobar -- A Subclass of EJBObservable

For testing purposes I created a subclass of EJBObservable called Foobar. This is the thing that will be observed. Foobar has a single member variable with a corresponding setter method. Setting the member variable also sets the changed attribute inherited from the superclass EJBObservable. The semantics of the changed attribute are identical to the respective attribute of java.util.Observable:

The Test Harness

Again for the purposes of testing, I created a test harness called Tester. This class simply creates an instance of Foobar, adds an observer, invokes the setter method of Foobar and calls notifyObservers:

Note: Tester will go through several revisions later in the document. My intent is to show you the abstraction process as well as the final result.

As you can see I've put the cart before the horse after the barn door closed. I do not yet have an implementation of EJBObserver. This turned out to be the interesting part.

Note that updating a remote observer via EJBObserver.update(...) will involve a remote method invocation. The observer is located at some arbitrary location in the EJB universe. The local manifestation of a remote observer will be an instance of a remote interface obtained by the usual means -- by invoking the create method of a home interface obtained via JNDI.

So now I needed to implement the usual components of an EJB, the home and remote interfaces and the bean itself. I used a stateless session bean. Note that there can be an arbitrary number of EJBObserver implementations in use at one time but only one is required to demonstrate the pattern.

Remote Interface of EJBObserver Implementation

First I created the remote interface, which I subclassed from the business interface EJBObserver:

Home Interface of EJBObserver Implementation

Next I created the home interface, which is almost as trivial as the remote interface. There's nothing special about it:

The astute reader may notice that I'm going to have a problem with this home interface. Rest assured that I was not astute. (What is a stute?) I didn't recognize the problem until I ran into it. I'll describe the exact problem and my solution later.

A stute is the adult form of a ware.

RemoteObserverBean

Now for the bean itself. Again, I implemented the superinterface EJBObserver. The implementation of the update method simply writes to the system output stream so I know that it worked:

There is nothing special about the deployment descriptor for this bean. I've included it in the download file. The only detail worth noting is that the EJB must be a well-behaved transactional component, preferably using Component Managed Transactions. Otherwise any persistence operations performed by an instance of EJBObserver may not be rolled back if some other EJBObserver aborts the transaction.

I used Weblogic to build and deploy it. It all worked perfectly the first time. Really. ... Why do you look skeptical?

Instantiate the Remote EJBObserver

At this point I cannibalized the appropriate code to get the new home and remote interfaces. Plugging this code into Tester I ended up with the following:

When I ran the above it worked as expected. The update method of the remote observer was called and I got the expected output on the console. But this isn't exactly what I wanted. Notice that I coupled myself directly to a specific implementation of EJBObserver, namely RemoteObserver. My intention from the beginning was to query some semi-static registry to obtain the JNDI parameters that identify one or more observers. The parameters stored in each entry of the registry would be the initial context factory, the provider url, and the JNDI name of the home interface. 

In other words, I wanted to invoke an arbitrary EJBObserver implementation via remote polymorphism.

Decoupling from RemoteObserver was easy enough. I simply changed every reference to EJBObserver -- which I should have done in the first place. Then I ran into a problem with the home interface. 

The aforementioned astute reader has by now realized that my home interface RemoteObserverHome does not have some convenient superinterface to use. Any such interface would have to define the create method in order for polymorphism to work. EJBHome doesn't define the create method. Casting the home interface to EJBHome and invoking the create method will generate a compile error.

Reflection of the Home Interface

When you know a class implements a method with a given signature but you can't use polymorphism, reflection works great. Here's how Tester looked after I used reflection to find and invoke the create method of an arbitrary home interface:

The initial context factory, provider url, and EJB home name can easily be retrieved from some external repository via JDBC. I won't show that implementation. One could also imagine an additional mechanism by which a remote application could register itself in the above repository. I won't show that either. Such details aren't really part of this pattern.

Why Not Subclass EJBHome?

You might be asking yourself, "Self, why resort to reflection when I can subclass EJBHome to define the create method that I need to make polymorphism work?"

That's a reasonable question, Self. Let's try it. I did.

First let's create a new interface EJBObserverHome that extends EJBHome. This will be the superinterface to be extended by any home interface of a remote EJBObserver:

Now let's redefine RemoteObserverHome to extend this interface:

Looking good. Now let's rebuild RemoteObserverBean.

(build, build, build ... oops.)

(Sound of hand slapping forehead.) Of course, a create method of a home interface can't return some abstract class; it must return the exact remote interface type of the EJB that lives there. And if you try to override the create method in RemoteObserverHome to return the right interface type then the compiler will remind you that you can't change the return type of an overridden method. Catch-22. And you can't reuse the same home interface for every remote EJBObserver because there must be a one-to-one correspondence between a home interface and a specific EJB implementation. Otherwise how would the home implementation know which bean to create? Catch-23. (That last bit may have seemed obvious to you. I actually tried it.)

If anybody has another solution to this quandry -- besides reflection -- please enlighten me.

One More Abstraction

There was one detail of this that bothered me. Looking up the home interface and creating a remote interface is a lot of work to go through every time I want to add an observer. What if the observable instance never changes? That's a lot of work for nothing. When I add an observer I simply want to save the minimum amount of information that will be necessary to instantiate and invoke the remote observer when and if the time comes to do so. There's one more abstraction to make.

EJBObserverProxy

Patterns can be addictive. You can't eat just one. A proxy pattern was a perfect fit. I created a new class called EJBObserverProxy that implemented EJBObserver. EJBObserverProxy was designed to save all the information needed to instantiate and invoke a remote observer. It also encapsulated the reflective mechanism by which this was accomplished:

There are a number of things to note about EJBObserverProxy:

  • It encapsulates member variables that store the settings required to lookup a home interface.
  • It defines hashCode and equals methods so that EJBObserverProxy will be a well-behaved member of a Set. Recall that instances of EJBObserver are stored in a HashSet within EJBObservable. The semantics defined by Observable dictate that any given Observer must be unique within an instance of Observable. A Set implementation uses the hashCode and equals method to enforce uniqueness.
  • It implements java.io.Serializable since instances of this class will be stored inside EJBObservable, which will be serialized as an argument of a remote method call.
  • The update method encapsulates all of the nasty JNDI and reflection that we need to remotely invoke the associated EJBObserver instance.

A reader suggested the use of javax.ejb.Handle to maintain a persistent relationship to a remote observer. You can get a Handle to a remote object and serialize it to persistent storage. The Handle can be materialized from storage at any time to recreate the remote object. I considered encapsulating a Handle object inside EJBObserverProxy but I couldn't figure out how to implement hashCode such that two Handle objects that pointed to the same remote interface would evaluate to the same hash code. Remember that duplicate observers cannot be added to a single observable. The HashSet implementation first compares hash codes to determine if two objects might be equal. If the hash codes are different then it doesn't bother calling the equals method. So there was no way (that I found) to eliminate duplicate observers using Handle objects.

Of course, the act of writing the above paragraph guarantees that it can be done. Such is the nature of the universe.

New Tester using EJBObserverProxy

Now I could use EJBObserverProxy to greatly simplify Tester, thus simplifying the eventual client code that will use this pattern:

The term client in this context means the party that instantiates the EJBObservable object. The client could easily be another EJB.

Motivation Redux

Recall that my motivation was to allow an arbitrary collection of EJBs to observe changes in a central data repository. Any such EJB can now simply extend EJBObserver to define its remote interface and register its JNDI information with the manager of the central repository via some simple database table. If a business entity within the central repository is changed then the central repository manager, after making the change, instantiates a subclass of EJBObservable which encapsulates the updated business entity. The EJBObserver registry is then used to add the appropriate observers and notifyObservers is called. 

If you look again real close you might recognize a mediator pattern in the above paragraph.

Any remote EJBObserver may abort the transaction by simply throwing a system exception or calling setRollbackOnly on its EJBContext object. Recall that a system exception is a java.lang.RuntimeException or java.rmi.RemoteException, or any subclass directly or indirectly thereof. Note that Tester doesn't start a new transaction before it calls notifyObservers. This is an academic example. If I really wanted a single transaction context I'd have to start a new client demarcated transaction. In reality, the role of Tester will usually be filled by an EJB and container managed transactions would be used.

This implementation can be used in any distributed application where an Observer pattern makes sense -- not just in the distributed transaction scenario that I've outlined.

 

About the Author

Greg Comeau is a Senior Software Engineer at Webb Interactive Services, Inc. with offices in Denver and Boulder, Colorado. The author prefers the latter but harbors no ill will against the former.

Comments on this article are welcome. Flames will go unanswered. I'd rather be skiing. Have a nice day.

设计模式 - 观察者模式(Observer Pattern) 详解

观察者模式(Observer Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权...
  • u012515223
  • u012515223
  • 2014年05月22日 14:37
  • 2977

理解观察者模式(Observer Pattern)必看的文章

1.       说明:观察者模式是软件设计的一种模式,也叫做发布/订阅模式。日常生活中基本上都有用到它。比如使用MSN当你的一个好友上线时,你就会收到上线通知;开心网当某个人转帖后,他的好友就都可以...
  • dujingjing1230
  • dujingjing1230
  • 2009年08月10日 14:56
  • 3495

设计模式总结之Observer Pattern(观察者模式)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。...
  • cooldragon
  • cooldragon
  • 2016年08月11日 00:53
  • 955

Java设计模式之观察者模式(Observer Pattern)

Observer Pattern 是一种常用的设计模式,它是一种事件监听模型,
  • tracker_w
  • tracker_w
  • 2014年06月18日 00:53
  • 2495

观察者模式 Observer Pattern

之前总是想写一个程序,大体上是这样的:单服务器,多客户端。服务器上比如说是个房屋价格信息,客户端则是租房子,显示价格。如果现在有特价,怎么客户端怎么实时获取?而且作为客户端,用户定是不止一个,如何通知...
  • haimian520
  • haimian520
  • 2016年04月13日 15:10
  • 3453

设计模式笔记——(三:观察者模式 Observer Pattern)

观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/...
  • xiaoyu_93
  • xiaoyu_93
  • 2016年11月18日 15:38
  • 839

Observer pattern in Enterprise J2EE Architecture

Observer pattern is one of the most popular patterns used in J2EE software architecture, from its de...
  • GaaraZhu
  • GaaraZhu
  • 2013年07月25日 00:25
  • 908

Observer Design Pattern

意图:定义对象间1对多的依赖关系,当一个对象状态发生变化时,所有依赖它的对象能自动得到通知;别名:依赖,发布/订阅;适用性:1,当一个模型有两方面,其中一方依赖另外一方;2;当对一个对象状态改变时,需...
  • kissjob
  • kissjob
  • 2011年07月11日 15:32
  • 260

观察者模式【Observer Pattern】

《孙子兵法》有云:“知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好的一个办法,我们...
  • u011694328
  • u011694328
  • 2016年10月12日 15:52
  • 286

WebLogic Enterprise JavaBean 编程

了解 Enterprise JavaBean 以下部分简要回顾了不同的 Enterprise JavaBean (EJB) 类型以及它们在应用程序中可提供的功能,并描述了它们如何使用其他应用程序...
  • kdsde
  • kdsde
  • 2014年08月28日 14:26
  • 687
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Observer Pattern - An Enterprise JavaBean Implementation
举报原因:
原因补充:

(最多只允许输入30个字)