HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Despite extensive development over many years and significant demonstrated benefits, the object-oriented paradigm remains poorly formalized. Several concurrent object-oriented languages have been designed and implemented based on the concurrent object model. However, upon attempting to apply formal techniques to a significant application, several well known shortcomings actually impeded progress dramatically right at the outset.

In the second part of our series (JDJ, Vol. 2 Iss. 6), we defined the meaning of the inheritance anomaly to describe the conflict between inheritance and concurrency in object-oriented languages. It has been proven that Java at the semantic level is powerful enough to provide a mechanism for solving the inheritance anomaly. However, semantic conflicts often occur using even a simple and elegant language like Java. The concept of Meta-Object will be introduced as a methodology to obtain a separation of the protocol from functionality in class definition and an evaluation framework for a server component model will be defined. This part deals mostly with design topics and leaves implementation details and performance issues for future articles.

Reasons for a Meta-Level Approach
Despite their popularity, the basic principles of object-oriented programming - encapsulation, inheritance, polymorphism - are still not used correctly. How do we implement encapsulation today? The object fields are private/protected but the object has better methods like pleaseSetMyPrivateData(). I once saw the following code:

public class SplendidEncapsulation {
private String s = "SENSITIVE DATA";
public void setS
(SplendidEncapsulation anotherObject) {
anotherObject.s = "MODIFY SENSITIVE DATA";
}
}

Imagine x, y object of type SplendidEncapsulation and an invocation of type y.setS(x). Would you consider using the x object after y.setS(x) is executed? Personally, I wouldn't recommend it. Although the intention was clearly to set the data of object y, questionable in itself, the sample shows how today these hurdles arise from the current practical software development. There is clearly a problem here, as nothing prevents us from constructing language in syntactically correct forms [3], that could lead to the miserable failure of a software product. It is obviously a semantic interoperability problem between object requester and object provider. Indeed, we might now wonder how a viable concept like encapsulation could be enforced.

I believe that making implicit semantics explicit and accessible at the meta level would allow semantic forms to be converted into syntactic forms and thus make them amenable to automated detection and possible resolution. Information hiding must be maintained and the ability to modify the object state at varying levels of abstraction must be provided. Various authors have proposed different solutions, most of them converging to a meta-level model that will be explained shortly. A comprehensive approach relies on design patterns methodology [1]. An object has a state, behavior and identity. The object behavior needs to change as its state is modified. The State pattern provides a good conceptual model and it is a valuable pattern to master because it is a practical way of avoiding mistakes such as those described in the SplendidEncapsulation class. However a StateType object can encapsulate the state-transition behavior and may be used to predict the result of an inconsistent state of the object. Correcting the error using the State pattern is, however, an important prelude to a formal and comprehensive object manipulation model. It seems likely that the use of formal models will become standard practice in software engineering. It is generally recognized that no viable model, describing a self controlled object, can be designed. Objects are becoming increasingly concurrent and distributed and also, in many cases, they are also mobile. Therefore, the semantic models, assuming a sequential execution context or those not addressing concurrency and distribution, will be of limited value.

As you can see, there are many reasons to consider a fundamental and unified approach for a new semantic foundation. To accommodate objects in a newly created computing environment we need a way to encapsulate and control not only the object state (data) but also the object behavior (methods). Hold on; you may say using Java and encapsulating object behavior means having only private/protected methods. How could I further use my object? In fact, your object will not be used, at least not directly. A client object will see only an object wrapper, a controlling object called Meta-Object.

Meta-Object
So what is a meta-object? A meta-object of a base-level object is an object that defines the semantic of the base-level object behavior. The client object will never interact directly with a base-level object. Instead, the client interacts with an external object representation, the meta-object. The main goal of the new concept of a meta-object is to separate what an object does - the base-level implementation - from how it does it - the object meta-level implementation.

A clear separation of concerns is defined in the meta-level programming model. Thus, the main concern at the base-object level is to solve the application domain problem. The meta-object can alter the computational characteristics of an object regardless of its base-level semantics. A passive object, for example a sequential base-level object, could exhibit a different behavior into a concurrent environment without modifying object code. Using the meta-objects for concurrency control is the ideal control form since it provides the most flexible control mechanism [2]. Meta-objects are responsible for delivering messages between base-level objects so that the meta-objects' implementation can provide optimal communication protocols and scheduling policies. The data of a meta-object is called metadata and it is the base-level object data representation at its metalevel. Even at this earlier stage of the meta level object model definition, several mechanisms might be beneficial to us. A meta-object may inspect the state of a base-level object within its execution environment and free us from writing object code which depends on one particular computational system. Eventually you ought to deal with concurrency, security, distribution and other services, but only once at the meta-object level.

