egervari
Aug 15th, 2004, 05:40 AM
During the past week and a half, I've been developing a new MVC for Spring. "Yet another MVC" you say? While in most circumstances I would whole-heartedly agree, however, I think building this one is justified.
For one, I don't think there that many great MVCs out there. They do the job, sure, but they are far from perfect. Struts seems to have won the hearts of many in the Java community, but it's not without its problems. For instance, Struts is not built on interfaces, it's rather complicated for something that is supposed to be simple and to put it bluntly, the Apache guys usually flood some their products with cool but needless features or suffer from one major design constraint that makes their products unusable in many circumstances (Maven, Digester, etc.). The best things that came out of Apache for Java are essential Ant and Log4J, which are the simplest tools I can think of. Tomcat comes in as number three.
WebWork is another choice. I'll admit, I haven't used the latest version all that much but I've read some comments that it's harder to get to work in Spring as opposed to Struts and you are dealing with non-Spring artifacts all across the board, from configuration to interceptors. I'd rather use equivalent Spring functionality, especially as far as interceptors are concerned (both the web-model ones and the built-in support for AOP).
One of the things I really liked about Spring is that it separated the Dispatcher Servlet from the Controller interface very elegantly and pushed away a lot of details that sometimes the controller would ordinarily be responsible for. In my perspective, this separates all the boilerplate code from the things you'd want to customize. I have to give major credit to guys at Spring for making their Dispatcher Servlet extremely flexible and integrating it nicely with application contexts, no matter how many Servlets you have running in the container. Aurora builds off all of this functionality.
Another feature I liked in Spring was their ModelAndView concept. I've been using this model with a lot of success, generating JSPs, Velocity scripts, FreeMarker scripts and PDF documents. I have to say that it's the easiest approach as it doesn't require a lot of upfront setup to make it work and swapping velocity to FreeMarker (for example) doesn't incur any application code changes (just template changes, which you'd expect to work with anyway).
As good as Spring MVC is, it's not perfect. For one, it does not separate configuration away from application web-tier logic of the controller. Sure, you could configure any bean properties on a Controller via Spring IoC, but it also includes your business properties as well. I also wanted to do more complex configuration on my own, and unfortunately, this can't be done very concisely using Spring's Bean XML. This required a separate XML DTD altogether, but one that has Spring in mind.
However, these are things I think most people can put up with. After all, Spring MVC is a simple approach that people can easily work with. However, Spring's Controller concept is somewhat self-defeating in that you basically extend from their pre-implemented controllers anyway, such as SimpleFormController or AbstractWizardFormController. This would be fine in many respects; however, the programming model is slightly different between these two, so you'll have to do some extra work in refactoring a normal form to use a wizard, and vice-versa. In practice, I've found this to be fairly limiting.
Furthermore, because Spring suggests that you extend from their class hierarchies in form cases, you cannot inherit web-tier logic that is common throughout your application in a singular fashion. Thus, if you decide to use a wide variety of controller super-classes, you have no choice but to use delegates or static utility classes to avoid the duplication across controllers. This makes application-code rather messy, as the application code needs to cater to the design of the framework.
There is a way around this limitation. If you subclass the specifics of a particular form controller into a separate class using the Strategy Pattern, you can freely use the same form Controller sub-class for your entire application, avoiding the use of delegates and the like. This also has the advantage that your web-related application logic is in one place. So, if you do any common bean lookups or have application-specific HttpServletRequest behavior, you can actually put this in a base Controller for your application. If you need a different programming model than those supported by classic forms or wizards, you can implement your own separate from Controllers implementations.
This is a pretty big shift in paradigm for such a simple conceptual change. This allows Controllers to fully realize that they are first-class, application-level components and not used for framework plumbing. It makes sense that the framework should be placed elsewhere and this is the approach that Aurora takes. WebWork and Struts suffer from similar problems.
One of the other problems that I often found when working with any MVC framework is that they like to deal in primitives or arrays and not the domain objects that are pertinent to your application. This usually doesn't become a problem if your domain objects are simple and have no dependencies, but in all likelihood, this is not the case. Aurora supports a simple model for mapping request values to domain objects, which goes beyond simple binding through property editors. Instead, Aurora employs the concept of Controls where you can associate a framework or custom Control class for each field on the form which dictates how it's initialized, bound and mapped to your domain object.
These controls can also handle special cases generically, outside of your application. For example, you don't have to worry about checkbox values being null when using a BooleanControl or automatically escaping HTML characters via the HtmlTextControl. When a framework control does not suit your needs, Aurora supports factories for declaring your own custom controls during startup, which can implement the Control interface or can extend from any of the support classes supplied by Aurora.
The controls are also not bound to HTML in any way, except for the HtmlTextControl obviously but you need not use it if you don't want to. The Controls are implemented generically so that they can theorectically be used with other technologies over HTTP.
With Aurora, you can not only map any kind of primitive type, but you can map you're specific domain objects, or even collections of them. This isn't magic, but it is pretty simple as all you have to do is implement the AuroraIdentifiable interface, which makes you implement Object getAuroraIdentity(). All you have to do is return the object that acts as the identity for your object, where this object must implement equals(). Now, you can create either a SelectOneControl or a SelectManyControl for that particular bean property and the framework will map the objects in your controller's reference data (a concept already familiar to Spring users) directly to your bean properties.
Lastly, one of the big features that Aurora provides is an improved configuration model that separates all the controller details into a single place for the application. Thus, you can set controller strategies (such as classic Forms or Wizards), page flow, controller settings and obviously the fields on the form with their Controls. This central facility is actually very intuitive since it looks a lot like Hibernate's configuration, but instead of mapping objects to relational tables, you are specifying request values to domain objects but in much less detail than required by Hibernate. This also eliminates the need for having an extra class for Form Data and optionally, you can choose not to implement a Validator class either as Aurora allows you to specify Validators with Controls. This makes form implementations very clean and focused.
All in all, it's been a fairly positive development experience and I'm going to open-source the project soon to the community. I really wanted something working, tested, and worthy of using before the community even saw it because I wanted it to be good, and not one of those projects that die (like most). However, there are still a lot of efforts that need to be made, such as documentation, merging the two with Spring even further, some more advanced features to increase productivity, and some efforts to remove the very few Aurora dependencies that exist in application objects, etc., but these are efforts I'm hoping people can help out in.
I'm currently talking with Rod to see if Aurora fits into their product strategy although I still have yet to hear from him and the Spring Team. The code, as described above, is already developed, so it's matter of setting up a project in source-forge or adding something under Spring. If you are interested in helping on such a project, be sure to post a comment on my blog saying where I can contact you.
Blog:
http://www.jroller.com/page/egervari
For one, I don't think there that many great MVCs out there. They do the job, sure, but they are far from perfect. Struts seems to have won the hearts of many in the Java community, but it's not without its problems. For instance, Struts is not built on interfaces, it's rather complicated for something that is supposed to be simple and to put it bluntly, the Apache guys usually flood some their products with cool but needless features or suffer from one major design constraint that makes their products unusable in many circumstances (Maven, Digester, etc.). The best things that came out of Apache for Java are essential Ant and Log4J, which are the simplest tools I can think of. Tomcat comes in as number three.
WebWork is another choice. I'll admit, I haven't used the latest version all that much but I've read some comments that it's harder to get to work in Spring as opposed to Struts and you are dealing with non-Spring artifacts all across the board, from configuration to interceptors. I'd rather use equivalent Spring functionality, especially as far as interceptors are concerned (both the web-model ones and the built-in support for AOP).
One of the things I really liked about Spring is that it separated the Dispatcher Servlet from the Controller interface very elegantly and pushed away a lot of details that sometimes the controller would ordinarily be responsible for. In my perspective, this separates all the boilerplate code from the things you'd want to customize. I have to give major credit to guys at Spring for making their Dispatcher Servlet extremely flexible and integrating it nicely with application contexts, no matter how many Servlets you have running in the container. Aurora builds off all of this functionality.
Another feature I liked in Spring was their ModelAndView concept. I've been using this model with a lot of success, generating JSPs, Velocity scripts, FreeMarker scripts and PDF documents. I have to say that it's the easiest approach as it doesn't require a lot of upfront setup to make it work and swapping velocity to FreeMarker (for example) doesn't incur any application code changes (just template changes, which you'd expect to work with anyway).
As good as Spring MVC is, it's not perfect. For one, it does not separate configuration away from application web-tier logic of the controller. Sure, you could configure any bean properties on a Controller via Spring IoC, but it also includes your business properties as well. I also wanted to do more complex configuration on my own, and unfortunately, this can't be done very concisely using Spring's Bean XML. This required a separate XML DTD altogether, but one that has Spring in mind.
However, these are things I think most people can put up with. After all, Spring MVC is a simple approach that people can easily work with. However, Spring's Controller concept is somewhat self-defeating in that you basically extend from their pre-implemented controllers anyway, such as SimpleFormController or AbstractWizardFormController. This would be fine in many respects; however, the programming model is slightly different between these two, so you'll have to do some extra work in refactoring a normal form to use a wizard, and vice-versa. In practice, I've found this to be fairly limiting.
Furthermore, because Spring suggests that you extend from their class hierarchies in form cases, you cannot inherit web-tier logic that is common throughout your application in a singular fashion. Thus, if you decide to use a wide variety of controller super-classes, you have no choice but to use delegates or static utility classes to avoid the duplication across controllers. This makes application-code rather messy, as the application code needs to cater to the design of the framework.
There is a way around this limitation. If you subclass the specifics of a particular form controller into a separate class using the Strategy Pattern, you can freely use the same form Controller sub-class for your entire application, avoiding the use of delegates and the like. This also has the advantage that your web-related application logic is in one place. So, if you do any common bean lookups or have application-specific HttpServletRequest behavior, you can actually put this in a base Controller for your application. If you need a different programming model than those supported by classic forms or wizards, you can implement your own separate from Controllers implementations.
This is a pretty big shift in paradigm for such a simple conceptual change. This allows Controllers to fully realize that they are first-class, application-level components and not used for framework plumbing. It makes sense that the framework should be placed elsewhere and this is the approach that Aurora takes. WebWork and Struts suffer from similar problems.
One of the other problems that I often found when working with any MVC framework is that they like to deal in primitives or arrays and not the domain objects that are pertinent to your application. This usually doesn't become a problem if your domain objects are simple and have no dependencies, but in all likelihood, this is not the case. Aurora supports a simple model for mapping request values to domain objects, which goes beyond simple binding through property editors. Instead, Aurora employs the concept of Controls where you can associate a framework or custom Control class for each field on the form which dictates how it's initialized, bound and mapped to your domain object.
These controls can also handle special cases generically, outside of your application. For example, you don't have to worry about checkbox values being null when using a BooleanControl or automatically escaping HTML characters via the HtmlTextControl. When a framework control does not suit your needs, Aurora supports factories for declaring your own custom controls during startup, which can implement the Control interface or can extend from any of the support classes supplied by Aurora.
The controls are also not bound to HTML in any way, except for the HtmlTextControl obviously but you need not use it if you don't want to. The Controls are implemented generically so that they can theorectically be used with other technologies over HTTP.
With Aurora, you can not only map any kind of primitive type, but you can map you're specific domain objects, or even collections of them. This isn't magic, but it is pretty simple as all you have to do is implement the AuroraIdentifiable interface, which makes you implement Object getAuroraIdentity(). All you have to do is return the object that acts as the identity for your object, where this object must implement equals(). Now, you can create either a SelectOneControl or a SelectManyControl for that particular bean property and the framework will map the objects in your controller's reference data (a concept already familiar to Spring users) directly to your bean properties.
Lastly, one of the big features that Aurora provides is an improved configuration model that separates all the controller details into a single place for the application. Thus, you can set controller strategies (such as classic Forms or Wizards), page flow, controller settings and obviously the fields on the form with their Controls. This central facility is actually very intuitive since it looks a lot like Hibernate's configuration, but instead of mapping objects to relational tables, you are specifying request values to domain objects but in much less detail than required by Hibernate. This also eliminates the need for having an extra class for Form Data and optionally, you can choose not to implement a Validator class either as Aurora allows you to specify Validators with Controls. This makes form implementations very clean and focused.
All in all, it's been a fairly positive development experience and I'm going to open-source the project soon to the community. I really wanted something working, tested, and worthy of using before the community even saw it because I wanted it to be good, and not one of those projects that die (like most). However, there are still a lot of efforts that need to be made, such as documentation, merging the two with Spring even further, some more advanced features to increase productivity, and some efforts to remove the very few Aurora dependencies that exist in application objects, etc., but these are efforts I'm hoping people can help out in.
I'm currently talking with Rod to see if Aurora fits into their product strategy although I still have yet to hear from him and the Spring Team. The code, as described above, is already developed, so it's matter of setting up a project in source-forge or adding something under Spring. If you are interested in helping on such a project, be sure to post a comment on my blog saying where I can contact you.
Blog:
http://www.jroller.com/page/egervari