dev@jersey.java.net

RE: [Jersey][WebDAV] Fluent API of WebDavBuilder

From: Markus KARG <markus.karg_at_gmx.net>
Date: Mon, 26 Jan 2009 20:32:16 +0100

Craig,

thank you so much for your comments. More inlined.

> > For example, how to add multiple instances of the same class to a
> root
> > element?
> >
> > return new MultiStatus(new Result(x), new Result(y)); (non-fluent)
> >
> > return WebDavBuilder.multiStatus().result(x).result(y); ?
> >
> > return WebDavBuilder.multiStatus(new Result(x), new Result(y)); ?
> >
> Technically, either of these can work.

Sure. But none of the two fluent variants is easier to write or read than
the non-fluent variant, so what does the user win? A longer code and the
confusion how to add something to the result, obviously!

> One important principle illustrated in my example above (and sometimes
> violated in the JAX-RS and Jersey instances of the pattern) is that the
> "this" object you are building up should not change as the expression
> gets evaluated. In your first pattern, I'm assuming that the result(x)
> and result(y) methods return the MultiStatus instance you are adding
> to,
> so that would be obeying the pattern. However, what if result() was
> itself a builder that let you construct a Result? That would be
> confusing to the user, because the "." connectors in the expression
> after this would refer to that Result, and you could never get back to
> adding things to the MultiStatus.

Ok, this will work for HelloWorld, but to tell the whole truth about WebDAV
and demonstrate the dilemma of a fluent API: A valid WebDAV Result is not
constructed by just code and message (Result(int, String)) but a lot of
other objects which again have a lot of references... This code line shows
what currently is typical for WebDAV and must be replaced by a fluent API:

-- SNIP --

// Excerpt from webdav-addressbook sample

final Response folder = new Response(new HRef(uriInfo.getRequestUri()),
null, null, null, new PropStat(new Prop(new MicrosoftRedirectorPatch2(), new
DisplayName("My Collection"), new CreationDate(new Date()), new
GetLastModified(new Date()), COLLECTION), new Status(OK)));

if (depth.equals(DEPTH_0))
        return new MultiStatus(folder);

final Collection<Response> responses = new
LinkedList<Response>(Collections.singletonList(folder));
for (final Contact c : (List<Contact>)
this.em().createNamedQuery("ListContacts").getResultList())
        responses.add(new Response(new
HRef(uriInfo.getAbsolutePathBuilder().path(String.format("%s.adr",
c.getMatchCode())).build()), null, null, null, new PropStat(new Prop(new
MicrosoftRedirectorPatch2(), new DisplayName(String.format("%s %s",
c.getLastName(), c.getFirstName())), new CreationDate(c.getCreationDate()),
new GetLastModified(c.getLastModified()), new GetContentLength(0), new
GetContentType(ADDRESS_MIME)), new Status(OK))));

return new MultiStatus(responses.toArray(new Response[0]));

-- SNIP --

This is NOT an overly complex example -- it demonstrates the MINIMUM that is
needed to construct a valid MultiStatus instance. Have a look at the
contrib/webdav source and you'll notice that we're talking about dozens of
XML elements that must be combined only in the valid schema (and the builder
shall enforce and support that).

So how to turn THIS into a fluent API without using a lot of different
builders or split up into a lot of lines (and in both cases blow up the
content by far)? Cannot imagine a solution.

Any suggestions...? I do not see that it is possible AND useful to the
user... :-(

Thanks
Markus