How could a base-level object maintain its state in different contexts of execution? At this point in our model design, I believe it is an easy question to answer: the persistence will be implemented at the meta-level and it is the responsibility of the meta-object to maintain the persistence of its base-level object. As an exercise, let's try to find one way of implementing the persistence at the meta-level. A handle type object that identifies a meta-object will solve our problem; from a meta-object we can obtain its handle, and vice-versa, from handle we can generate the meta-object. What we need is a class, let us call it Handle, that implements java.io.Serializable. The meta-object will implement the default serialization mechanism for its base-level object, using a symbolic model for binding its fields in the stream to the fields in the corresponding base-level object.

A client that has a reference to a meta-object can obtain meta-object's handle by invoking getHandle() method on the reference:

ObjectOutputStream stream =
new ObjectOutputStream(...);
MetaObject metaobject = new MetaObject(...);
Handle handle = metaobject.getHandle();
stream.writeObject(handle);

A client can later read the handle for the storage and will recreate a meta-object reference from the handle:

ObjectInputStream stream =
new ObjectInputStream(...);
Handle handle = (Handle)stream.readObject();
MetaObject metaobject =
handle.getMetaObject(...);

Would you recognize the advantage of redirect serialization through a meta-object handle? Consider the scenario in which the parts of technology used by the container have been replaced or upgraded. The base-level object can be resurrected because the client stored only a meta-object handle. It seems a small gain, but if you think of the speed of today's transformations and the diversity of our technologies, you will realize the advantages of a model that is not domain-specific. Let me assume that you agree about the viability of a meta-object concept. You may ask about a methodology to check the consistency of a newly created meta-object. It is a natural question and I hope the answer will satisfy your concerns. In order to validate a meta-object we need to extrapolate the type of base-meta relationship to another layer. A new kind of relationship should be established between our meta-objects and a meta-meta-object if you wish, that will be called, for simplicity, object Cluster or Container.

Container: An Object for Controlling Meta-Objects
To achieve the separation of execution domain from object programming, defining meta-objects is a necessary condition but not sufficient. Recently, it has become evident that programming by contract at the interface level is a step in the right direction, but at the same time we need to define all the operational constraints into a uniform space where the meta-objects live. In programming, as in life, our list of basic concerns is open-ended. However, we should not lose sight of the goal: to define a unified domain, a context where all the laws of object existence are simple to express and apply.

Thus, a container could provide the life cycle management for each meta-object component. If you think of the Factory pattern, we already have in place the container methodology for allowing clients to create meta-objects inside a container. The list of basic constraints can be extended, including location control, failure recovery, security services and so on. Effectively implementing a portability layer, a container lets us express design constraints and high-level system properties in a modular way, so it provides a context environment for meta-objects. The interaction between base-level objects and the container is depicted in Figure 1. The better we understand the model, the more we will know how to apply it to the framework for practical software development. If we are to understand the model design's conceptual simplicity, I need to introduce and define two terms: reflection and reification.

Figure 1
Figure 1:

The ability of a computational system to reason about and act upon itself is called reflection. Structural reflection reifies the organizational aspects of model structure. You may have studied or used the Java reflection package or runtime type identification in C++ which are implementations of structural reflection. A base-level object could be aware of changes at the meta-level environment using reflection concepts. Thus, we already have in place a methodology for reflecting structures of meta-level into base-level.

The act of making hidden information accessible is called reification. The architectural design of our model implies an enforced encapsulation not only of base-level object data, but also of its behavior. The client would be able to use the computational algorithms encapsulated in base-level objects only if the model implements the reification concept. Let us suppose that a client wants to obtain some results and it knows the location of a container satisfying its demands. The first thing the client has to do is to invoke a create() on the container's factory. The container will invoke a newInstance() method to create a new base-level object. The container will call further a setContext() function to define the computation environment in which the base-level object lives, followed by a createMetaObject(...); finally, it returns a meta-object reference to client. The meta-object exposes all the application-domain methods of the base-level object but not the interfaces that allow the container to manage and control the object. Thus, the client interacts only with a meta-object, the external representation of the base-level object whose behavior is encapsulated and reified at the meta-level.

