Hi Tim,
Iâve read the thread again and prefer to wait post 2.1 to make decisions. Some thoughts for now:
· As javax.inject isnât part of Java SE (yet?), we canât add a dependency on it directly from org.restlet.jar
· We could however add support for it via an org.restlet.ext.inject extension
· In addition to passing shared objects via Context.attributes, it is also possible to directly access the parent Application and its custom properties via ServerResource#getApplication(). It should be overridden to return the proper instance of your Application subclass
Regarding the ServerResource pattern, I would say that the main reason is that there can be any number of URIs/resources in a REST system, at no design cost. Think about a REST API exposing billions of GPS points as resources with separate URIs. It is obvious that you canât pre-instantiate all of them like a graph of objects.
Therefore, you need a way to manage groups/classes of resources and the easiest criteria is to use URI path templates including variable parts and query parameters. Then, you need a way to create/discard resource instances on a per call basis.
Note that for resource classes limited in number of instances, it is in theory possible to preinstantiate them. This is the purpose of the Finder class and its find(Request,Response):ServerResource method, to reuse existing ServerResource instances. However, as ServerResource acts as a wrapper of a Request/Response couple, it doesnât work in practiceâŠ
Best regards,
Jerome
--
Restlet ~ Founder and Technical Lead ~ <http://www.restlet.org/> http://www.restlet.oârg
Noelios Technologies ~ <http://www.noelios.com/> http://www.noelios.com
De : tpeierls-***@public.gmane.org [mailto:tpeierls-***@public.gmane.org] De la part de Tim Peierls
Envoyé : samedi 22 janvier 2011 20:41
à : code-s0N/mLB9wL+***@public.gmane.org
Objet : Re: Unnecessary type check?
We're way off topic, so I'll respond privately when I get a chance. (Which might not be for quite a while -- things got busy here.)
--tim
On Sat, Jan 22, 2011 at 2:35 PM, Tal Liron <tal.liron-KRbUgwfp4vGAiRqxG6I7vAC/***@public.gmane.org> wrote:
I see your point about DillFinder potentially growing too big, but
actually the code you wrote was pretty much good enough for me. If I
have to manually inject the Context with what I need, so be it. I have
no expectation for Restlet helping me with that.
Restlet has numerous curious architectural pattern decisions: the
factory pattern for resources, and the lack of interfaces, that may have
the advantage of encouraging certain programming styles. To me, this has
always felt like a mistake: encouragement is not enforcement, definitely
not at a code level. It ends up feeling heavy handed, rather than free.
In its place, I would hope for more real-world examples showing how to
manage complex state in Restlet. These would create best practices,
rather than a awkward API (which I consider the Finder to be).
Moreover -- I think you're overstating (pun!) the state issue. I do not
think it's correct to talk about a resource being "stateless" (in any
case, encapsulation is just one of three OO principles, such that a
Resource with state is still not quite an "object"). Remember that
Fielding's dissertation called the chapter Representaional State
Transfer, and not "a resource-oriented architecture". Look at the name:
it's not "representations of state," but "representational state." It's
definitely state. The point that Fielding tried to emphasize in many
different ways was that the web was exactly *not* uselessly stateless,
as so many programmers thought, but that in fact it is exactly stateful
in a specific matter that is useful. In other words, the web is *an*
application -- instead of building applications "on top" of it, we
should be treating it as the application layer itself, and work within
its state semantics, which he then elaborated.
The qualifier -- "representational" state -- as opposed to, say,
"actual" state, means that the expectations of human and other users of
the web-as-an-application is that they access (see, alter) the state
subjectively, within terms and conditions true to capabilities,
location, time, etc., of the moment. Thus a browser loads a page -- and
all web users know they can refresh the page and possibly see a new
representation of the application's state. Or not: caching of
representation becomes possible (a crucial REST concept not yet handled
by Restlet) as does massive scalability. Point is that this expectation
allows for representations to be manipulated along the way (the
"transfer") as pure things-in-themselves with no strings attached. (This
is somewhat analogous to the notion of pure functions in functional
programming.) In other words, it's not resources that are stateless, but
representations.
Your Restlet resources definitely have state, except that you might have
decided not to store them in ServerResource instances, and instead in an
application context. But that's your own architectural decision, albeit
one that is somewhat encouraged by Restlet right now. But I don't see
anything immediately wrong with storing state in a ServerResource, if
that's where it logically belongs. For example, it might store a
connection pool to a database it uses. If only one URI pattern uses that
pool, why have to put it elsewhere? This is actually a common design for
URI spaces. And if the same resource class is re-used for numerous URIs,
each instance can hold its own relevant state. That's good OOP
re-usability that has nothing to do with REST per se.
I'll have to disagree with your take on functional programming in the
Guava list, too. Clojure's transactional memory (via Refs) is an
innovative and simple way to share state without bringing "the whole
multiprocessor down." Persistent data structures can be used outside of
functional programming (indeed, Clojure's all implement the standard JVM
interfaces) but are so much more elegantly expressed (purely)
functionally. And that's the difference I was trying to point out
earlier: Clojure *does* enforce coding patterns at the code level, in a
way Restlet (as a Java library) never can -- Refs can only be used in a
transaction, otherwise an exception is thrown. Functional programming is
already the best practice for using persistent data structures.
Moreover, I see the mechanism of transactional memory fitting quite well
with the architectural notion of representational state. Per transaction
(user request) you can get a snapshot of very complex state.
In summary, I was quite serious about the potential of a Clojure rewrite
of Restlet. Not so serious about actually doing it, though...
-Tal
Post by Tim PeierlsNo one wants to see the Restlet core take on too much. Trouble with
DillFinder, light as it is, is that it falls short of implementing the
official standard, JSR 330. That might not bother you, but it would
complicate the story: "You can use built-in DI lite, which has the
following idiosyncrasies, or you can use full JSR 330 without the
idiosyncrasies but with an extension jar." What's more, the next
person is going to ask for Provider support in the core, and recursive
injection of objects, and scope, and ... By the time you've added
all that in to Restlet, you've got what amounts to small DI framework
that is now too big to fit comfortably in core Restlet. I don't see
clear criteria for drawing the line anywhere but the extremes, which
is why I proposed that the core have only enough configurability to
*support *JSR-330-compliant extensions, without trying to do any DI,
even poor man's DI, on its own.
Hmm, just noticed a simple framework for injection that claims to be
http://code.google.com/p/java-simple-utils/wiki/Injection
Is 70K within the threshold of acceptability?
Regarding the use of the factory pattern for resources: I can't speak
for Noelios, but I imagine the original motivation was not so much to
reduce concurrency issues, but to accurately reflect the distinction
between stateful/OOP/Restlet to stateless/ROP/resource. I think it was
a brilliant design choice. It forced me to confront the fact that
resource != object and that resource is more closely analogous to
class. The only thing missing was a way to let my resources
participate in DI, and now I have that, too -- although I'm hoping it
can be made a bit prettier and more consistent.
It's true that the factory pattern doesn't solve the problem of
sharing data safely between resource handlers, but at least it
clarifies where the problem is. The resource instance is ephemeral so
you *must* get all your state from somewhere else. If it wasn't, it
would be all too tempting to try to associate state with the resource,
and before you know it, whoops, you've essentially got servlets and
sessions.
And I know you weren't being entirely serious about Clojure, but I
don't think we need to look for help from other languages. Clojure /
functional programming doesn't eliminate concurrency issues any more
than garbage collection eliminates memory leaks. There are plenty of
great tools in Java to help manage the concurrency burden. Guava
MapMaker is a terrific example. (Incidentally, I recently ranted
<http://groups.google.com/group/guava-discuss/browse_thread/thread/278dd6a99214d91a/92f14996f69b179d?hl=en%5Cf14996f69b179d# <http://groups.google.com/group/guava-discuss/browse_thread/thread/278dd6a99214d91a/92f14996f69b179d?hl=en%5Cf14996f69b179d> >
about the fallacy of FP solving all concurrency problems on the Guava
discussion list.)
--tim
On Fri, Jan 21, 2011 at 6:39 PM, Tal Liron
Dill is not my favorite herb, but it does add some flavor. :)
Your implementation of DillFinder is fine! What I was trying to suggest
is to merge such code or something very similar into Restlet's core
Finder and perhaps solve 90% of people's DI needs when it comes to
ServerResource. And it's such lighweight and little code, isn't it?
I know 700k isn't enormous, but it's always the struggle between the
desire to keep things minimal and to add dependencies. It's not just
700k: it's adhering to the library's patterns and integrating them into
your library's culture, it's keeping up with progress there, etc. The
cost of dependency is uncertainty and headaches and lock-ins. Jerome's
decision to keep support for all these dependencies as extensions is
important. I know that for you and many others Guice is worth all those
costs, but I would be upset if using Restlet would *practically* require
some kind of external DI library for it to run properly.
Sigh. I guess even more than having DillFinder-like functionality, I
would appreciate an alternative to the Finder/ServerResource system
entirely. I know that the idea of "one ServerResource instance per
request" has certain advantages in terms of ease of handling state, and
also in terms of performance, but it's always been a headache in terms
of initialization. And it's difficult to program -- a kick in the head
for how most of us understand OOP and its relationship to Java. When it
comes down to it, you still have to do the hard concurrency programming
to handle shared state between resources (which is part of what
"initialization" via the Context does). So, I wonder how much we've
really made things easier.
In my opinion, it would be most comfortable if there were a
ResourceRestlet class inheriting from Restlet, such that you would be
router.attach("/customer/{id}/", new MyResource(arg1, arg2));
You would have to make sure that MyResource is thread-safe, of course,
but it would much more sense to most programmers, and also flow better
with the rest of the Restlet API. No need for DI (unless you want it, of
course), and definitely no need to integrate DI into the
framework. I'm
sure this approach has been tried and abandoned in Restlet's history.
Perhaps it's worth trying again, now that Restlet is mature and we have
so many concurrency gurus onboard?
Argh. I want to rewrite Restlet in Clojure so we can do away with all
these painful concurrency tradeoffs. Maybe that will be my project for
next year. Who wants to help? Hmm, now where would be a good place to
find experts on JVM concurrency... ;)
-Tal
Post by Tim PeierlsTal,
How about this?
public class DillFinder extends Finder {
public DillFinder(Context context, Class<? extends
ServerResource>
Post by Tim PeierlstargetClass) {
super(context, targetClass);
}
@Override public ServerResource create(
Class<? extends ServerResource> targetClass,
Request request,
Response response)
{
for (Constructor ctor : targetClass.getConstructors()) {
if (ctor.getAnnotation(Inject.class) != null) {
@SuppressWarnings("unchecked")
Constructor<? extends ServerResource> c =
(Constructor<? extends ServerResource>) ctor;
return create(c);
}
}
// No constructor with Inject found, use default.
return super.create(targetClass, request, response);
}
private ServerResource create(Constructor<? extends
ServerResource> ctor) {
try {
Map<String, Object> attrs =
getContext().getAttributes();
Post by Tim PeierlsClass<?>[] paramTypes = ctor.getParameterTypes();
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; ++i) {
Class<?> paramType = paramTypes[i];
String paramTypeName = paramType.getName();
Object paramValue = attrs.get(paramTypeName);
if (paramType.isInstance(paramValue)) {
params[i] = paramValue;
}
}
return ctor.newInstance(params);
} catch (InstantiationException ex) {
throw new ResourceException(ex);
} catch (IllegalAccessException ex) {
throw new ResourceException(ex);
} catch (InvocationTargetException ex) {
throw new ResourceException(ex);
}
}
}
Dill is an acronym: *D*epencency *I*njection *L*ite al *L*iron. :-)
But Guice is <700K, so if you don't mind adding that jar and you're
willing to try the incubator's Guice extension, you can get this
behavior without the limits of DillFinder (and it is pretty limited)
and without having to buy any further into full-fledged DI. It would
FinderFactory finderFactory = new RestletGuice.Module() {
@Provides CustomerProcessor provideCustomerProcessor(Context
context) {
return (CustomerProcessor) context.getAttributes()
.get("org.happy.CustomerResource.processor");
}
@Provides LogManager provideLogManager(Context context) {
return (LogManager) context.getAttributes()
.get("org.happy.CustomerResource.log");
}
}
Your customer resource's constructor would be annotated with
@Inject.
Post by Tim PeierlsFor the moment, you can use the Guice extension in its current
router.attach("/customer/{id}/",
finderFactory.finder(CustomerResource.class));
where finderFactory would need to be passed in somehow. If the
enhancements I've proposed (including the improvement suggested by
router.attach("/customer/{id}/", CustomerResource.class);
--tim
On Fri, Jan 21, 2011 at 2:03 PM, Tal Liron
Echoing Jerome's question about the Context --
A very common use case for me is to inject data right from the Context.
In fact, it was Tim who, more than a year ago, promised me
that it was
Post by Tim Peierlsthe "Restlet way" to initialize resource instances. I guess
this was
Post by Tim Peierlsbefore he started making babies with Guice. ;)
So, I'm wondering if Restlet could provide a standard
mechanism for
Post by Tim Peierlsinjection from the Context, without reliance on external DI
tools, and
Post by Tim Peierlswith no integration beyond the "inelegant" extensions as they
exist now.
package org.happy;
class CustomerResource extends ServerResource {
@InjectFromContext
public CustomerResource(CustomerProcessor processor,
LogManager
Post by Tim Peierlslog) {
...
}
}
Where the Context attributes will be expected to be named
"org.happy.CustomerResource.processor" and
"org.happy.CustomerResource.log" and to be in the types
specified.
Post by Tim PeierlsThat's "DI lite", but very much in tune with the "Restlet way."
-Tal
On Fri, Jan 21, 2011 at 2:48 AM, Jerome Louvel
My main remark is that using Guice or Spring or the default
mechanism or something else shouldnât be an engine wide
choice.
Post by Tim PeierlsEach application contained in a component should be able to
use a
different mechanism. It should also be possible to use DI to
configure the component itself.
component 1 : Guice DI
+-- application A : Spring DI
+-- application B : Default behavior
+-- âŠ
Remember that DI is *not* analogous to the concept of "service".
It's
a global aspect, much like the choice of Engine. You should
never have
to say things like "that's a Spring app" or "that's a Guice
app". That's the motivation for JSR 330: It lets you specify
injection
points in a standard way without committing to a particular DI
framework. In your example, the application A author might have
developed the application using Spring, but by expressing
dependencies
entirely with JSR 330 annotations, the application can be used
without
change in a Guice environment.
Of course if there are internal dependencies that rely on
framework-specific bindings they can be encapsulated in one
place and
exposed in a way that users can invoke from other DI
frameworks. For
Post by Tim Peierlsexample, Guice can import bindings from Spring through the
http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/spring/SpringIntegration.html
Post by Tim PeierlsThere's probably something similar to adapt a Guice injector as a
Spring bean factory.
Iâm wondering if the Context shouldnât be the place to
implement
Post by Tim Peierlsone of the solutions you evoked.
The trouble is that context can be overridden down the Restlet
tree,
Post by Tim Peierlsand that could end up unintentionally orphaning a whole
subtree from
Post by Tim Peierlsthe global DI.
But there might be cases where an Application writer would want
to be
able to override the use of global DI, and Context could be used
there. For example (assuming approach #1 of my earlier e-mail),
if you
added a finderCreator property to Context, defaulting to null, the
1. Check to see if the given finderClass is non-null (and
not ==
Post by Tim PeierlsFinder.class). If so, use it.
2. Check to see if the given context has a non-null
finderCreator
Post by Tim Peierlsproperty. If so, use it.
3. Check to see if the Engine has a registered
finderCreator. If
Post by Tim Peierlsso, use it.
4. Otherwise, use new Finder(...).
Case 4 is when no one has done anything special; it's what we have
today. Applications written to use DI won't work in this case.
Case 3 is when the component or embedding environment wants to
support
applications that require DI (using JSR 330 annotations). The
engine
Post by Tim Peierlsis told, in effect: The system is using such-and-such Spring
application context (or such-and-such Guice injector).
Case 2 is for an application that explicitly disclaims an
interest in
the global DI and wants to handle things separately, possibly by
using
its own private DI mechanism throughout the application.
And case 1 is for overriding finder creation for a Router (or
Application) instance without affecting finder creation for other
Restlets within the same Application.
--tim
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699698> &dsMessageId=2699698
<http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699698> &dsMessageId=2699698>
<http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699698> &dsMessageId=2699698
<http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699698> &dsMessageId=2699698>>
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699729> &dsMessageId=2699729
<http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699729> &dsMessageId=2699729>
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458 <http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2699839> &dsMessageId=2699839
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=7458&dsMessageId=2806031