Announcement Announcement Module
Collapse
No announcement yet.
Aurora MVC for Spring to be open-sourced soon. Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Aurora MVC for Spring to be open-sourced soon.

    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

  • #2
    Wouldn't it be better to improve the existing Spring MVC instead of creating another project ? I also think Spring's MVC could be improved but it should be good enough to build real applications with it. Spring MVC has now some "official" documentation ( if the new book will ever be available in Germany :shock: and Matt has more time with another child :P ).
    IMO WebWork is the better MVC framework that is more natural to learn and has better validation support. On the other hand it maybe a better choice to stick with the whole Spring stack for new projects to reduce dependencies.

    Anyway I hope your project will be successful.

    Regards,
    Lars

    Comment


    • #3
      Originally posted by j2ux
      Wouldn't it be better to improve the existing Spring MVC instead of creating another project ? I also think Spring's MVC could be improved but it should be good enough to build real applications with it. Spring MVC has now some "official" documentation ( if the new book will ever be available in Germany :shock: and Matt has more time with another child :P ).

      Anyway I hope your project will be successful.

      Regards,
      Lars
      I wholeheartedly agree with both points.

      You have my sincere best wishes for your project - I know first-hand how much work and energy it takes to start and maintain an open-source project, especially when it's so tough to get others to contribute in significant (and actually useful) ways. The "Spring space" is becoming a lively one, so perhaps you'll have some luck with garnering support.

      However, for the reasons mentioned above and many others, forking a new project when other strong efforts already exist can dilute where those rare productive developers invest their time. I believe that one should always exhaust their options for contributing to the existing projects before forking a new one.

      Sincerely,
      Scott

      Comment


      • #4
        I do completely agree with you both. I think it's important that we improve what is current there rather than start something new. That is why I have sent emails to both Rod Johnson and Juergen Hoeller talking about Aurora and I gave them a link to download the source and try out the sample uses of Aurora. I do hope to hear from them because I *want* to integrate these concepts and implementations into Spring. I want things to remain consistent above all.

        However, I have not received a response but I am patient and the project takeover for "aurora" on SourceForge is still going to take at least another 2 weeks (unfortunately). If worse comes to worse, Aurora might have to be a separate project :/

        Even if Aurora does not fit into Spring's product strategy, you guys don't have to worry about inconsistencies that much. For example, Aurora is built off the DispatcherServlet, so you can take advantage of your Url mappings, view resolvers, etc - none of this will change. Aurora also implements the Controller interface defined in Spring and uses the ModelAndView concept, thus if you move your existing code to Aurora, your test cases will still function correctly providing you have tested against the Controller interface. Lastly, while I could not reuse that much past the Controller interface, I have kept things fairly similar despite the unique differences goals between Aurora and Spring MVC (however, because of the mappings and strategy implementations, it is different and that can't be avoidable).

        I hope this clears up a lot of your questions or worries. If you ask me what the ideal situation is, I'd wholehearted want Aurora to be a part of Spring. I don't care about the name or it's identity in any way. I really just want developers to use the most productive tools possible. I use this framework for my own apps, so I want others to enjoy using it too.

        I think ultimately will swing people over to the framework is when they see how little code it takes to do stuff in Aurora and when the code for doing a wizard form is the same as it is for a classic form. Let's make a form to edit this domain object:

        Code:
        public class Company implements AuroraIdentifiable {
        
        	private Long id;
        	private String name;
        	private String websiteAddress;
        	private Date date = new Date();
        	private String content;
        	private Language language;
        	private Set types;
        	private Boolean isHiring;
        	private Boolean isDoingStuff;
        
        	public Company( Long id, String name, String websiteAddress, String content, Language language ) {
        		this.id = id;
        		this.name = name;
        		this.websiteAddress = websiteAddress;
        		this.content = content;
        		this.language = language;
        		this.types = new HashSet();
        		this.isDoingStuff = Boolean.TRUE;
        	}
        
        	// ... Plus domain behaviour and properties
        Notice that we didn't set a default value for isHiring. In fact, we could not have set any default values and Aurora would have initialized them for us either during binding or mapping.

        Here is a common usage pattern in Aurora with such a domain object:

        Code:
        package net.sf.aurora.samples;
        
        import net.sf.aurora.mvc.controller.AbstractAuroraFormController;
        import net.sf.aurora.mvc.model.FormData;
        
        import javax.servlet.http.HttpServletRequest;
        import javax.servlet.http.HttpServletResponse;
        import java.util.Map;
        
        public class CompanyForm extends AbstractAuroraFormController {
        
        	public void populateReferenceData(
        		HttpServletRequest request, Map referenceData, FormData formData ) {
        
        		// Here you can add reference data by pulling it out of the database or
        		// just reference bean properties
        		referenceData.put( "languages", Language.findAll() );
        		referenceData.put( "types", CompanyType.findAll() );
        	}
        
        	public void initializeFormData( HttpServletRequest request, FormData formData ) {
        		// You could create a new No-Arg Company() or load it from the database
        		Company company = Company.createMock();
        
        		formData.setDomainValue( "company", company );
        	}
        
        	public void processFormSubmission(
        		HttpServletRequest request, HttpServletResponse response, FormData formData, Map model ) {
        
        		// Here the framework has binded, validated and mapped everything to your
        		// domain object, ready for storing into a database or whatever
        		Company company = (Company) formData.getDomainValue( "company" );
        	}
        
        }
        You don't have to actually inherit from this base controller - it's for convenience. Hooking in Aurora into a controller with a super-class already there is actually trivial since no properties are actually defined on the controller - only your business properties. I'll show how to do this in the documentation when the project goes up.

        Another thing people will like is how much stuff is pulled into configuration. The mapping to make this work is as follows:

        Code:
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE aurora-forms PUBLIC "-//AURORA FORMS//DTD BEAN//EN"
        	"http&#58;//www.upfactor.com/dtd/aurora-forms.dtd">
        
        <aurora-forms>
        	<form controller="net.sf.aurora.samples.CompanyForm"
        		formView="simple.form"
        		successView="simple.form"
        		bindOnNew="true"
        		forwardRequestValues="true"
        		>
        		<text name="company.name" />
        		<long name="company.dynamic"
        			validatorId="numeric"
        			errorMessage="Must be numeric" />
        		<htmltext name="company.websiteAddress"
        			validatorId="weburl"
        			errorMessage="Must be valid http&#58;// url"
        		/>
        		<!-- Dates can also specify a pattern="" attribute that works like formatting dates in java, which enables editing for any date format -->
        		<date name="company.date"
        			validatorId="date"
        			errorMessage="Must be DD/MM/YYYY" />
        		<htmltext name="company.content" />
        		<boolean name="company.isHiring" />
        		<boolean name="company.isDoingStuff" />
        		<selectone name="company.language"
        			referenceData="languages"
        			validatorId="singlereference"
        			errorMessage="Must enter some languages in the database." />
        		<selectmany name="company.types"
        			referenceData="types"
        			validatorId="multireference" />
        	</form>
        </aurora-forms>
        Having bindOnNew="true" and forwardRequestValues="true" allows for a recursive post to work seemlessly without the controller careing about it. This works for search-based forms that redisplay the search form with the results in the model, among other things.

        I also want to mention that the selectmany mapping is efficient in that persistent layers like Hibernate won't dump existing records in the one-to-many or many-to-many relationship and re-insert the newly added ones. Aurora is smart in that adds and removes only the modified set or list and keeps the set and it's element references the same.

        Also notice that company.dynamic is not on the domain object, but you can get it's value by going formData.getDomainValue( "company.dynamic" ); - hence the name dynamic. This means you can use aurora even if you aren't mapping to domain objects, such as just filling out a contact form that you are sending to a mailer. The decisions to be dynamic or bound to a domain object all happen transparently.

        Validators are defined seperately, however you can override the validate() method in the controller to add your own validation code if custom validators won't work for you for some reason.

        Code:
        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE aurora-validators PUBLIC "-//AURORA VALIDATORS//DTD BEAN//EN"
        	"http&#58;//www.upfactor.com/dtd/aurora-validators.dtd">
        
        <aurora-validators>
        
        	<validator id="null" class="net.sf.aurora.validation.object.NullValidator" />
        	<validator id="alpha" class="net.sf.aurora.validation.object.AlphaValidator" />
        	<validator id="alphanumeric" class="net.sf.aurora.validation.object.AlphaNumericValidator" />
        	<validator id="date" class="net.sf.aurora.validation.object.DateValidator" />
        	<validator id="email" class="net.sf.aurora.validation.object.EmailValidator" />
        	<validator id="emptystring" class="net.sf.aurora.validation.object.EmptyStringValidator" />
        	<validator id="file" class="net.sf.aurora.validation.object.FileValidator" />
        	<validator id="numeric" class="net.sf.aurora.validation.object.NumericValidator" />
        	<validator id="password" class="net.sf.aurora.validation.object.PasswordValidator" />
        	<validator id="singlereference" class="net.sf.aurora.validation.object.SingleReferenceValidator" />
        	<validator id="multireference" class="net.sf.aurora.validation.object.MultiReferenceValidator" />
        	<validator id="time" class="net.sf.aurora.validation.object.TimeValidator" />
        	<validator id="url" class="net.sf.aurora.validation.object.UrlValidator" />
        	<validator id="weburl" class="net.sf.aurora.validation.object.WebUrlValidator" />
        	<validator id="ziporpostalcode" class="net.sf.aurora.validation.object.ZipOrPostalCodeValidator" />
        
        </aurora-validators>
        Anyway, that's the basics to give you an idea on how it works.

        Regards,
        Ken

        Comment


        • #5
          Originally posted by egervari
          However, I have not received a response but I am patient and the project takeover for "aurora" on SourceForge is still going to take at least another 2 weeks (unfortunately). If worse comes to worse, Aurora might have to be a separate project :/
          Why don't you use dev.java.net ? The name "aurora" is still free and it's a project related to Java.

          The examples look promising and it would be the best solution if the Spring team would incorporate (at least parts) your solutions.

          The validator stuff reminds me of WebWork

          Regards,
          Lars

          Comment


          • #6
            What became of Aurora? I don't see it on sourceforge, per URL in Pro Java Dev with SFW.

            Comment

            Working...
            X