Archive for the ‘MonoRail’ Category

Integrating MonoRail with your favorite IoC container

September 5th, 2008

I’m way outdated on my e-mail-reading and especially blog-reading. I don’t even read them anymore! I quickly scan them and try to get a gist… Someday people will actually emphasizes some words to make it easier for scanners like me to get the idea without stopping and reading every word ;-)

Anyway, I bumped into one of Jeremy’s post. Quoting:

“When we were deciding between Ruby on Rails, the ASP.Net MVC, and MonoRail for our project architecture in June, I dismissed MonoRail in small part because I didn’t want to be forced to use Windsor as our IoC tool (for some reason).”

Well, MR in no way forces you to use Windsor. For goodness’ sake, it’s on our mission page:

Castle should not be all-or-nothing. The developer can use the tools he wants to use, and at the same time, use different approaches in different areas of his application in a peaceful manner.

Of course, MR magically integrates with Windsor - what would you expect from us? But it can, just as well, magically integrates with any IoC container too.

So, what does it take to integrate MonoRail with your IoC container of choice?

First thing: MonoRail is made of several services. It had always used a composition-over-inheritance design principle that someone day more people will understand and adopt more - we’re always a decade behind, aren’t we?

(note to self: blog about all services that make the whole monster live).

So basically all you need to do is replace one or two services, depending on the level of integration you want. For the most basic integration, you might want to replace the controller factory and the filter factory by services implementation that know your IoC container (and knows how to access an instance of it). This can be made through the external monorail configuration (on the web.config) as described on the services article and on the configuration documentation.

A more complete level of integration, however, is something faily recent (10 months old I guess). Quoting an old post:

MonoRail now always uses the ServiceProviderLocator class to obtain a IServiceProvider and uses it as it’s parent container. That’s means that you can use any IoC container by simply making your HttpApplication class implement IServiceProvider (or better, IServiceProviderEx).

That means that MonoRail will always ask your code (the class that extends HttpApplication and is set on global.asax) before adding its default implementation of any of its services. That makes trivial to replace any service.

Note: I was actually going to provide a sample code but sorry, no time…

MonoRail marches into the cloud

September 1st, 2008

David is blogging about his experience with Amazon EC2. As you might know the images you can apply on a EC2 machine are all linux based. That didn’t stop him. He went on with Mono and MonoRail and got it working there. That certainly put more pressure on Castle community to get a bit more Mono-friendly/driven code. Now, if only the mono people could offer some help…

My first and last experience with EC2 was java based, and man, painful. The pain that only java can deliver. :-)

Poor man’s usability test

July 3rd, 2008

Going public with donjon was a great decision. We got several levels of feedback, and not all of them are conscious or direct. Let me go over some of these:

Exceptions

First of all, we use MonoRail Exception Chaining extension and get e-mail with all exception details, plus the page, form, query string, session and flash state, so it’s relatively easy figuring out how to reproduce the situation that caused the exception. This approach wont send us only unhandled exceptions, instead we use RegisterExceptionAndNotifyExtensions to make sure we get it. The following is a typical donjon’s action in a controller

[return: JSONReturnBinder(Properties = "Id,UserName,FullName,Email")]
[AccessibleThrough(Verb.Post)]
public User[] AddMembers(string key, string[] users)
{
    try
    {
        ...
    }
    catch(Exception e)
    {
        RegisterExceptionAndNotifyExtensions(e);
        Logger.Error("AddMembers failed", e);
        throw;
    }
}

RegisterExceptionAndNotifyExtensions will make sure the chain is going to be executed. The Logger is our ultimate place to detect problems, finally we throw so the ajax call get a status 500. Some places I dont even throw, just set StatusCode to 500 and RenderText(a nice error message)

I also find a best practice to always start writing and testing an action/page with the error handler. So my actions usually starts as this:

public void Action()
{
    try
    {
        throw new Exception("something bad");
    }
    catch(Exception e)
    {
        RegisterExceptionAndNotifyExtensions(e);
    }
}

I find a good practice to assume that at some point something might go wrong, and the user is properly notified. It’s just too much to assume that the action will always be successful.

Settings and defaults

Each user on donjon has a preferences setting. Inspecting the database I found that almost no one ever set a preference. Disturbing to me, as I think the Issue list as it is is just too bloated with information, so the first thing I do is configuring it to show less columns. Nobody did that, possibly because nobody knew how to do it.