The container may implement different methods on behalf of base-level objects like restoring their previously stored state or activating base-level objects and so forth. Many of these concepts enable a container to control the execution path regardless of the underlying operating system. All services are available to the client and more importantly to the base-level object whose main concern was only to solve the application domain-specific problem.

Unfortunately, in software engineering you cannot validate results purely by proving theorems. On the other hand, formalization still plays a fundamental role in software development because you must have a formal model of a domain before you can design effective software for that domain. Measuring the value of a model by its impact on practical software development is the only way to find out the viability of the model.

Server Component Model
As you know, in a traditional client/server relationship the client application contains graphical presentation/interaction with the user, algorithms for solving specific domain problems and data manipulations depending usually on an underlying operating system. It is natural for such an application, which has been labeled as fat-client, to be unreliable, difficult to maintain and to integrate in any changing computational environment. I suppose everyone knows that thin clients are in great demand nowadays, not as a matter of fashion but as a normal consequence of a web-based computing environment. At least, at the theoretical level, the multitier concept has been around for almost a decade. The difficult part of the transition process is to define a framework in which the server components are reusable. The main reason I introduced to you the meta-level programming architecture was to create a framework for building reusable server components.

Let's consider our base-level object as a Bean [4]. This means that our base-level objects have attributes that affect their behavior. The properties of such base-level objects could be bound, constrained and, more importantly, customized. Our objects do not have java.awt.Component as an ancestor since they are what is called an invisible Bean. Based on a well-defined protocol between a Bean and its container, the model must specify protocol interfaces. Thus, a server component model should define not only the basic architecture of a component, but also should specify the structure of its interfaces and the mechanisms by which a component interacts with its container and with other components. A typical component representation will distinguish between components that are actively transforming data and passively storing data. The Container itself does not make service demands on the meta-objects. The calls a container makes provide a meta-object with access to container services and delivers notifications issued by the component. The container might also define the ContainerContext object which gives base-level access to its container and most importantly to its meta-object.

There should be an interface container that allows a client to do the following:

  1. Obtain a factory object that allows a client to create a new meta-object in the container
  2. Obtain a finder object that allows a client to look up an existing meta-object in the container
  3. Destroy meta-objects
Eventually, the container insulates the base-level object from the specifics of an underlying component server providing a simple, standard protocol between base-level objects and a container. Thus, a client's view of a base-level object is unaffected by the container and server the object is deployed in. Therefore, a normal component server will provide a scaleable runtime environment for a large number of concurrently active base-level objects. An object cached can be shared by many clients and the performance can greatly improve for objects which are frequently read but seldom modified. Server components can be replicated and distributed across any number of servers to boost system availability and performance.

The model described has a current implementation in Enterprise JavaBeans [4], which defines a concrete component model to support multitier, distributed object applications.

Conclusion
The meta-level programming concept generates a simple and elegant server component container model. The model ensures that server components can be developed once and deployed to any system. Even though the container systems implement their runtime services differently, the interfaces ensure that a server component can rely on the underlying system to provide consistent life cycle, persistence, transaction, distribution and security services. In spite of using simple architectural primitives like reflection and reification, the model automates the use of complex infrastructure services such as transactions, thread management, and security checking.

The server component model built using a meta-level architecture has many advantages. Future articles will demonstrate how moving data manipulation logic to a server allows an application to take advantage of the power of multithreading in Java.

References

  1. E. Gamma, R. Helm, R. Johnson, J. Vlissides, "Design Patterns." Addison-Wesley, 1995
  2. D. Lea, "Concurrent Programming in Java Design Principles and Patterns." Addison-Wesley, 1996.
  3. J. Gosling , B. Joy, G. Steele, "The Java Language Specification." Addison-Wesley, 1996.
  4. T. Lorentz "Making Enterprise Java a Reality." Java Developer's Journal, Volume 2, Issue 12, www.javadevelopersjournal.com
About the Author
Jordan Anastasiade holds a BS in Architecture and an MS in Mathematics. He works for Hummingbird Communications Ltd., focusing on design patterns using object-oriented techniques in Java. Jordan can be reached at [email protected]
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.