Return-Path: Delivered-To: apmail-xml-cocoon-dev-archive@xml.apache.org Received: (qmail 13401 invoked by uid 500); 26 Feb 2003 15:38:45 -0000 Mailing-List: contact cocoon-dev-help@xml.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: list-post: Reply-To: cocoon-dev@xml.apache.org Delivered-To: mailing list cocoon-dev@xml.apache.org Received: (qmail 13316 invoked from network); 26 Feb 2003 15:38:43 -0000 Received: from fo1.kc.aoindustries.com (209.15.201.17) by daedalus.apache.org with SMTP; 26 Feb 2003 15:38:43 -0000 Received: from apache.org ([66.208.12.130]) (authenticated) by fo1.kc.aoindustries.com (8.11.6/8.11.6) with ESMTP id h1QFcjX31208; Wed, 26 Feb 2003 09:38:45 -0600 Message-ID: <3E5CDF85.5000602@apache.org> Date: Wed, 26 Feb 2003 10:38:45 -0500 From: Berin Loritsch User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.2.1) Gecko/20021130 X-Accept-Language: en-us, en MIME-Version: 1.0 To: dev@avalon.apache.org, cocoon-dev@xml.apache.org Subject: [RT] Managing Dependencies in a Component Based Environment (long) Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N In this random thought, I want to address the issue of change in a component based environment. The reason I am bringing it up is because of some of the issues that are present in Cocoon's CVS structure with dependencies on dependencies on dependencies, and the issues that the Avalon team faces daily as they try to make their projects better. Through this excersize, we will hopefully learn just what amount of change is really possible in a component based environment. BACKGROUND For the uninitiated, components are *supposed* to allow for greater degrees of change under the hood. The do this by all components being loosely coupled. Each component that requires another component uses that other component according to the contracts put in place in its interface. While not all contracts can be effectively expressed in code, they can be expressed in the javadocs. Components live inside of a container. THe container's responsibility is to create, manage, map, and destroy all the components it controls. As a result, there are a great many things that a container can incorporate into its management of components. Things like pooling and caching can be done at the container level, as opposed to the component level. That's the theory anyway. WHAT CAN GO WRONG For developers who are not used to component programming what inevitably happens is that component contracts are either incorrectly specified, or because the user changes an implementation they want to have a new interface as well. Components are really cool in that they will allow you to have vastly different implementations, all of which will work without breaking the system. As long as the contracts surrounding the component interface are flexible enough to allow new implementations, yet strict enough to force them to be compatible, we won't have any problems. What happens if for some reason the interface changes? For things as simple as a package change (the actual binary interface is in all other ways identical) the change can be handled by a dynamic proxy. It's really simple and all existing code still works. For other situations, the new component may still offer the same *role*, but with new semantics. A common and flexible solution is to provide a manual proxy, or a facade component. The facade component acts as a placeholder for all requests to the old component and translates them into requests for the new component. The facade pattern is a powerful tool if used wisely. You can play with the interface until you find something that *works*, and then change all the underlying implementations to the new interface. That takes care of the back end. EXISTING COMPONENTS A challenge to frameworks that have been around for a while like Cocoon is that other users outside of that framework's control use it. It is a good thing until you have to change something. So how do we manage that change? The facade component can be a useful tool to keep around, that will nag the user at runtime until they change their code. You don't have to be as heavy handed at that, but the facade component can work to maintain backwards compatibility as well as it can help the change toward a new system. Components can change more dramatically behind the scenes than you might think. As long as a component supports the client/component contracts it is compatible. Everything else behind the scenes can change and everything still works. META INFORMATION AND CONTAINERS WITH COMPONENTS Something in the pipeline for the future of Avalon is allowing the developer to express meta information about the components. What this meta information allows is interaction with the container in a more automatic way. Caching hints can be expressed as component attributes, and the container can interpret those attributes and provide the right level of caching. The caching implementation can vastly change, but the component won't have to change its implementation. The example of "component caching" is a bad example mainly because it is *resources* that should be cached. Components are simply meant to be used. However, it is a reality that Cocoon deals with. Each step in the pipeline can be cached. That is a good thing, but the cache was applied to the component and not the resource that it produces. This choice was mainly because all the resources produced by the same component usually have the same caching attributes. It is a "shortcut" so to speak. The problem is that Cocoon expressed in component interfaces what is really meta information. In their defense, at the time Cocoon was being written, not even Phoenix had the notion of meta information. They did what they could do given the limitations of their container. This is a bigger problem than simply changing a client interface, or something along those lines. It can still be handled by a facade component that makes the translation from the old caching artifacts to the new ones. HOW TO MANAGE FACADE COMPONENTS It is important to keep the core API of a framework as simple as possible. That is why I advocate the use of an API JAR and an implementation JAR. The API jar has all that is needed to properly express the client/component contracts in a framework. The implementation JAR has all the blessed implementations of the components. When you need to support legacy users, you can place all the facades and deprecated APIs in a separate compatibility JAR. Both the interfaces and the facade implementations are included in that JAR. The users that don't want to keep up with change can simply include that JAR and everything would work as it has in the past. That would allow the Cocoon system to clean itself up, and separate alot of dependencies. The chalange is of course to make the compatibility JAR completely separate from the original source. If I am missing something, let me know.