Ok, lesson learned and code fixed. donjon will set up an user with an initial preference set to less columns.

Right side Navigation

Aaron complained about this one. It was inspired on some tool I’ve used, so I thought breaking the convention would be a good thing. Bad news to me: “don’t break user expectation” and “don’t try to create new conventions unless they are revolutionary” are some huge bullet points in the book “Prioritizing Web Usability”.

Just to unarguably prove that my decision was dumb, I found the article Navigation: Left is Best

Another lesson learned.

Others

The issue list page was designed and redesigned at least 10 times. All of them functional. The present version is the one that I’m satisfied, but I’m sure there are room for improvements.

I gotta tell you that at first it’s not easy to not to think as a programmer and instead wear the user hat when designing a page. Your programmer’s mind want to make things easier for you (code-wise), your user’s mind wants to make things much more complex. Yeah, sometimes to make the experience simpler you have to write much more code. My guess is that it’s an eternal struggle.

So expect some big improvements for beta 2 :-)

CombineJS ViewComponent

June 27th, 2008

A while ago I added this issue, which is basically a spec for a viewcomponent I urged for, in the hope that someone would face the challenge and, well, just do it. My first thought was that creating something like that would be a bit complex. Here’s the issue description:

The idea is to be able to combine and allow browsers to cache a set of files:

#blockcomponent(JSCombine)
$combiner.add("$siteroot/content/jquery-1.2.3.js")
$combiner.add("$siteroot/content/jquery-someplug.js")
$combiner.add("$siteroot/content/myjs.js")
#end

Which should output something like



The hascode should be a composition of the lastmodified of each file’s lastmodified attribute, so if a file is changed, the browser will request them again.

There should also be a CssCombine.

The goal is pretty simple: one request and all js is downloaded, hopefully cached, and if some file change a new hash will force the browsers to update their copy.

Today I decided to go on and implement that, was prepared to spend the whole day, but dont know how, was done in less than an hour.

Here the idea:

  1. Collect a list of the files
  2. Calculate a hash based on the last modified date of each file
  3. Create an in-memory aggregation of all files
  4. Register this content within MonoRail static resource service
  5. Output a script tag pointing to the newly created MR resource

And here’s the code that does exactly that:

[ViewComponentDetails("CombineJS")]
public class CombineJSViewComponent : ViewComponent
{
    public override void Render()
    {
        var combiner = new CombinerConfig(AppDomain.CurrentDomain.BaseDirectory);
        PropertyBag["combiner"] = combiner;

        // Evaluate the component body, without output
        RenderBody(new StringWriter());

        string key = (string) ComponentParams["key"];
        long hash = CalculateChangeSetHash(combiner.Files);

        IStaticResourceRegistry resourceRegistry = EngineContext.Services.StaticResourceRegistry;

        if (!resourceRegistry.Exists(key, null, hash.ToString()))
        {
            var staticContentResource =
                new StaticContentResource(CombineJSContent(combiner.Files));

            resourceRegistry.RegisterCustomResource(
                key, null, hash.ToString(), staticContentResource,
                    "application/x-javascript", DateTime.Now);
        }

        string fullName = "/MonoRail/Files/CombinedJS?name=" + key + "&version=" + hash;
        RenderText("<script type="text/javascript" src="" + fullName + ""></script>");
    }

    private string CombineJSContent(IList<string> files)
    {
        StringBuilder sb = new StringBuilder(1024 * files.Count);

        foreach(var file in files)
        {
            using(StreamReader reader = File.OpenText(file))
            {
                sb.Append(reader.ReadToEnd());
            }
            sb.AppendLine();
        }

        return sb.ToString();
    }

    private static long CalculateChangeSetHash(IList<string> files)
    {
        long hash = 0;

        foreach(var file in files)
        {
            if (File.Exists(file))
            {
                DateTime dt = File.GetLastWriteTimeUtc(file);
                hash += dt.Ticks * 37;
            }
        }

        return hash;
    }

    public class CombinerConfig
    {
        private readonly string basePath;
        private readonly List<string> files = new List<string>();

        public CombinerConfig(string basePath)
        {
            this.basePath = basePath;
        }

