Oleksiy Stashok wrote:
Hello Ken,
Context... What would be the
BEST to describe an attribute in a
collection, in the context of grizzly's Context, SelectorHandler,
Controller? does it really a String type? Actually, the way Grizzly
using the *term* attribute is not even appropriate. Why? attribute is
something that describes a specific distinguishable feature of the
particular object (or instance). The way grizzly uses here is, to
store SOME objects and their references, in case it needs at various
scopes. This forces users to "create" their own strings?keys (to
accommodate in) to retrieve the references.
Right. Might not be perfect, but a lot of API use that approach:
http://java.sun.com/j2se/1.4.2/docs/api/javax/net/ssl/SSLSession.html
http://java.sun.com/javaee/5/docs/api/javax/servlet/ServletContext.html
And all of them suffer a string hash lookup on every attribute access.
That is a String hash computation
and comparison, and these are expensive operations to perform (possibly
multiple times) every time we
read bytes from a channel.
Hashcode computation happens just once for String, though comparison
could be expensive.
The design tradeoff here is that there should
only be a small number of unique attributes in the system, so that the
AttributedObject arraylists are not too large (say 10-30 attributes,
not hundreds). Many other designs are possible,
depending on exactly how a Map<Integer,Object> is implemented,
with the assumption that the keys are small contiguously
allocated non-negative integers. For example, segmented maps could be
used, but then the lookup cost is higher.
Saving space costs time in this case.
I like your implementation! Agree, we can have several
"AttributeObjectBase"-like implementation, one you have in Corba with
ArrayLists, another with Maps for situation, where we can have more
attributes.
I currently use around 20 Attributes in codegen, which is probably more
than a Grizzly client
would typically need. For me, the ArrayList approach works fine. But
codegen has an additional
complication: any Node may be copied, and the copy and the source must
share the current
values, but any new Attribute set calls must result in independent
values for the source and
the copy. This results in a fairly complex implementation of
AttributedNodeBase, which is probably
not what you want for Grizzly. Grizzly's implementation of
AttributedObject can be very simple:
just delegate everything to the appropriate implementation of a
Map<Integer,Object>.
A more complex situation could occur, where a module uses several
different contiguously created
attributes. So one module might use attrs 0-4, another 5-7, another
8-12, etc. This would point to a
Map<Integer,Object> implementation that uses an ArrayList plus an
offset. Chains of such things
could also be used, but that would only make sense if the dynamic
attribute implementation was
widely used with many attributes.
There is another consideration: we want to keep the range of Attribute
indices as small as possible, so
Grizzly attributes on the SelectionKey attachment should have nothing
to do with other sorts of
Attributes used elsewhere in the system (or in other parts of Grizzly
for that matter). To do that,
we need something like AttibuteBuilder which creates an
AttributeFactory which creates Attributes
instead of using the constructor of Attribute. I would not expose
AttributeBuilder to the Grizzly client.
The Grizzly client would just do something like
SomeGrizzlyClass.makeAttribute( ... )
with makeAttribute taking the same arguments as the current
constructors.
Of course, we could also simply copy the code and rename the packages
rather
than sharing the implementation. In fact, the exposed interface should
be like this,
so that we are free to use any implementation at all behind the scenes.
Not sure we
need to store type in Attribute? Which role does it play?
The original intent was to use cls to cast the object stored in the
AttributedObject to the
correct type. This of course does not work for things like
Attribute<List<String>>,
but then I ended up always using a "typedef" for things like that, e.g.
public interface Foo extends List<String> {}
so that Foo.class is really a Class<Foo> which is as good as
(though not at all equivalent to!)
Class<List<String>> for most purposes.
There is currently a confusion here that can be resolved in one of two
ways:
- Remove cls from the Attribute constructor
- Change Attribute.get to use cls.cast instead of (T)
While I don't see a strong need for approach 2, I do use the classname
in the
toString method. Consequently I am changing the implementation to use
approach 2.
Grizzly could of course choose a slightly different approach, such as
only using the attribute
name in the toString method.
Ken.