![]() |
|
URL of the article:
Component Based Development and JSR 175
The OO approach to software development is an efficient way to build fine-grained relationships between business objects. Although this architecture style is suitable for co-located entities, it can cause some problems and challenges for distributed applications.
by Adam Bien
Component Based Development is an important vehicle to build maintainable, well-structured, software. In this article, we'll take a look at service-oriented architectures using a Java/J2EE example. Later, we will also look at the need for a consistent way to annotate the business components.
Although we have a bunch of technologies for object distribution, it's still not possible to distribute fine-grained objects without causing a measurable impact to at least some of the non-functional requirements. Since local method invocation is still much faster than a remote one, only coarse-grained objects should be exposed to the network. However, it's hard to reuse coarse-grained objects, so the reusable business logic should remain fine grained. So how do we get over this issue? The Birth of Facades Here's the issue -- we do not want to publish the fine grained entities to the client, so we'll have to provide a coarse view to them. On the other hand, we do not want to change the interface of the entity, so we'll have to provide an additional element that provides a distributable view to the system. Another responsibility of this façade is the co-ordination of the business entities, so that they still remain independent of each other. This approach is also called "Pure Fabrication", because, from the business point of view, an insignificant class was introduced to decouple the client from the business objects. The result of the pure fabrication is the Façade Pattern (see Figure 1). Figure 1: The Façade-Pattern In more complex applications the facades could also be cascaded. We should also define how mighty the facades should be. The façade works in a service oriented (or procedural) way. Clients pass some parameters to the interface and receives the required data -- they not care about the concrete structure behind the façade, they only need the business service to complete their work. Because clients does not care about the implementation behind the façade, they assume that the method (service) will be executed as a whole and the data will remain consistent. At this point, we could introduce transactions. On the other hand, the façade need not be stateless - if we've well justified reasons to maintain state on the server, then the façade should be kept stateful. In this case, the number of the method parameters could be drastically reduced. The access to some services can be restricted to authorized clients. The Decoration Now we could consider implementing all the additional functionality in the façade. But this approach would neglect the principle of Separation Of Concerns. The façade should only be responsible for the coordination of independent business objects and aggregation of the low-level functionality to coarse-grained services. The additional (and agile) functionality should be factored out. So, instead of changing the existing service, it can be decorated. The solution for this problem is found in the "Gang-Of-Four" Design Patterns book and has a name -- the Decorator Pattern. ![]() Figure 2: The Decorator Pattern The decorator has the same interface as the façade, but it's responsible for making available the additional services like transactions, security, and caching. A decorator should not implement business or domain logic -- this should be the responsibility of the business entities. The decoration process is very flexible, because it's possible to chain fine-grained and independent decorator layers together. Further, this infrastructural functionality can be completely removed, in case only a plain-façade functionality is required. We Need Components Now are able to enhance the already-implemented facades with additional functionality without changing them. But it is also important to hide the decorators and facades from the client. This can be done with a usual interface -- the responsibility of this interface is to publish only the functionality that's needed to complete the services. All the other methods are kept "private". It's perfectly appropriate to use several interfaces to publish different aspects of one façade. All these elements together have nearly the same characteristics as a "usual" object. This container of elements is also called a "component". A component has a public and a private "view" of its elements. The public view has to be further specified to be used without the knowledge of the internal component's structure - this is called "specification". The component's implementation is called "realization" and it's not required for component usage. See Figure 3. Figure 3: The Business Component Implementing some of these components could be really tedious, especially when the component's decorators are almost the same -- so the idea of reusing them with a copy & paste is not very far. However, note that the non-functional requirement maintainability and reliability could be negatively affected. For this reason, in component technologies like J2EE, the decorators were factored out from the custom components into the EJB container. The component is decorated during the deployment process or at runtime. The declarative programming is a standardized way to apply the needed behavior to a J2EE component. In J2EE this additional information is stored in XML descriptors. Strictly speaking, this annotation belongs to the component and should be developed, documented, tested, and versioned with the source code and not separately. Component Connectors The best components are built with the "Maximal Cohesion, Minimal Coupling" idea. So, in a perfect world, one component type is completely independent of another. But in the real world, projects like this rarely occur -- real world components do interact with each other to complete their tasks. For this purpose a flexible connector mechanism is needed. A connector has only one purpose - to reduce the coupling between components. There are different types of connectors: a Business Connector provides a fine-grained and therefore highly reusable view to the functionality of another component. With an Business Outbound Connector (BOC) a shortcut will be published for usage by other components. The Business Inbound Connector (BIC) specifies the functionalities needed in the development phase, while this functionality is provided by a Business Outbound Connector (BOC) of another component. Since both have compatible connectors, the two components can be connected to each other. For more details, see the tabular description in Table 1. Table 1
Having come so far, let's redefine our component: "A component is a container of elements organized in a specification and realization. The specification of the component is not only responsible to advertise provided services, but also to specify required functionalities (connectors)". To allow reuse on the component level, both these things have to be specified. Also for the installation of a component into a defined environment like EJB-container this information is valuable. See Table 2. Table 2
Figure 4: The Package Structure A Brief Introduction to JSR 175 The Annotation facility allows the definition of some descriptive information - they are also referred to as metadata or annotations in the source code of a class. Packages, classes, interface, methods, and fields can be annotated. Before JSR 175 this approach was only possible using the javadoc tool. So the documentation of a class was mixed with the annotation. Tools like Xdoclet or EJBGen have defined their own set of tags, which wasn't usually understood by the JavaDoc tool: /** * @ejb.bean name="Test" type="CMP" local-jndi-name="com.abien.cbd.TestLocal" * view-type="local" * transaction-type="Container" * cmp-version="2.x" * schema="TestSchema" * and other annotations */ Another problem is the maintenance of the allowed tags. The developer will not be able to check the syntax of the annotation without additional tools. Because the J2SE 1.4.X compiler does not understand annotations, problems with mispelled tags were only recognized in the later development phases (deployment). JSR 175 allows a strict definition of the annotations, which are checked by the J2SE 1.5 compiler. So it's easy to define custom templates, which can be used for configuration of custom architecture styles. For the xdoclet example shown earlier, a template has to be defined first: import java.lang.annotation.*; @Documented @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface EntityBeanStereotype { String name(); String cmpVersion() default 2.x; String localJNDIName(); String viewType() default "local"; String transactionType() default "Container"; String schema(); } Please note, that the tag type is no longer required - it has been replaced by the type of the annotation itself (EntityBeanStereotype). Now, some classes can be annotated by using this template. The compiler checks the syntax, so that errors can be minimized: @EntityBeanStereotype( name="Test", cmpVersion="2.x", localJNIDName="com.abien.cbd.TestLocal", viewType="local", transaction-type="Container", schema="TestSchema", )public class TestBean implements EntityBean {} The JSR 175 also allows package annotations that might be helpful in our case. Components and Annotation As mentioned earlier, components need some additional description to be successfully installed on the application server. Custom architecture styles allow the definition of additional constraints, to ensure the integrity and correctness of the application. These constraints can be also used to measure the quality (or the adherence to the already defined architecture) of software. In this article we have started to define our own service oriented architecture style. Also, a component from this architecture needs some annotations. The annotations can be compared to stereotypes from the UML. The properties of an annotation are comparable to the tagged values. All annotations related to architecture are similar to custom profiles, which are responsible for organizing stereotypes and tagged values during the inception phase. See Table 3 for some examples of layer specific annotations, and check Figure 5 to see how they are all linked together. Table 3
Figure 5: The Component Connectors The table defines some examples for layer specific annotations. A real architecture should also define annotations for methods, packages, and fields. The annotations are independent of the technology and the programming language (almost!) -- this is the reason I call them "stereotypes". All stereotypes should be used and versioned as in any other framework. For the implementation of the application the annotations have to be imported into the workbench (for example, annotations.jar), so that the compiler knows them. The definition of the annotations is the responsibility of the architect. The developer cannot change the annotation definition, he/she can only use or instantiate them. Now we have all the needed data but not tools to process it. We can obtain the stored data from the class files using reflection, and, with this approach, we can build a custom "metadata2xdoclet" converter or code generator: public class AnnotationTest { public static void main(String[] args) throws Exception{ Class clazz = Class.forName("ServiceInterface"); System.out.println("Class " + clazz + " loaded "); System.out.println("Checking for annotations "); Annotation annotation[] = clazz.getAnnotations(); for (int i = 0; i < annotation.length; i++) { System.out.println("Annotation found: " + annotation[i].toString() ); } } } On the other hand, some tools like XDoclet2 can understand the defined annotations. So, in the near future, we could get rid off annotations inside the documentation of the component. In this article we have only considered the technical aspects of a component layer. Annotations on the method-level are suitable for the refinement of the component specification. These annotations do not address issues like transactions or distribution. They are responsible to ensure the pre- and post-conditions and invariants. Components should be "designed by contract", and even the contract can be defined by an annotation. The range of a parameter and return values, exceptions, and concurrency contribute to the robustness of a component. The contract of the advertisement/publication layer should be carefully specified. Summary Component and Service Oriented Architectures need a more precise way to describe their capabilities. Until now, the descriptions were not very well standardised, which made it hard to ensure the coherence of the code and its declarative description. JSR 175 supports the Componet Based Development, since it makes it easy to put together well defined annotations for the specification and realization of a component. This information cannot get lost, since it is stored together with the class files. The debugging, testing, and deployment stages of an application can become more efficient with this process. On the other hand, it is only possible to improve an architecture, in case there is one already available. Hackers wouldn't like the idea of components and annotation. Since annotation is technology-independent, an already implemented class could be deployed with the AOP or the traditional EJB approach to the application server. The instantiation of the annotation needn't be coded in the IDEs. Model Driven Architecture tools could transform the information from Stereotypes and Tagged Values into the JSR 175 format. About the Author Adam Bien is an independent speaker, software architect, developer, consultant, and author. Adam has been working with Java and WLS from the beginning (JDK 1.0 and Tengah) in different large-scale projects and is an expert group member in the JCP.org. He has edited several books about Java and J2EE technology (J2EE Patterns, from Addison-Wesley (ISBN 38273190-X); J2EE HotSpots, from Addison-Wesley (ISBN 3827319501); Enterprise Java Frameworks, from Addison-Wesley (ISBN 3827317770); and Struts, from Software & Support Verlag (ISBN 393504237X)). These days, he is involved as an architect and developer in several J2EE/MDA-projects in Europe. He is also working with embedded Java and P2P technologies. If you have some constructive comments or ideas, send an e-mail to: abien@adam-bien.com. References
|
|||||||||||||||||||||||||||||||||||||||
|