        public IList<string> Files
        {
            get { return files; }
        }

        public void Add(string jsFile)
        {
            Add(jsFile, null);
        }

        public void Add(string jsFile, IDictionary options)
        {
            files.Add(Path.Combine(basePath, jsFile));
        }
    }
}

And this is how to use it (Nvelocity view engine example)

#blockcomponent(CombineJS with "key=deflayout")
 $combiner.Add("Content/js/jquery/jquery-1.2.6.min.js")
 $combiner.Add("Content/js/jquery/jquery.metadata.js")
 $combiner.Add("Content/js/jquery/jquery.label_over.js")
#end

Note that the key must represent an unique set of files.

Now my plan is to add js minify capabilities, either by selecting the minified file using a naming pattern, or minifying in place.

Where no man has gone before

June 6th, 2008

Bill seems to have guts. He has posted some of his experience with a custom MonoRail + Windsor solution for a b2b app, and seems to have gone deep to customize the way components are resolved to address a business requirement. His point: majority of his clients deal with orders their own way, instead of branching the code or scattering ifs everywhere, he creates a customized component that deal with those requirement, which is automatically selected based on the client accessing the app.

To have that working, he decided to go down and speak with the devil himself. He embraced the use of containers hierarchy.

First of all a disclaimer: the support for parent/child container was added long time ago to MicroKernel/Windsor, really long time, probably in the first commits. But they never went into a _real_ real world testing. Also, I’m not sure the dependency resolution behavior is safe enough. I implemented support for one-way, bottom-up resolution to prevent someone from doing something really bad, but I’m not sure that’s enough. For all these reasons - plus a gut feeling that I shouldn’t have added this concept in the first place - I strongly discourage its use. And that’s why there are no documentation about it on castle web site, no articles (from me at least), and probably no blog posts.

Now if you like sky skiing, drives a motorcycle without helmet, have a compelling reason to use it, then why not give it a try? :-) Bill knows what he’s doing, though, he has contributed patches that demonstrates a deep understanding of the inner workings of castle.

Another idea that I played for a while was the concept of scoped containers for web apps. Mapping containers to the life styles associated with a web app: application, session, request. That would make simple to have components to lifestyles attached to an user session, for instance. However it’s a lot of work and difficult to test. I’m sure it will be available on Windsor/MonoRail 2.0

MR and Windsor set up screencast

May 26th, 2008

Quick, dirty and unrehearsed. Just show how to set up MR without external configuration, how to get Windsor integration enabled and setting up routing. Created to test Screenflow.

Mov file

Autoregistration without Binsor

May 13th, 2008

On my Global.cs I usually have:

private static void RegisterWebComponents()
{
    container.Register(AllTypes.Of<SmartDispatcherController>().
        FromAssembly(typeof(HomeController).Assembly));

    container.Register(AllTypes.Of<ViewComponent>().FromAssembly(typeof(Global).Assembly)
            .Configure(
                delegate(ComponentRegistration reg)
                {
                    reg.Named(reg.ServiceType.Name);
                }));

}

To get Windsor integration:

First, make the Global implement the IContainerAccessor

public class Global : HttpApplication, IContainerAccessor

Then add a static field to hold the container

private static WindsorContainer container;

The App_Start/End and the IContainerAccessor implementation:

protected void Application_Start(object sender, EventArgs e)
{
    container = new WindsorContainer(new XmlInterpreter());
    container.AddFacility("mr", new MonoRailFacility());

    RegisterWebComponents();
}

protected void Application_End(object sender, EventArgs e)
{
    container.Dispose();
}

public IWindsorContainer Container
{
    get { return container; }
}

To Configure MonoRail using code

Make the global implement IMonoRailConfigurationEvents and implement the method Configure:

public class Global : HttpApplication, IContainerAccessor, IMonoRailConfigurationEvents
{
    public void Configure(IMonoRailConfiguration configuration)
    {
        // Configuring ViewEngine
        configuration.ViewEngineConfig.ViewPathRoot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Views");
        configuration.ViewEngineConfig.ViewEngines.Add(new ViewEngineInfo(typeof(NVelocityViewEngine), false));

        // You can configure other things, and hopefully in the future
        // we can expose a kind of fluent interface to guide you on this process
    }
}

MonoRail with RoutingEx on IIS7

