Component Manager Requirements
Warning
This Page was created by Ian Boston, and until it is reviewed by the Framework Working Group and members of the community it remains Ian Boston's view of the requirements. This page attempts to consolidate a number of discussion threads on the Framework and Sakai-Dev mailing lists.
Introduction
This page is an attempt to identify the requirements and problems and present a broad spectrum of possible futures for the Sakai's Component manager. At the time of writing there did not appear to be a focus on these requirements or the broad spectrum of possible solutions, rather a subset of preferred solutions. If you are reading this as dont see a requirement that you have, or a solution that you believe to be significant please add to the list.
Related Pages
Here is a list of related pages that talk about the component manager in no particular order.
- http://confluence.sakaiproject.org/confluence/display/ENC/Component+Manager
- http://confluence.sakaiproject.org/confluence/display/SAKDEV/Component+Manager+Upgrade
- http://confluence.sakaiproject.org/confluence/display/CONF07/The+Unbearable+Heaviness+of+Shared+%28Component+Manager+Directions%29
- http://confluence.sakaiproject.org/confluence/display/REQ/Framework+Requirements (Section on Component Manager)
Requirements
The following Requirements have been gathered from the list and are grouped by stakeholder. There is no indication of the relative importance of each requirement or a balance against the risk or resources required to deliver. Obviously with unlimited resources and neglagable risk we would like to have all, but reality isn't like that.
- Production
- Reloadablility
- able to recover/reload a failed service automatically or manually
- ability to restart/upgrade a service without interruption of user
activities
- Memory Footprint
- reduce perm space usage
- Remove Components from deployment
- Better controlled component startup
- No classloader leaks from the first webapp
- Eliminate risk of total failure if first webapp reloads.
- Reloadablility
- Developers
- Faster Development Cycle
- Better Component Isolation
- reduce the size of the shared space
- reduce number of binding dependencies
- Separation between internal beans and exported service implementations.
- able to use multiple versions of jars without conflicts
- A Better Provider or Optional Mechanism
- Reducing Code maintenance Costs
- Reducing reliance on non mainstream technologies and techniques
- Standard ClassLoader Environment
Faster Development Lifecycle
Core Sakai takes a long time 120 - 200s to startup and developers struggle to make headway when developing components for Sakai if they have to wait for tomcat to startup every time they make a mistake.
Developing webapps is faster since webapps can be reloaded by the developer without having to restart tomcat. Developing components is harder since a full container restart is needed, and where Spring configuration files are complex its very hard to get the component file right first time, hence there are usually lots of container resarts.
So it is felt that making components reloadable as in the webapp environment will speed this cycle. This is certainly true, however there are many other ways of achieving the same ends that should be considered.
- Unit Testing, Integration Testing, Acceptance Testing all greatly increase the speed at which applications are developed and the code quality. Currently the development cycle for a unit test is about 250 times faster than starting tomcat. Reloading a web application is about 10 times faster than restarting tomcat, presumably a reloadable component would be of the same order. Making it easier to reload components might reduce the incentive to write test code that runs in eclipse and maven.
- Using the Framework distribution to develop components reduces the Sakai startup to about 15s comparable with other Tomcat applications, making it possible to get components working more easily.
Better Component Isolation
We currently have a reasonably large number of jars in shared. Some are API's, and others are there because they need to communicate over multiple webapps. Spring has been placed in shared since it provides the basis for the Component Manager, but since it is in shared space, anything that it depends on has to also be in shared. This pulls in Hibernate which makes it impossible to use anything else that depends on a different Hibernate version, eg Alfresco. Because Hibernate's session handling is essentially static (class-level), having Hibernate in shared also forces Sakai developers to split their persistence-handling code out into its own shared JAR. Since the whole point of Hibernate is to make development faster and more straightforward, this unusual build structure makes Hibernate a much less attractive option.
We could devise clever classloader isolation mechanisms, better jar isolation could also be achieved by removing Spring from shared.
Component Isolation is also about the isolation of components that are exposed or exported to other components or webapps. At present every Spring bean is available by name to every other component. This makes it hard for more sophisticated users of Spring to get correct as they may get references to internal beans when performing wild card queries against the component manager. Less sophisticated developers can easily trample on other components' internal beans without realizing it. Finally, the all-in-one namespace makes it impossible to know which bean properties can be overridden fairly safely and which ones should be left alone.
Reloading Components
In production we need manage the servers and have the ability to fix app servers when they go wrong. Currently when an appserver goes wrong, the users on that app server will experience some loss of service, and then when the app server is restarted they will loose their sessions. The promise of reloadability is to replace or repair individual components within the app server rather than restart it. Component reloading works by emptying a component of active threads by queuing up threads in a safe place, usually a proxy, and then reloading the component - releasing the threads when done. If the component can't be emptied of active threads the reload will fail and the reload can't save components that are causing out of memory errors. The users will notice a period of delay, and if the component fails to restart they will probably notice errors and perhaps loss of session.
An alternative approach to making it possible to manage the app servers without interrupting the users it to make their sessions mobile. If this is achieved it becomes possible to restart entire app servers one by one without impacting the users sessions since each request can use any app server in the Sakai cluster. Reloading individual components is useful when Sakai is hosted on a single box or when users sessions are not mobile between app servers in the cluster.
Memory Footprint
Sakai uses a large number of classes and strings all of which require a part of memory called perm space. Once a class definition is loaded this perm space cannot be recovered until all references to that class are removed from the classloader that it was created in. A class that is loaded by 2 classloaders independently will consume twice as much perm space. Although this is not a huge amount of space, technologies like JSP and JSF consume perm space for every page. Sakai has a reasonably high perm space usage, that does reduce the amount of memory required to run Sakai. Ideally we would want to reduce the amount of perm space.
Component Startup
The Component manager starts up as a side effect of the first webapp starting. As a result some of this webapps classloader environment leaks into the component manager, and causes some dependency on which webapp starts. If that webapp is ever reloaded its is likely that the app server will stop responding correctly to requests as parts of the component manager may become detached.
It would be extremely good to have a component startup that did not have a classloader leak and was not vulnerable to webapp order or a webapp restart.
Provider and Optional Service mechanism
In some areas of Sakai we have Optional or Provider interfaces that are only used if a component named after that interface exists. The current mechanism is to check if the expected bean exists and then use it if it does. Unless special work is done to make the top-level provider a proxy and to make all target provider implementations "lazy", this means that only one binary implementation of a provider interface can be deployed, which in turn forces institutions to edit and build from source code rather than simply configuring a binary distribution of the product. Obviously that doesn't fit well with the original promise of drop-in selectable interfaces.
Reducing Code Maintenance Costs
We have a continued process of attempting to reduce the cost of code maintenance in Sakai. This is partially around reducing the amount of code by taking best of breed third party libraries to perform well defined tasks, usually with a well defined Standards based API (eg JDBC, JMS, JSR-168, JSR-170). In addition to this we must always we alert to keeping the code base as simple as possible so that new developers can maintain more of the code. If we all need to be advanced Java programmers to maintain the code base, then there will not be many of us and everyone will have to work much harder.
Reducing reliance on non mainstream technologies.
We want to reduce the complexity of Sakai and make it easier to develop for Sakai by adopting mainstream technologies that even the most basic Java programmer could be expected to understand. J2EE webapps are an example of this approach. Most Java programmers understand what a webapp is. Where we have a reliance of a non mainstream 3rd party library that impacts the way in which developers work, we should try and reduce its scope and bindings. Where we have an internal structure that is hard to understand or causes developers problems, we take considerably more time to explain it whilst adapting it an making it simpler to engage with.
Standard ClassLoader environment
Many modern, and particularly Spring-based libraries, cannot function correctly in Sakai's current ClassLoader environment, since in component code the context ClassLoader is incorrectly set to the most recent webapp which serviced a request. The concept of the context ClassLoader was introduced in Java 2, and an increasing number of libraries are relying on it. With an incorrect context ClassLoader, many packages, such as Alfresco, or ActiveMQ cannot be used without a good deal of custom coding, in the case of Alfreso a prohibitive amount. At a lower level, the incorrect ClassLoader causes unexpected effects with many Spring-based techniques (resource loading, creating proxies etc.) which steepen the learning curve for developers who are familiar with Spring but not Sakai. (For example, transaction proxies in a component can only be defined against targets which have an interface in shared space.) Correcting the context ClassLoader is one of the functions that could be achieved by a proxy-based design (see below).
Possible Component Manager Layouts
Here are 8 possible layouts for component managers in Sakai. Each one addresses some although not all of the requirements above. Each one also represents compromises and resource cost implications. There is no perfect solution that costs nothing and presents no risk.
The standard Sakai component manager is a static bean that starts when first used. It initializes a single Spring context and then loads beans into that context using a dedicated classloader for each bean. It gives classloader isolation between components from implementations but we think that is has a classloader leak from the webapp that causes it to start. Components are not releoadable and all the APIs live in the shared classloader as does Spring Hibernate and anything that Spring binds to. This can include any view technology that Spring has provided adapters to.
In order to address the lack or reloadability a branch was done by Antranig as the first step on the 'Component Manager Upgrade" effort are described in this page http://confluence.sakaiproject.org/confluence/display/SAKDEV/Component+Manager+Upgrade This work was undertaken as a result of A BOF organized by Antranig at Amsterdam which is documented here http://confluence.sakaiproject.org/confluence/display/CONF07/The+Unbearable+Heaviness+of+Shared+%28Component+Manager+Directions%29
Proxies have been placed automatically over the top of every spring bean and some work has been done to make ensure the setters and getters work. Although reloadability has not be tested, this branch is trying to achieve that end.
Ray has more recently done an experimental branch that takes replaces the blanket proxy mechanism with a specific proxy mechanism so that components can start define what beans are exported. With proxies reloadabiity might be achievable at the component level, but this has not been tested.
A full Spring OSGi container model requires that the Spring OSGi Container is the top level container. With this model we would become a full Spring follower and would not be able to run on any other J2EE container. We would have to embed a J2EE webapp container which would probably be Tomcat. This model was demoed at the Spring Experience 12/2007 but as a write this it has not been released. I am not 100% clear on the details of the implementation so the above diagram is based on what I have been able to understand from email exchanges.
An alternative approach to avoid having to startup the OSGi container on the command line and give the impression of being able to run in any web application container is to embed the full Spring OSGi container into a single monolithic web application, and provide a bridging servlet into the internal web application. Although this might make it possible to run Sakai in any container, it is a hack to achieve that end.
Removing Spring from shared and replacing the top level component manager with a simple registry of services will greatly increase the ability for developers to choose which jars they use, there is both classloader and spring isolation although we still have a classloader leak from the webapp.
Embedding an OSGi container such as Felix, Equinox or something else, will eliminate the need to bind to Spring OSGi, but will break the OSGi model as the Service API's will not be reloadable. However its quite unlikely that we would want to change Service API's without a restart.
Adding in a Loader that binds to the tomcat lifecycle will eliminate the webapp classloader leak. This could be applied to any of the webapp based loaders, but is not necessary for the Spring OSGi containers.