May 9th, 2008

It takes some time to figure out how IIS7 performs its handler/module matching, and to realize that the ordered list not always reflect the real order - once you set up an order click on View Unordered List and back to View Ordered List to see if it really changed to reflect what you want.

And one complain: it would be a lot easier if IIS7 and their handlers/modules could work together to identify if the handler/module was able to deal with the request or not, this is especially true for the DefaultDocumentModule. More on that later

This is how the handlers should look (in the ordered view)

iispanel.png

Steps

First of all, I’m using Vista Ultimate, so from what I’ve read, the integrated mode doesn’t work yet, so fall back to Classic mode.

After creating the web site or virtual folder, go to Handler Mapping and

  • Delete the handler for static files Empirical reason, I can be wrong: I wasn’t able to re-order this one as it seems to inherit from a parent configuration, deleting it saves a lot of hair
  • Add a new Module Mapping, request path: *.*, Module StaticFileModule, Restrictions: File and GET verb. I call this one StaticF
  • Add a new Script Map, request path *, Executable aspnet_isapi.dll (the whole path), name: MR. On the restrictions, uncheck the Invoke handler only if request is mapped to (maybe you could mark Folder, not sure if IIS is going to check if the folder actually exists, that would be a problem)

Now order the handlers. In my case I have a default.aspx, so I make the .aspx handler the first one.

Then you, as you dont want MonoRail handling static content, you make StaticF the next one

Finally MR handler should handle the rest of the requests.

I struggled for some time to get the DefaultDocumentModule working. The problem is that it conflicts with MR handler in its restrictions. You basically want that it handle a folder request, and redirect to one of the default document (if found). What actually happens is that it will greedily take all requests. Leaving no room for MR handler. The fix for that is changing MonoRail routing to be able to handle empty folders requests and matching a PatternRoute, something that today is not possible.

Let me know if that works for you.

Incremental development with Monorail

April 23rd, 2008

Just bumped into this. Great work, Ben.

Just personal preference-wise:

- I’d prefer not to use the BaseControllerTest class
- Instead of writing the form tags, I’d use the form helper: $Form.FormTag(”%{action=’save’}”) and $Form.EndFormTag(). Then you take advantage of automatic form field validation if you want.
- For actions that change things, I’d use [AccessibleThrough(Verb.Post)]
- I’d configure Windsor through code
- The mapper/wrapper sounds like iBatis. Why not using Repositories with Castle ActiveRecord?
- Instead of DTO you could use the domain class as a prototype. I think that simplifies the code.

The cool thing is that Ben knows TDD. He creates a dummy implementation, get the test passing, corner it, triangulate it, and then is forced to get rid of the dummy implementation. That’s the essence of “test-driven”.

The series is also a good way to compare MonoRail with MS MVC. The goals are the same, they just provide different ways to get the same result, up to you to use the one you like the most.

Bashing Castle and its committers

April 10th, 2008

Today I had lunch with two friends/former co-worker, on a project I’ve blogged about earlier. Nice to hear some things I’ve implemented - and which I completely forgot about - are still being used.

Anyways, this project obviously used Castle, from a local build on my machine back in 2005. A huge effort was taken to port it to RC2 - yes, Release Candidate 2 -then it was decided to skip RC3 altogether and go with whatever comes after, which I hope would be 1.0.

I was told that porting was a nightmare. Dealing with compiler errors because some interface changed or some method was renamed is one thing, I dare to say somewhat easy to deal with to some extend. The biggest problem lied elsewhere. Helpers were changed, specifically methods being added, removed and renamed, and those completely broke views.

I argued that these changes are considered important changes, and are registered on the changes.txt that every project under the Castle umbrella has. This document is used to later compile a list of new features, breaking changes and bug fixes.

My friend replied that they looked into that very document, and no mention to these specific changes were there. These at very minimum can be used to drive an smart Find/Replace on the views and get them ported.

To err is human, certainly, build those slips have high costs. This project started being used in Brazil, then South America and now it’s going world wide. It’s ridiculously big. My complain to him is that this went so long unstated. People should complain about these and similar problems.

Do it nicely, though, or I will ask you to contact our customer service to get your money refunded.

And yes, I’m to blame as well as all commiters. We are directly tight to the reputation Castle has, be it bad or good.