Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Archived » Eclipse Communications Framework (ECF) » A Critique of the Presence API
A Critique of the Presence API [message #581469] Sun, 29 May 2005 21:26 Go to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

At Scott's invitation, I will be providing a critique of the
org.eclipse.ecf.presence interfaces. In this posting I will explain why
I am offering this critique. In four subsequent, child postings I will
discuss the following:

1) My understanding of how the presence API works
2) Reflections on the presence API as a service or component interface.
3) Some thoughts about how much the ECF should show through to services
and components.
4) Some comments on how I think we should try to model chat.

Li-Te Cheng has provided invaluable assistance in reviewing these
postings and helping me understand how things work.

I am undertaking this effort because I believe that higher level APIs
are essential to the fulfillment of the ECF mission. The Base ECF API
itself is probably both too general and too low-level to provide easy to
use virtualizations of standard collaboration services. To achieve this
we will need higher-level APIs that particularize the ECF to one or
another use. The presence API is the first example of such a
higher-level API.

Over time I believe the ECF will become a family of APIs supporting the
work of three types of developers. Protocol-level development is the
first type. The Base ECF API is a relatively low-level API that can be
used in a wide variety of communication circumstances. Protocol-level
developers will implement providers that map their capabilities onto ECF
constructs, thereby hiding the protocol and easing the job of those who
wish to use their protocol. Insofar as the ECF provides the necessary
features, they will use them, but they will also create extensions as
needed to support any protocol-specific capabilities. The difficult
challenge of the Base ECF API design is to provide a common base design
that captures many of the capabilities of a wide variety of protocols
without leaning too much in the direction of any one.

Service or component development is the next type. I have not found one
word that characterizes this level of development. If, as in the
Presence API, an API is trying to encapsulate an entire use of the ECF
from login to logoff, it seems more appropriate to think of it as a
service. If an API is trying to encapsulate some tool, like a chat,
that can be added to a service, then we might prefer to think of this
API as a component rather than a service. In either case, the
development job is more like tools development. There is a high-level
API that characterizes how the service or component will manifest itself
and the tool developer must map the low-level ECF capabilities onto the
high-level service or component API. Notice, that insofar as the
service/component/tool can avoid provider-specific extensions of the
Base ECF API, then these tool implementations will be reusable for
multiple providers. Even if the tools are unable to achieve
provider-neutrality in their implementations, we still have the
opportunity to achieve provider-neutrality at the application level if
the tools implement a standard higher-level API.

Application development is the third type. Application developers will
use the high-level service or component APIs. Whereas the Base ECF API
is an enabling API, I believe the higher-level APIs should focus on ease
of use. They should aim for an 80/20 rule approach in their design.
(80% of the benefit, for 20% of the effort.) In other words, they
should not try to do everything. They should aim for the common
denominator of a capability and avoid the tendency to encapsulate the
superset of all features offered by anyone. One goal is to make it easy
for application authors to do the familiar things. Another is to
achieve an effective virtualization of a capability that is often
implemented in different ways for different protocols.

Given this view, the Presence API and other higher-level service and
component APIs defined by the ECF are of enormous importance. They will
become the programming model for application developers who are not
interested in the details, but want to make sure that their applications
are robust over alternate providers. To the greatest degree possible we
want to encourage only one model for any particular use, so that
appllcations can be maximally leveraged. In many cases, I believe we
will find that supporting virtualization at the Base ECF level is too
difficult because we simply do not know enough about the uses for which
we are designing. The service and component APIs might prove more
fertile ground for some virtualizations precisely because more of the
uncertainity about their use will have been settled. Indeed, it seems
likely that we will only suggest higher-level APIs for those services
and components for which a common protocol-neutral approach seems
achievable..

What we need is some principles for how to design these higher-level
APIs. I think we know that they need to be easy to use and
protocol-neutral, but what does this mean in practice. How would we
know a better or worse API if we saw it? What are the patterns or
elements of design that we will use to judge quality or at least ensure
design coherence across multiple high-level APIs? Do we have a shared
view of what these APIs will look like?

By critiquing the Presence API, I hope to reveal some of my own opinions
(even to myself) and initiate the discussion about the design of these
high-level APIs. My critique is not intended as a slam on anyone's hard
effort or personal preferences. It's simply easier to react than to
start from scratch and the Presence API is sitting there as a first
opportunity for reaction. My goal is to start a conversation about what
these higher-level APIs are all about and how they should be designed.
I hope you'll come along for the ride:-)
My understanding of how the Presence API works [message #581510 is a reply to message #581469] Sun, 29 May 2005 21:28 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

In this posting I want to simply spell out my understanding of how the
Presence API works without offering any critical comments. It's
possible that some aspects of my critique will be incorrect, simply
because I misunderstood the API. This is our chance to catch that.

In the sections below I will try to achieve two things:
1) Explain the model offered by the Presence API found in
org.eclipse.ecf.presence.
2) Pose a few questions about things I am not clear on.

*** The Presence API Model ***

I will confess I was struggling with the Presence API until Li-Te Cheng
sent me off to http://www.jivesoftware.org/smack/ to check out the Smack
API for XMPP. Clearly the Presence API is very close to a one-to-one
mapping of Smack constructs onto Presence API concepts. The Presence
API uses interface definitions where Smack uses classes, but even the
naming is almost identical. My understanding of the Presence API
derives from the documentation at the jivesoftware site and diving down
into the ECF implementation.

The best place to start understanding the Presence API model is with
IPresenceContainer. This is the entry point into the Presence service
encapsulated by the Presence API. Here's the signature:

public interface IPresenceContainer {
public void addSubscribeListener(ISubscribeListener listener);
public void addPresenceListener(IPresenceListener listener);
public void addMessageListener(IMessageListener listener);

public void
addSharedObjectMessageListener(ISharedObjectMessageListener listener);

public IMessageSender getMessageSender();
public IPresenceSender getPresenceSender();
public IAccountManager getAccountManager();

public ISharedObjectContainer makeSharedObjectContainer(Class []
types, Object [] args) throws SharedObjectContainerInstantiationException;
}

We have ways to add listeners (including an ECF-specific
SharedObjectMessage listener), ways to get control objects for
initiating actions, and an ECF-specific extension mechanism. My
impression is that the addSharedObjectMessageListener and
makeSharedObjectContainer methods are not essential to the Presence
service, so I will not say much more about them.

If we dig into the listeners and control objects we can identify six
types of operations that are enabled:
1) activation/deactivation of the service,
2) managing accounts,
3) managing the roster,
4) subscribing for presence,
5) changing presence information, and
6) sending messages (engaging in chats.)

* Activation and deactivation of the service *

This is initiated outside the Presence API on the associated
ISharedObjectContainer. Within the Presence API these actions give rise
to events on the IPresenceListener.

* Account Management *

I confess this one was throwing me until I visited the jivesoftware
site. I thought it was about logging in and managing my account, but it
is only partially about my account. Mostly, it is an administrative
interface for creating and deleting other people's accounts. The one
method that is about managing my account is the changePassword method.
Given that there is no listener for the consequences of these actions, I
assume they are implemented synchronously and will hang until a server
responds.

* Managing the Roster *

The basic notion is that the PresenceService manifests itself through a
roster (buddy list) and each person found in that roster is represented
by an IRosterEntry. The presence service is prepared to maintain your
roster. I am not certain how one gets the roster upon logging in. In
Smack there is a getRoster method, but I do not see that in the Presence
API. It appears that I receive a sequence of handlePresence events to
provide my previously stored subscriptions and that this is kicked off
automatically up on login.

There is a way to add/drop roster entries using the sendRosterAdd and
sendRosterRemove methods of the IPresenceSender. These are puzzling
methods. They seem to initiate the subscribe/unsubscribe process, but
they accept information (like my preferred nickname for a user) that is
not needed to finalize a subscription. I originally assumed that
completion of these requests is signaled via the handleRosterEntry and
handleSetRosterEntry methods of the IPresenceListener, but now I believe
that the subscription process is completed using a handlePresence
event. Frankly, I do not understand the distinction between the
handleRosterEntry and handleSetRosterEntry methods or how they are used
in the service. I am beginning to believe that IRosterEntry objects are
really just a local capability and all communication about presence
across machines is achieved using the IPresence object.

A slight wrinkle on the roster management is that the roster entries can
be in groups represented by the IRosterGroup interface. One thing that
confuses me a little is that entries may be added to groups and groups
may be added to entries. Smack follows the more usual pattern that the
containing object, RosterGroup, can have the contained object,
RosterEntry, added to it, but not vice versa. Smack allows a
RosterEntry's groups to be obtained via the RosterEntry but not
assigned. I am a little inclined to think that this is an error in the
PresenceAPI, but, in any case, it does not seem to be an essential
feature of the Presence Service.

* Subscribing for Presence *

To learn about someone else's presence, we must first subscribe for it
Since we want to ensure that the other person may deny our desire to
know about them, there is a mildly complex protocol for obtaining that
permission. One of the things that threw me for a little while was the
IPresence object. At first, I thought of it the way I am now thinking
about the IRosterEntry -- as the proxy for a person. This is a
mistake. Despite the name, the IPresence object should be thought of as
a message that is passed around to carry on a conversation about one's
presence. It first shows up in the negotiation for permission to
receive presence information. A flag on the IPresence object indicates
whether it is being used to request a subscription, authorize a
subscritpion, unsubscribe, or actually convey presence information.
This is the secret to understanding the subscription process.

The steps to the subscription process seem to be as follows:
1) The requesting client calls addRosterEntry on the IPresenceSender
with indications about who to add.
2) The authorizing client receives a handleSubscribeRequest event on
the ISubscribeListener with the IPresence object flagged as SUBSCRIBE.
3) To authorize a subscription, the authorizing client calls
sendPresenceUpdate on the IPresenceSender with the IPresence object
flagged as SUBSCRIBED.
4) To deny authorization the authorizing client can call
sendPresenceUpdate with the IPresence flagged as UNAVAILABLE.
5) The requesting client receives a handleSubscribed event on its
ISubscribeListener with the IPresence object flagged to indicate the
outcome.

There is a similar process for carrying on an unsubscribe request,
though it seems doubtful that the authorizing client can deny a request
to unsubscribe.

* Changing Presence Information *

To change one's presence information it is only necessary to call the
sendPresenceUpdate with an appropriate IPresence object. In this case,
I believe it is flagged as AVAILABLE and contains the user's status
information. The service takes care of multicasting this out to the
interested subscribers. They recieve the information in the form of a
handlePresence event on the IPresenceListener.

* Sending Messages *

Sending a message to someone is simply a matter of invoking the
sendMessage method of the IMessageSender. The recipients' address, a
message type, and the message String must be provided. These are
received by the recipient as a handleMessage event on the
IMessageListener. To send to a group of recipeints, a chat room address
is created and the type flags the message as a GROUP_CHAT. In Smack
there is a provision for inviting someone to the group chat. I do not
see that in the Presence API. Also, I do not see anything indicating
that one must first obtain presence authorization before sending a message.

*** Some Questions ***

1 -- Please let me know if I got the procedures for roster management
and subscription correct. I found this one of the more difficult areas
to decode. One aspect of the API that still makes me think I got it
wrong is that the InterestType on the IRosterEntry can be set to BOTH,
NONE, FROM, REMOVE, and TO. I understand REMOVE, but I'm not sure what
the rest is about.

2 -- There are message types of NORMAL, CHAT, GROUP_CHAT, HEADLINE, and
ERROR. I think I understand CHAT and GROUP_CHAT, but what are NORMAL
and HEADLINE used for?

3 -- The Mode flag on the IPresence object has a setting for CHAT. I
don't think I understand this. If you are in a chat does everyone else
get informed about this?

4 -- When I was imagining the implementaiton for a Presence Service
using ECF, I assumed that the container would correspond to the whole
service and a shared object would be used for each person's roster
entry. It is not essential to the question of how the PresenceAPI
should be designed, but I was surprised to learn that the XMPP
implementation of the Presence API uses a single shared object to handle
all communication among the service users. I believe this shared object
is assigned the same ID as the container, which is presumably the name
of the XMPP server. Is this an expedient for getting on with the XMPP
implementation or is this how we should think of shared objects? Are
they really separate communication pathways within the container, i.e.,
separate channels? When a protocol does not really support any
multiplexing of channels, will there typically be only one shared object
representing all the communication for that protocol?
Reflections on the presence API as a service or component interface. [message #581681 is a reply to message #581469] Sun, 29 May 2005 21:29 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

In this posting I will try to critique the Presence API as an example of
a high-level ECF API. My goal is to discover principles that we can all
accept for driving the design of these interfaces. To some degree this
is an aesthetic exercise. I will try to give arguments for my opinions,
but I will not try to disguise the fact that they are opinions. In some
cases, I am not even certain that I will have good reasons for my
preferences. So-be-it. I am adopting the attitude that it is better to
get an issue on the table than to worry about whether I can fully defend
my position. You, the ECF community, and the ECF committers will be the
ultimate judges of which opinions are worth noting and which are worth
ignoring.

One final note before I begin. I do not plan to reflect much here on
the this API's approach to modeling chat. I will consider that concerns
in alater posting. In this posting, I am primarily concerned about the
ease of use of the Presence API, since I believe one of the principle
goals of these higher-level APIs is that they should offer easy-to-use
programming models to application developers.

Ok, let's go. I will organize my observations into three sections
characterized by the following headings:
1) Object Models vs. Protocol Models
2) The API as Training manual
3) Should the application developer need to know much about the ECF?

*** Object Models vs Protocol Models ***

My single biggest complaint about the Presence API is that it allows too
many protocol details to leak through the API. I believe that these
higher-level APIs are our opportunity to offer a fuly object-oriented
approach to services and components and that we should not be asking the
application developer to think like a protocol-level programmer.

Let me give two examples of what not to do, drawn from the Presence
API. First, there are many places in the Presence API where shared
entities are identified by an ID. While this might be appropriate at
the ECF-level, I believe we should try to operate in terms of objects in
the Presence API. Thus, for example, instead of calling
sendRosterRemove on the PresenceSender with an ID associated with the
RosterEntry, why can't I just call a remove method on the RosterEntry?
When I send a presence update, why must I give a fromID and a toID? We
know the from ID; it's mine! As for the toID, when I am truly updating
my presence (as opposed to negotiating subscriptions) is it even used?
(Frankly, the current collection of methods all looks like opportunities
to spoof becasue they require a fromID.)

Under most circumstances, an ID will be wrapped by some object (and if
it is worth talking about, it probably should be.) The containerID is
wrapped by the IPresenceContainer so we do not need it. Methods that
need it should either be on the IPresenceContainer or pass in the
IPresenceContainer. User IDs will usually be wrapped by something akin
to the IRosterEntry. Methods that need it will be on these objects or
pass them in. The one exception to this is when we want to refer to an
object that we do not yet have. In these cases, the ID is akin to an
address and must be provided usually for the purpose of obtaining the
object. After that, we will act on the object.

In the Base ECF APIl, IDs are used to ensure flexibility about the
association of runtime objects to the shared abstractions. I believe
that these are obscure cases that higher-level APIs do not need to
support and should not support. The programming model for the Presence
API would be much simpler if IDs were only used when essential (as
addresses for unavailable objects). As it is right now, these IDs will
need to be wrapped by some application-side object when there are
perfectly good constructs within the API for wrapping these IDs.

My second example comes from the use of the IPresence object. If I want
to mark myself as do-not-disturb, I don't go to my IRosterEntry and
select the do-not-distrub method. Instead, I create an IPresence object
and mark it as do-not-disturb. This is klunky, non-intuitive, and
completely surprising to an application developer schooled in the arts
of OO programming. The verb, DoNotDistrub, is hidden as an adjective on
a noun, IPresence. I understand from a protocol-level why this happens,
but there is no reason why we should carry this into our higher-level
APIs. There should be a do-not-distrub or changeStatus method either
directly on the service or on an object representing my RosterEntry that
I can directly invoke. Yes, the mapping wil be a little tougher for the
service or component implementer, but the resulting model will make much
more sense to the application developer.

I guess my first principle of higher-level API design is that these APIs
should be object-oriented, not protocol-oriented. If we lose some
capability along the way, so-be-it. I doubt we will lose anything
really critical. Frankly, if we want these APIs to be protocol-neutral,
we will need to avoid letting protocol-isms into the APIs. Every time
we do it, we are probably leaning towards one protocol and away from
another. Object models might just be our best shot at a representation
that is truly neutral with regards to alternative protocols.

*** The API as Training Manual ***

When I want to understand what a system can do, I read the API.
Assuming I can make sense out of it, this is the most precise expression
of exactly what I can and cannot do. For me, the API is my training
manual on the capabilities of a system. I want those training manuals
to be well-designed, use clear terms, and factor things in sensible
ways. Of course, what I consider sensible is not the same as what
others consider sensible, but we should try.

Let's look at some of the aspects of the Presence API that could bear
improvement. First, there is the issue of naming. Just because the ECF
uses the name container, we have no obligation to use that name for the
top level of the Presence API. For my money, we would be better off
calling the IPresenceContainer, the IPresenceService. Admittedly, the
term service is not enormously more precise than container, but I think
it is somewhat better. In the ECF there will be many forms of
containers some of which will feel like services and some of which will
feel like components, e.g., an online meeting. I am inclined to use the
term service for those cases when the container encapsulates a
capability from login to logoff.

Second, why are there so many interfaces in the Presence API? Putting
aside the listeners, it seems like there only need to be three main
interfaces: IPresenceService, IRosterEntry, and IChat (which isn't
there.) If we throw in IRosterGroup, we have four. As mentioned above,
IPresence is primarily a messaging format that should be expanded into
more methods on the primary objects. IPresenceSender, IMessageSender,
and IAccountManager do not seem necessary. Their methods could safely
be moved onto the IPresenceService without loss of clarity.

I am amenable to this form of additional control objects if it serves
some purpose, but I don't see the purpose in the Presence API. It can't
be to reduce the method count to something tractable. altogether they
add 11 methods, seven of which come from the AccountManager. So leave
the account manager separate and bring in the others. It will make it
easier to see all the service level methods in one place. Moreover,
pulling out IPresenceSender and IMessageSender as distinguished seems
like a continuing nod to the protocol-level design.

Finally, one quick comment. When I argued with Li-Te Cheng for
flattening the IAccountManager into the IPresenceContainer, he suggested
that one reason to keep it separate might be to be able to return null
if you lack permission to manage accounts. This makes sense, so I
relented in this case, only to notice later that the IAccountManager
contains both administrator functions and the changePassword method that
everyone will need to access. Even if Li-Te is right, we need to get
the changePassword method out of the IAccountManager and onto the
IPresenceContainer.

I guess my second design principle is to avoid allowing the base ECF API
or other models. like Smack lead us astray. For higher-level APIs, we
still need good OO design that uses clear and precise language, avoids
excessive interfaces, and factors things sensibly.

*** Should the application developer need to know the ECF? ***

Why should I need to know a complex, general purpose API, like ECF, to
use a specialized, simple API, like the Presence API? I don't think I
should have to know the base ECF API to use its higher-level APIs. This
has several implications for the Presence API. I'll discuss only one
here and expand on this theme further in my next posting.

I question whether it is advisable to expose ECF functionality through
the Presence API. This shows up in the form of the
makeSharedObjectContainer and addSharedObjectListener methods on the
IPresenceContainer. These methods do not seem to be essential to the
Presence Service and could be accomplished by a knowledgable developer
by working directly with the base ECF API. For any newbie application
developer exploring the Presence API, however, they pose a problem.
They draw the application developer into the base ECF API for fear that
it is critical to the operation of the Presence API. This simply leads
them astray. By keeping unnecessary ECF features out of the Presence
API, we can help the application developer keep focused on what the
presence model really is.

So, this is my third design principle: keep unnecessary ECF elements out
of the higher-level APIs.
Some thoughts about how much the ECF should show through to services and components. [message #581720 is a reply to message #581469] Sun, 29 May 2005 21:31 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

There must be some law of nature that says you cannot simultaneously be
general purpose and easy to use. If we want to make the ECF serve all
the purposes that have been idenitified, then it will be abstract. I
don't think it can be otherwise. Abstractions are not easy to use,
however. Concrete things that relate directly to one's immediate
purpose and have an obvious relationship to UI elements in similar
applications are much easier. This is why we want the higher-level APIs
accompanying the ECF.

But the value of the higher-level API is diminished if I must know the
base ECF API in order to use the higher-level API. In the Presence API,
I must learn about an ISharedObjectContainer to activate, get, and
deactivate the Presence Service. This probably seems pretty minimal,
but how do I know that I don't also need to:
1) get the ISharedObjectContainerConfig,
2) get the ISharedObjectManager, or
3) use the getGroupMemberIDs method to find out who is online.
Not only am I drawn into learning a number of complex things that I do
not need to know simply to do a Presence application, but I also have
access to capabilities that must be used correctly or they could break
the Presence implementation. It is for this reason that I have been
tryiing to figure out how to offer the higher-level APIs without
requiring one to know the base ECF API.

I will describe my thoughts in the following five parts, which roughly
correspond to the mental journey that I have gone through:
1) One Factory, No Service Container
2) Two Factories, No Service Container
3) Two Factories, A Service Container
4) One Factory, A Service Container
5) What do I prefer?

*** One Factory, No Service Container ***

My first thought about how to proceed was to simply eliminate the
"middle man." Why not expand the capabilities of the
SharedObjectContainerFactory to support launching something like the
IPresenceContainer directly. Now, all the application developer needs
to learn about is the SharedObjectContainerFactory (perhaps renamed to
something simple, like ECFFactory or, even simpler, ECF.) There would
be no need to study the ISharedObjectContainer and none "in hand" at
run-time to mess around with. A simple method like "getService" could
be used to hide container creation and getting the appropriate adapter.

To do this, the Presence API would need to be modified, since it is not
complete. The methods for login and logoff would need to be added.
This seems reasonably straightforward, however..

In the end, I achieve my objective. Application developers are not
drawn into learning a lot more than they need to know and they do not
have access to the underpinnings of the Presence Service to use
inappropriately.

*** Two Factories, No Service Container ***

I was pretty happy with the conclusion above until Li-Te Cheng suggested
that we create a new singleton for service creation rather than extend
the SharedObjectContainerFactory. I think this was partly an aesthetic
judgment about the desirability of blending the two levels of
abstraction. In addition, it is a way to ensure that we expose only
those things we really want to expose.

In this view of the world, there are two factories. The
SharedObjectContainerFactory stays the way it is and another
ServiceFactory is created. New services would plug into this factory
and be obtained by calling something like getService that would hide
creating the container and getting the adapter. As above, the Presence
Service would need to have its own login and logoff capabilities.

*** Two Factories, A Service Container ***

It was at this point that I realized why Scott designed the Presence API
the way that he did. There might be other reasons, but one critical one
is that he wants to ensure that one ISharedObjectContainer can support
several higher-level services without requiring a login for each
service. The approaches above associate the login with the service.
Multiple services off the same ISharedObjectConatiner will end up
logging in multiply, which is probably unencessary, if not incorrect.
Undoubtedly, there are ways to put the login into the service and still
avoid multiple logins, but Scott's approach is simpler. You get an
object that proxies for the service provider; you log on to that
provider; and then you request each service that you desire.

So how do I retrieve my ECF-hiding and incorporate Scott's pattern? The
answer is to recreate the machinery that we find in the base ECF API,
but in a manner that is appropriate to the higher-level service APIs.

First, we create the analog of the SharedObjectContainerFactory, let's
call it the ServiceContainerFactory and its job is to create
IServiceContainers. IServiceContainers are the analog of
ISharedObjectContainers, but they are not as rich. The only methods
that must be there are login and logoff methods as well as a getService
method. The goal is to isolate application developers from the innards,
so we don't want a lot of additional methods. It might make sense to
have ways to determine which services are active or which are
available. This is the IServiceContainer after all, so we could use it
to help keep track of services.

Notice that I used the term getService, but I expect that it will follow
the getAdapter pattern -- pass in the interface needed, cast the return
value to it, and check for null, just in case. I don't see why we can't
use the pattern, but change the name to what we think will really be
retrieved. It is less abstract, but it will make it easier for the
application developer to understand how he gets what he wants. It's
probably not necessary, but we might also modify the pattern slightly to
stipulate that the getService method will return an IService rather than
an Object. Then we could define an IService method with the minimal set
of methods that we would expect any service to implement. This would
not be a lot, since we don't want to impose many requirements on service
authors. The real purpose of IService is to create a type for
polymorphism purposes. All services would extend this interface.

The Presence API is now modified so that the IPresenceContainer becomes
IPresenceService. There is not much else that is needed, but we do need
to figure out how to activate a service. In the current Presence API
logging into the container starts activities that relate to the service,
i.e., presence information is reported from the server. It is important
to add the presence listener to the service before actually logging in.
I do not think we can require that all services be created prior to
login; this would seriously limit our ability to dynamically add
services. I think a different approach is needed. Here's what I would do:
1) require that the Service Conatainer be logged on before allowing
any services to be retrieved. (This compels an ordering, which will be
easier than allowing both orderings. It is also easier to enforce.)
2) avoid initiatiing any service specific processing prior to
activating the service.
2) provide activate and deactivate methods on the IPresenceService
that can be used to start and stop this particular service.

From an information hiding point of view, this approach has a certain
appeal. In addition, so long as the higher-level service APIs do not
impose too many base ECF API-isms, it can be supported without much
impact on the base ECF API.

*** One Factory, A Service Container ***

The approach just mentioned gives me what I am looking for, but is it
really necessary? Why do we need to define two parallel sets of
launching machinery? The pattern is the same, so why do it twice? The
question I find I am asking myself in the end is how to achieve my
objectives and the ECF objectives without defining a lot of new interfaces.

I believe the answer lies in splitting ISharedObjectContainer into two
interfaces: one that has all the machinery currently supported and
another that has only the minimal set of methods needed to activate the
higher-level APIs. (In essence, something like IServiceContainer
above.) To keep things as close to the way they currently are, let's
call the minimalist interface ISharedObjectContainer and the highly
capable interface, ISharedObjectContainerFull. (It's not the best name,
but I hope you get the idea.) Let's further stipulate that
ISharedObjectContainerFull extends ISharedObjectContainer.

Now the ISharedObjectConatiner is indistinguishable from the
IServiceContainer above. I would still have the getService method on it
as the means to obtain higher-level services. Now I might add the
getAdapter method, not as a means to obtain a service, but as a means to
upgrade my capabilities from a simple ISharedObjectContainer to an
ISharedObjectContianerFull. If the container is willing to expose the
innards, I'll be given the desired object. If it is unwilling to expose
the innards, I will receive null.

The key element of this approach is that it inverts the current approach
of the base ECF API. Whereas the current approach gives a full
container and allows one to get an adapter for a specialized service.
This approach gives a limited container and allows one to get an adapter
for the richer container. This allows one to block the richer access if
that seems like the right thing to do.

*** What do I prefer? ***

If there are four models on offer here, which do I prefer? The first
two are the simplest for the application developer, but they do not
separatie login from the higher-level service. I'm not yet sure whether
that is a problem, but I am tentatively inclined to dismiss these
approaches. Thus, the real question is whether to patch the ECF by
supporting some capability hiding or provide an alternate higher-level
model that sits above the ECF.

In truth, I am torn. On the one hand, some urge for efficiency in me is
offended by the idea of a completely parallel, but higher-level approach
to service launching. On the other hand, I often find that these urges
for efficiency are exactly the things that lead me to make things too
complicated.

Tentatively, I am inclined to think that if we define a parallel model
at a high-level, we can make sure that we design for ease of use and
simplicity without requiring that concrete constructs sit next to
abstract constructs. We can name things getService rather than
getAdapter, because we don't anticipate lots of other uses for this
pattern. And, we can use the 80/20 rule to avoid bringing in
capabilities that are only needed in obscure cases. In the end, we are
able to provide a simple model because the base ECF API is available
when much more is needed..
Some comments on how I think we should try to model Chat. [message #581767 is a reply to message #581469] Sun, 29 May 2005 21:32 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

In this posting I want to explain my attitude about what a Chat API
should be like. Many of these thoughts grow from working with Chat in
other contexts and are not really driven by the Presence API per se. As
usual, however, a Presence Service is uninteresting without the ability
to start a Chat and it therefore draws us into the question of how to
model Chat. The bottom line of my argument is that we do not want to
model Chat differently when it derives from a buddy list than when it
starts by some other means. In other words, we do not want the Presence
API to prejudge how Chat should be accomplished.

I'll try to make my case in the following four sections:
1) Chat as the phone ring
2) IM vs. eMeetings
3) Extensible Chat
4) The bottom line

*** Chat as the phone ring ***

A pattern of usage that we see a lot with Chat goes roughly as follows:
1) Initiate an IM session from the Buddy List (Roster)
2) Chat a little and then possibly say "bye"
3) If the conversation deepens, start a phone call.
4) Other forms of deepening are to invite other participants into
the Chat or add other types of communication tools, such as screen
sharing or a white board.
In other words, what starts as a Chat can grow into a full-blown online
meeting. I refer to these online meetings as eMeetings.

I think what is going on here is that the Chat is replacing the
telephone ring to some degree. I chat with you a little and find out
whether it is a good time or a bad time to have a deeper conversation
and then we proceed accordingly. A lot more information is exchanged
than can be achieved with a phone ring and it is less disturbing to most
people.

Admittedly, as often as 80% of the time the conversation will not deepen
beyond a simple chat, but there is no way to predict when it will.

*** IM vs. eMeetings ***

As engineering challenges, IM and full-blown eMeetings are somewhat
different beasts. Simple person-to-person chats can be done extremely
efficiently with some form of reliable datagram service. Each message
is addressed for the recipient and the service makes sure it arrives
appropriately. There is no need to place the Chat into a containing
context because there are no more resources needed and the messages
identify themselves adequately. (It can be a little harder if I want to
have two chats with the same person at the same time, but that is
unlikely.) eMeetings, on the other hand, usually involve some form of
containing context, e.g., a session or call or conference, to help keep
track of all the people and resources, e.g., audio or video or screen
sharing, devoted to the eMeeting.

Because the engineering challenges are different, there has been a
tendency to model IM and multi-way chat differently. From an efficiency
point of view, this makes sense, since it seems unnecessary to allocate
a containing context for a simple IM when 80% of the time that
containing context will just be thrown away. Unfortunately, this can
mean that when we want to grow our simple IM to include other people or
add additional resources, we have to abandon the initial IM and start up
a new eMeeting that has the capacity to grow.

*** Extensible Chat ***

I do not want to undervalue the engineering efficiencies gained by
avoiding full-blown eMeetings in the cases when they are not needed, but
I don't believe we need to force application developers to understand or
manage that tradeoff. We can model Chat as being like an eMeeting and
leave it to our implementations to decide when the Chat shifts from
being simple to more complex.

Suppose that when I want to Chat with someone from the Presence API, I
create a Chat object. (Smack actually does use a Chat object even
though the Presence API does not.) So far the resources desired are
simple and I can implement this using reliable datagrams even though it
presents itself as more session-like. Methods on the Chat object would
permit me to add a participant or add a tool, such as VoIP or a white
board. Once the implementation detects that the Chat needs to become
more complex, it could shift to a more full-blown eMeeting with all the
support needed for the burgeoning conference. If this is done well, the
user and the application developer do not need to understand that the
Chat has really undergone a change internally. The Chat API insulates
them from these engineering details.

*** The bottom line ***

My main point is that I do not want to see the ECF perpetuate the
tendency to treat IM and Chat as different entitites, leaving it to
application developers to decide at the outset what sort of Chat is
needed. This is not necessary and it is not desirable. If we create a
Chat API that presents Chat as an object with participant and tool
extensions, then we should be able to achieve the engineering
efficiencies without burdening the application developer with deciding
up front whether the Chat is likely to grow.
Re: A Critique of the Presence API [message #581788 is a reply to message #581469] Mon, 30 May 2005 17:41 Go to previous messageGo to next message
Bjorn Freeman-Benson is currently offline Bjorn Freeman-BensonFriend
Messages: 334
Registered: July 2009
Senior Member
John,
This is great. I'm more pleased by your example of "how to do a
critique" than the specifics of this critique - I'll leave that to the
ECF team. But thanks for taking the time to write this up - wow.

> My goal is to start a conversation about what
> these higher-level APIs are all about and how they should be designed.

- Bjorn
Re: A Critique of the Presence API [message #581892 is a reply to message #581469] Wed, 01 June 2005 16:37 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

Before responding/adding to all of John's thoughts in subsequent
postings I just wanted to say 'thanks' to him an Li Te for doing this
analysis. I agree completely with all of John's thoughts in the posting
attached below, and think that this sort of community critique and open
evaluation is *exactly* what will make ECF useful as a suite of APIs for
building communications applications. I hope we can use this as a model
for continuous development/improvement of ECF and other foundation projects.

Again, on behalf of the ECF team, thanks John and Li Te.

Now...into the design fray...I'll address John's subsequent postings as
rapidly as possible...please forgive me if significant time periods go
by without a posting from me or others on the ECF team...it's a very
busy time for us right now with the prep for another release (0.3.0),
prep for the upcoming Eclipse Foundation BOD meeting (next week), our
regular jobs, and our personal commitments. Again, I'll do the
best/most rapid that I can to engage John about these thoughts.

Thanks again.

Scott


John F. Patterson wrote:
> At Scott's invitation, I will be providing a critique of the
> org.eclipse.ecf.presence interfaces. In this posting I will explain why
> I am offering this critique. In four subsequent, child postings I will
> discuss the following:
>
> 1) My understanding of how the presence API works
> 2) Reflections on the presence API as a service or component interface.
> 3) Some thoughts about how much the ECF should show through to services
> and components.
> 4) Some comments on how I think we should try to model chat.
>
> Li-Te Cheng has provided invaluable assistance in reviewing these
> postings and helping me understand how things work.
>
> I am undertaking this effort because I believe that higher level APIs
> are essential to the fulfillment of the ECF mission. The Base ECF API
> itself is probably both too general and too low-level to provide easy to
> use virtualizations of standard collaboration services. To achieve this
> we will need higher-level APIs that particularize the ECF to one or
> another use. The presence API is the first example of such a
> higher-level API.
>
> Over time I believe the ECF will become a family of APIs supporting the
> work of three types of developers. Protocol-level development is the
> first type. The Base ECF API is a relatively low-level API that can be
> used in a wide variety of communication circumstances. Protocol-level
> developers will implement providers that map their capabilities onto ECF
> constructs, thereby hiding the protocol and easing the job of those who
> wish to use their protocol. Insofar as the ECF provides the necessary
> features, they will use them, but they will also create extensions as
> needed to support any protocol-specific capabilities. The difficult
> challenge of the Base ECF API design is to provide a common base design
> that captures many of the capabilities of a wide variety of protocols
> without leaning too much in the direction of any one.
>
> Service or component development is the next type. I have not found one
> word that characterizes this level of development. If, as in the
> Presence API, an API is trying to encapsulate an entire use of the ECF
> from login to logoff, it seems more appropriate to think of it as a
> service. If an API is trying to encapsulate some tool, like a chat,
> that can be added to a service, then we might prefer to think of this
> API as a component rather than a service. In either case, the
> development job is more like tools development. There is a high-level
> API that characterizes how the service or component will manifest itself
> and the tool developer must map the low-level ECF capabilities onto the
> high-level service or component API. Notice, that insofar as the
> service/component/tool can avoid provider-specific extensions of the
> Base ECF API, then these tool implementations will be reusable for
> multiple providers. Even if the tools are unable to achieve
> provider-neutrality in their implementations, we still have the
> opportunity to achieve provider-neutrality at the application level if
> the tools implement a standard higher-level API.
>
> Application development is the third type. Application developers will
> use the high-level service or component APIs. Whereas the Base ECF API
> is an enabling API, I believe the higher-level APIs should focus on ease
> of use. They should aim for an 80/20 rule approach in their design.
> (80% of the benefit, for 20% of the effort.) In other words, they
> should not try to do everything. They should aim for the common
> denominator of a capability and avoid the tendency to encapsulate the
> superset of all features offered by anyone. One goal is to make it easy
> for application authors to do the familiar things. Another is to
> achieve an effective virtualization of a capability that is often
> implemented in different ways for different protocols.
>
> Given this view, the Presence API and other higher-level service and
> component APIs defined by the ECF are of enormous importance. They will
> become the programming model for application developers who are not
> interested in the details, but want to make sure that their applications
> are robust over alternate providers. To the greatest degree possible we
> want to encourage only one model for any particular use, so that
> appllcations can be maximally leveraged. In many cases, I believe we
> will find that supporting virtualization at the Base ECF level is too
> difficult because we simply do not know enough about the uses for which
> we are designing. The service and component APIs might prove more
> fertile ground for some virtualizations precisely because more of the
> uncertainity about their use will have been settled. Indeed, it seems
> likely that we will only suggest higher-level APIs for those services
> and components for which a common protocol-neutral approach seems
> achievable..
>
> What we need is some principles for how to design these higher-level
> APIs. I think we know that they need to be easy to use and
> protocol-neutral, but what does this mean in practice. How would we
> know a better or worse API if we saw it? What are the patterns or
> elements of design that we will use to judge quality or at least ensure
> design coherence across multiple high-level APIs? Do we have a shared
> view of what these APIs will look like?
>
> By critiquing the Presence API, I hope to reveal some of my own opinions
> (even to myself) and initiate the discussion about the design of these
> high-level APIs. My critique is not intended as a slam on anyone's hard
> effort or personal preferences. It's simply easier to react than to
> start from scratch and the Presence API is sitting there as a first
> opportunity for reaction. My goal is to start a conversation about what
> these higher-level APIs are all about and how they should be designed.
> I hope you'll come along for the ride:-)
Re: Reflections on the presence API as a service or component interface. [message #581914 is a reply to message #581681] Wed, 01 June 2005 20:15 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

John F. Patterson wrote:
<stuff deleted>

>
> Ok, let's go. I will organize my observations into three sections
> characterized by the following headings:
> 1) Object Models vs. Protocol Models
> 2) The API as Training manual
> 3) Should the application developer need to know much about the ECF?
>
> *** Object Models vs Protocol Models ***
>
> My single biggest complaint about the Presence API is that it allows too
> many protocol details to leak through the API. I believe that these
> higher-level APIs are our opportunity to offer a fuly object-oriented
> approach to services and components and that we should not be asking the
> application developer to think like a protocol-level programmer.

I agree that we should be trying to abstract protocol-level details away
as much as possible, and it's certainly possible that the presence API
can do more/better of that, but it's hard for me to imagine that *all*
protocol aspects can be hidden...precisely because protocols at this
level of abstraction (at the 'application' layer) have specific
semantics...rather than being completely application generic (unlike the
link layer, network layer, session layer, etc within the OSI reference
model). The protocols themselves *provide* the presence sharing
functionality.

>
> Let me give two examples of what not to do, drawn from the Presence
> API. First, there are many places in the Presence API where shared
> entities are identified by an ID. While this might be appropriate at
> the ECF-level, I believe we should try to operate in terms of objects in
> the Presence API.

So I need to ask some questions about this idea/suggestion. One meaning
to your thought could be the object references themselves are cross
process (e.g. like rmi or other rpc implementations). That is, objects
that represent things like state changes (like IRosterEntry) could be
referred to with an object reference and those references could be
passed around...so that I could be 'given' a reference to your presence
state (e.g. when you arrived), and then 'consult' that present state to
show things in the UI (i.e. access a method on 'your' presence object to
show your presence state changing in my UI). First, I should ask: is
this what you mean by 'operating in terms of objects'? It might not be,
so please clarify if that's what you mean.

If this *is* what you mean, then I think this would/could be a very poor
design as the basis for a multipoint real-time communications system.
The reason I think this is *not* the way to go is that if object
references are passed around directly across process it has a number of
consequences: 1) an rpc mechanism has to be present to provide 'remote
references' to objects, and these mechanisms are typically (although not
necessarily) rather heavy weight in terms of both code size and runtime
performance; 2) garbage collection becomes very complicated (see, for
example, the problems with RMI); 3) failure handling becomes much more
complicated (i.e. how does a direct object reference become
'stale'?...and how/when do remotes get notified when that object 'goes
away'?); 3) the scaling and performance characteristics of such system
can be very problematic (e.g. when is the last time you saw a real-time
messaging system implemented with RMI)?

The approach taken with ECF (and many other messaging systems) is to
have object instances which are passed by *copy* rather than by
reference. The IPresence instances are of this nature (e.g.
IPresenceSender.sendPresenceUpdate(...)). Note this is conveyed in the
IPresence declaration via having the IPresence interface extend
Serializable.

Passing objects across process by value rather than by reference gets
around all the nasty problems with object-reference passing systems
(some described above)...particularly in the multipoint case (what is
the effect of providing X references to my presence to remote users?
What is the timing relationship of method calls on my remote object
between those users?, Is there any local /replicated state associated
with these proxy objects? If so, which state is local and which remote?
How is such state kept consistent? If the authoritative copy of an
object 'goes away'...e.g. user turns off their client...how does that
information get conveyed to remote proxies?).

The notion of having an ID for an object (like Presence info) allows it
to be uniquely identified across processes. That is, even if there are
two hundred copies of *my* presence info, it can have one globally
unique ID that indicates that it is mine. It may be stale (i.e. it
might be yesterday's presence state for me), but at least users of that
copy of my presence know that it is mine without having to be connected
to me. The ID (for users, services, containers, whatever) serves as a
persistent, but fragile reference.

The Eclipse plugin model itself is filled with ids...e.g. ids for
plugins...and this makes it possible to do things like have lazy loading
for plugin code (i.e. only actually loading the plugin code at time of
first usage). This is also a very helpful property for extensible
distributed applications where clients/servers may need to run for a
very long time, with all kinds of new object instances introduced by
both the local user and remote user. Having an ID for the object (e.g.
Presence) provided by you, for example, allows me to only load the code
for the presence plugin if I happen to need it for my application.

Thus, for example, instead of calling
> sendRosterRemove on the PresenceSender with an ID associated with the
> RosterEntry, why can't I just call a remove method on the RosterEntry?
> When I send a presence update, why must I give a fromID and a toID? We
> know the from ID; it's mine!

This 'fromID' can probably be removed, although it may very well be the
case that a given application that uses this api would want to actually
be the 'source' of presence updates. In that case, it would make sense
that the client application that uses IPresenceSender would want to
specify which of the (local) users the presence update was from.

As for the toID, when I am truly updating
> my presence (as opposed to negotiating subscriptions) is it even used?

Well, if you are updating your presence relative to a specific target,
then yes it would be used. That is, what if I want to only update my
presence state for *you* (as opposed to all my other buddies?). It
should be possible for me to specify a target for the presence
update...even if the typical case is for me to send a presence update to
'everyone' (whoever is on my 'buddy list').

> (Frankly, the current collection of methods all looks like opportunities
> to spoof becasue they require a fromID.)

The fromID for this api is a user identifier (e.g.
slewis@cerf.composent.com) rather than a client/process identifier (e.g.
A12234@216.99.211.99). The user identifier specifies who on the local
machine (if the application has/requires multiple users) is responsible
for the message. Of course the local application that uses this API has
to guarantee the validity of the userid if it has/uses several of them.

>
> Under most circumstances, an ID will be wrapped by some object (and if
> it is worth talking about, it probably should be.) The containerID is
> wrapped by the IPresenceContainer so we do not need it. Methods that
> need it should either be on the IPresenceContainer or pass in the
> IPresenceContainer. User IDs will usually be wrapped by something akin
> to the IRosterEntry. Methods that need it will be on these objects or
> pass them in. The one exception to this is when we want to refer to an
> object that we do not yet have. In these cases, the ID is akin to an
> address and must be provided usually for the purpose of obtaining the
> object. After that, we will act on the object.

I agree. Actually, I think that wrapper classes should probably be
created, and used in the IPresenceSender API (for example) rather than
specifying things like I did. That is,

IPresenceSender.sendPresenceUpdate(ID fromID, ID toID, IPresence presence)

could/should probably be changed to something like

IPresenceSender.sendPresenceUpdate(PresenceUpdate update);

where PresenceUpdate is a (new) wrapper class. This is a matter of API
taste, however, as I've had people complain about the need to create
instances of wrapper classes for objects that are simply 'throw away'.
Of course there is some a) programmer overhead for creating such
instances; b) some runtime overhead (i.e. memory creation/garbage
collection). BUT, I'm basically in agreement with you about the
creation/use of such wrapper classes.

>
> In the Base ECF APIl, IDs are used to ensure flexibility about the
> association of runtime objects to the shared abstractions. I believe
> that these are obscure cases that higher-level APIs do not need to
> support and should not support. The programming model for the Presence
> API would be much simpler if IDs were only used when essential (as
> addresses for unavailable objects). As it is right now, these IDs will
> need to be wrapped by some application-side object when there are
> perfectly good constructs within the API for wrapping these IDs.

Sure. I think you are making the same point as above...but not quite
certain. If not then please clarify.

>
> My second example comes from the use of the IPresence object. If I want
> to mark myself as do-not-disturb, I don't go to my IRosterEntry and
> select the do-not-distrub method. Instead, I create an IPresence object
> and mark it as do-not-disturb. This is klunky, non-intuitive, and
> completely surprising to an application developer schooled in the arts
> of OO programming. The verb, DoNotDistrub, is hidden as an adjective on
> a noun, IPresence. I understand from a protocol-level why this happens,
> but there is no reason why we should carry this into our higher-level
> APIs. There should be a do-not-distrub or changeStatus method either
> directly on the service or on an object representing my RosterEntry that
> I can directly invoke.

This can/could be added to the existing interfaces, but I think there
are some reasons to reconsider from the point of view of programmer
understandability.

The thing that I think gets particularly confusing (to the programmer)
about methods such as

public void doNotDisturb(IPresenceSender sender) {
// code here to construct a PresenceUpdate instance and send it
// this could be implemented easily, BTW with current interfaces
}

is a) timing; b) failure handling. Does this method block? (because it
requires 10,000 messages to be sent?). Doesn't that mean that the
caller should 'know' that this method will take 10 orders of magnitude
longer to execute than a method that just sets local state? What if the
communication fails? Will the sender ever know about it?

Yes, the mapping wil be a little tougher for the
> service or component implementer, but the resulting model will make much
> more sense to the application developer.

I'm not sure that's right. AFAICT, all network API efforts to 'abstract
away' things like network failures, timing issues, etc. via pure
replicated object abstractions have resulted in far more confusion...I
think that when it becomes apparent (at run time) that the application
programmer *really does care* about whether a given abstraction executes
10 orders of magnitude slower (i.e. blocks), or whether the
communication itself fails. I'm thinking here of the research:

http://www.cs.cornell.edu/vogels/papers/ew98.pdf

and http://research.sun.com/techrep/1994/abstract-29.html

To some degree, though, I agree with you...it *is* in some cases
desireable to have an API that 'hides' from the programmer that there is
network communication going on, but I think one has to do it very
carefully, because things like timing and failure handling are *vastly*
different and such APIs can be very surprising to users when their app
just 'behaves differently' because network communication is going on
underneath the covers...and that network communication has some
characteristics that are very different from within-process behavior
(e.g. partial failure basically doesn't happen).

>
> I guess my first principle of higher-level API design is that these APIs
> should be object-oriented, not protocol-oriented. If we lose some
> capability along the way, so-be-it. I doubt we will lose anything
> really critical. Frankly, if we want these APIs to be protocol-neutral,
> we will need to avoid letting protocol-isms into the APIs. Every time
> we do it, we are probably leaning towards one protocol and away from
> another. Object models might just be our best shot at a representation
> that is truly neutral with regards to alternative protocols.

I would like to see what you mean by 'object oriented'. Could you
describe/present a simple Presence API that that you would consider to
be object oriented in the sense that you are looking for?

The APIs that we have in ECF, like the presence API *is* object oriented
in that all data structures are represented as objects. This is really
valuable for encapsulation, security (type safety), etc.

BUT, method calls on object instances don't necessarily correspond to
network operations...and this is for several very good reasons as
discussed above. This makes it slightly less object oriented in that we
are not trying to make transparent the network operations
(messaging...both sending and receiving)...rather we are making them
explicit (by providing specific methods on objects that represent the
communications session/container itself). For the reasons expressed in
the papers above, I would be very hesitant to try to create abstractions
that fundamentally hide the network, because it's been basically a
dismal failure in terms of programmer adoption to this point.

>
> *** The API as Training Manual ***
>
<stuff deleted>

>
> Let's look at some of the aspects of the Presence API that could bear
> improvement. First, there is the issue of naming. Just because the ECF
> uses the name container, we have no obligation to use that name for the
> top level of the Presence API. For my money, we would be better off
> calling the IPresenceContainer, the IPresenceService. Admittedly, the
> term service is not enormously more precise than container, but I think
> it is somewhat better. In the ECF there will be many forms of
> containers some of which will feel like services and some of which will
> feel like components, e.g., an online meeting. I am inclined to use the
> term service for those cases when the container encapsulates a
> capability from login to logoff.

OK. I'm more than OK with renaming based upon consensus about
appropriate names. The one thing about naming that I've found is that
mine (or anyone's) rarely are universally agreed upon initially.

With IPresenceContainer I was trying to introduce the notion that
extensions of base container functionality (represented by
ISharedObjectContainer) would have I<semantics>Container rather than to
introduce a new concept ('service'), BUT I'm OK with the notion of
having adapter interfaces that are extensions of the basic container be
referred to as 'services'. OTOH, I think we might be a little careful
here...because the notion of having a service is already quite well
defined in the OSGI component model, and it might be best to *not* use
that word (service) unless it's associated with the same kind of
behavior implied by the OSGI service model (i.e. it's really an OSGI
'service'). If we used service in a completely different way thatn OSGI
did (e.g. see org.osgi.framework.*) this might be *very* confusing.


>
> Second, why are there so many interfaces in the Presence API? Putting
> aside the listeners, it seems like there only need to be three main
> interfaces: IPresenceService, IRosterEntry, and IChat (which isn't
> there.)

aka IMessageSender/Listener

If we throw in IRosterGroup, we have four.

With listeners corresponding to both, we then have a total of 8. In the
existing API there are a total of 10...an additional interface
IAccountManager, which provides some 'meta operations' for user account
management (e.g. password changes, etc). Basically this brings it up to
~10. The account management utility interface could be consolidated
(i.e. just made a part of IPresenceContainer), but this kinds of
complicates the core interface IPresenceContainer with a number of
functions that are relatively peripheral.


As mentioned above,
> IPresence is primarily a messaging format that should be expanded into
> more methods on the primary objects. IPresenceSender, IMessageSender,
> and IAccountManager do not seem necessary. Their methods could safely
> be moved onto the IPresenceService without loss of clarity.
>
> I am amenable to this form of additional control objects if it serves
> some purpose, but I don't see the purpose in the Presence API. It can't
> be to reduce the method count to something tractable. altogether they
> add 11 methods, seven of which come from the AccountManager. So leave
> the account manager separate and bring in the others. It will make it
> easier to see all the service level methods in one place. Moreover,
> pulling out IPresenceSender and IMessageSender as distinguished seems
> like a continuing nod to the protocol-level design.

I'm amenable to consolidation...but I think there is good reason (for
'semantic coherence') to have things grouped a little bit. I personally
despise interfaces that have all kinds of methods...from soup to
nuts...which I then have to wade through to figure out which are
important. I think that others have these concerns as well. BUT like I
said I'm open to some consolidation upon lines that the community finds
appropriate/desireable. Also with the IAccountManager there is an issue
of access control/permission and, in fact, 'optional' functionality
(IAccountManager) vs. required functionality (IPresenceContainer).

>
> Finally, one quick comment. When I argued with Li-Te Cheng for
> flattening the IAccountManager into the IPresenceContainer, he suggested
> that one reason to keep it separate might be to be able to return null
> if you lack permission to manage accounts.

Yes.

This makes sense, so I
> relented in this case, only to notice later that the IAccountManager
> contains both administrator functions and the changePassword method that
> everyone will need to access. Even if Li-Te is right, we need to get
> the changePassword method out of the IAccountManager and onto the
> IPresenceContainer.

True enough. I would be fine with moving such methods around if we keep
the separation between these interfaces.

>
> I guess my second design principle is to avoid allowing the base ECF API
> or other models. like Smack lead us astray. For higher-level APIs, we
> still need good OO design that uses clear and precise language, avoids
> excessive interfaces, and factors things sensibly.

I agree completely!! I would like to understand better, however, what
you mean by 'good OO design'...as I think the key thing to figure out is
*which* OO concepts are appropriate for a network API...e.g. data
structures represented as classes, serializability, class/method naming
conventions, inheritance and/or delegation for extensibility, adapters,
etc are just great for me. My main concern about your points above is
that *if* we try to create an abstraction that gives/attempts to give
network transparency that we might be going down a well-worn and poor
path. BUT, I do agree that we can introduce more/other OO concepts
short of network transparency...and I'm open to any such detailed ideas
there.

>
> *** Should the application developer need to know the ECF? ***
>
> Why should I need to know a complex, general purpose API, like ECF, to
> use a specialized, simple API, like the Presence API? I don't think I
> should have to know the base ECF API to use its higher-level APIs. This
> has several implications for the Presence API. I'll discuss only one
> here and expand on this theme further in my next posting.

Well, here's a minimal use of the presence API...and this doesn't
require much knowledge of the core API:

ISharedObjectContainer container =
SharedObjectContainerFactory.makeSharedObjectContainer();

IPresenceContainer presence =
container.getAdapter(IPresenceContainer.class);

// Use presence API in any way desired here

If you are looking for something shorter it would be easy to do
something like this (which I contemplated adding to the presence plugin):

IPresenceContainer presence =
PresenceContainerFactory.makePresenceContainer();

// Use presence API in any way desired here

This might be good...and would be trivial to implement. Actually, I was
thinking of using the Eclipse adapterfactory to do this without even
needing a new factory class.

>
> I question whether it is advisable to expose ECF functionality through
> the Presence API. This shows up in the form of the
> makeSharedObjectContainer and addSharedObjectListener methods on the
> IPresenceContainer. These methods do not seem to be essential to the
> Presence Service and could be accomplished by a knowledgable developer
> by working directly with the base ECF API. For any newbie application
> developer exploring the Presence API, however, they pose a problem.
> They draw the application developer into the base ECF API for fear that
> it is critical to the operation of the Presence API. This simply leads
> them astray. By keeping unnecessary ECF features out of the Presence
> API, we can help the application developer keep focused on what the
> presence model really is.

Well, I think most of the functions exposed by ISharedObjectContainer
*are* in fact critical to most real applications...i.e. those that need
to dispose the container when done with it, need to 'disconnect'
(leaveGroup) and 'reconnect' (joinGroup) via user actions and/or network
actions. With the possible exception of getSharedObjectManager(), I
think that the basic container semantics are pretty necessary for
most/many real apps...even those that only use IPresenceContainer.

>
> So, this is my third design principle: keep unnecessary ECF elements out
> of the higher-level APIs.

I think this is, in general, a good principle, but I'm not sure if I
agree with your assessment of ISharedObjectContainer in this case.
OTOH, I'm OK with models where access to the ISharedObjectContainer
reference directly are 'hidden' by a given provider. But I think that
WRT methods like 'joinGroup' and 'leaveGroup' and several others in
ISharedObjectContainer we would quickly find that any application would
require these to be made available so that applications can control such
operations.

Thanks again for the comments. I'll respond to your other posted
comments and your/the community's comments in response to this note as
quickly as possible. Please be patient and we'll work through all of
these things to get to an much improved API.

Scott
Re: Some thoughts about how much the ECF should show through to services and components. [message #581986 is a reply to message #581720] Wed, 01 June 2005 22:43 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

This is a very interesting posting John. Lots of it
parallels/summarizes my own struggles with the designs of this part of
the system...e.g. at the container level of abstraction (very high
level), what is common to all applications? We don't want to have every
'higher' level interface/extension of the container (e.g. presence api)
be required to reimplement certain functionality required of *all*
systems (e.g. join/leave aka login/logout), but at the same time we
would like to keep the base container functionality as simple as
possible, so that more application-specific APIs (e.g. presence again)
are not burdened with 'extra' and possibly unnecessary functionality to
implement.

It's good to know I'm not the only one struggling with these questions
of abstraction vs. usability of these apis...:).

A couple of comments interspersed below...

John F. Patterson wrote:
> There must be some law of nature that says you cannot simultaneously be
> general purpose and easy to use. If we want to make the ECF serve all
> the purposes that have been idenitified, then it will be abstract. I
> don't think it can be otherwise. Abstractions are not easy to use,
> however. Concrete things that relate directly to one's immediate
> purpose and have an obvious relationship to UI elements in similar
> applications are much easier. This is why we want the higher-level APIs
> accompanying the ECF.

Yes...and we want to be able to introduce other higher-level APIs
without having to continuously modify the core abstractions. And we
need/want to do this at runtime, so that clients can make decisions
about UIs, etc. based upon the available set of implementations, rather
than on some ECF compile-time specified set of capabilities.

>
> But the value of the higher-level API is diminished if I must know the
> base ECF API in order to use the higher-level API. In the Presence API,
> I must learn about an ISharedObjectContainer to activate, get, and
> deactivate the Presence Service. This probably seems pretty minimal,
> but how do I know that I don't also need to:
> 1) get the ISharedObjectContainerConfig,
> 2) get the ISharedObjectManager, or
> 3) use the getGroupMemberIDs method to find out who is online.

True enough...but I would argue that of the methods in the
ISharedObjectContainer interface, the only ones that *require* that you
learn about them from the get go are the ones most likely to be used by
all (or nearly all) clients...which is exactly what you would want.
That is the methods on ISharedObjectContainer:

joinGroup
leaveGroup
(critical for 'connect/login' 'disconnect'/logout semantics)

dispose()
(critical for cleanup/resource mgmt)

getAdapter()
(critical for runtime extensibility)

are the main ones that I would expect nearly all applications to need to
use (whether they are applications for user presence, apps for real-time
chat, etc., etc).

> Not only am I drawn into learning a number of complex things that I do
> not need to know simply to do a Presence application, but I also have
> access to capabilities that must be used correctly or they could break
> the Presence implementation. It is for this reason that I have been
> tryiing to figure out how to offer the higher-level APIs without
> requiring one to know the base ECF API.

Well, I agree with your general principal strongly...but I think that
the 'need to know' the semantics associated with the above operations is
*pretty strong*, no matter what apps. You can argue (and believe me, I
have) that the above concepts can be conveyed more clearly/better than
what the above convey, but I think they are pretty minimal...even for
applications like presence.

>
> I will describe my thoughts in the following five parts, which roughly
> correspond to the mental journey that I have gone through:
> 1) One Factory, No Service Container
> 2) Two Factories, No Service Container
> 3) Two Factories, A Service Container
> 4) One Factory, A Service Container
> 5) What do I prefer?
>
> *** One Factory, No Service Container ***
>
> My first thought about how to proceed was to simply eliminate the
> "middle man." Why not expand the capabilities of the
> SharedObjectContainerFactory to support launching something like the
> IPresenceContainer directly. Now, all the application developer needs
> to learn about is the SharedObjectContainerFactory (perhaps renamed to
> something simple, like ECFFactory or, even simpler, ECF.) There would
> be no need to study the ISharedObjectContainer and none "in hand" at
> run-time to mess around with. A simple method like "getService" could
> be used to hide container creation and getting the appropriate adapter.
>
> To do this, the Presence API would need to be modified, since it is not
> complete. The methods for login and logoff would need to be added.
> This seems reasonably straightforward, however..
>
> In the end, I achieve my objective. Application developers are not
> drawn into learning a lot more than they need to know and they do not
> have access to the underpinnings of the Presence Service to use
> inappropriately.
>
> *** Two Factories, No Service Container ***
>
> I was pretty happy with the conclusion above until Li-Te Cheng suggested
> that we create a new singleton for service creation rather than extend
> the SharedObjectContainerFactory. I think this was partly an aesthetic
> judgment about the desirability of blending the two levels of
> abstraction. In addition, it is a way to ensure that we expose only
> those things we really want to expose.
>
> In this view of the world, there are two factories. The
> SharedObjectContainerFactory stays the way it is and another
> ServiceFactory is created. New services would plug into this factory
> and be obtained by calling something like getService that would hide
> creating the container and getting the adapter. As above, the Presence
> Service would need to have its own login and logoff capabilities.

I would be a little concerned with naming things 'getService' because of
the conceptual conflict with OSGI itself. I don't want to conflict with
OSGI's notion of service by introducing the term used in a different way
in ECF...although I think that perhaps ECF can use the OSGI service
notion to address some of your concerns articulated in this posting. I
need to consider this some more, however. It might be that an
OSGI-level service could be introduced/exposed by ECF that would behave
very much as you describe...i.e. providing for a way to use the OSGI
service lookup to 'centralize' the service lookup and creation for all
kinds of containers (e.g. those that implement ISharedObjectContainer
and/or those that implement IPresenceContainer).

>
> *** Two Factories, A Service Container ***
>
> It was at this point that I realized why Scott designed the Presence API
> the way that he did. There might be other reasons, but one critical one
> is that he wants to ensure that one ISharedObjectContainer can support
> several higher-level services without requiring a login for each
> service. The approaches above associate the login with the service.
> Multiple services off the same ISharedObjectConatiner will end up
> logging in multiply, which is probably unencessary, if not incorrect.
> Undoubtedly, there are ways to put the login into the service and still
> avoid multiple logins, but Scott's approach is simpler. You get an
> object that proxies for the service provider; you log on to that
> provider; and then you request each service that you desire.
>
> So how do I retrieve my ECF-hiding and incorporate Scott's pattern? The
> answer is to recreate the machinery that we find in the base ECF API,
> but in a manner that is appropriate to the higher-level service APIs.
>
> First, we create the analog of the SharedObjectContainerFactory, let's
> call it the ServiceContainerFactory and its job is to create
> IServiceContainers. IServiceContainers are the analog of
> ISharedObjectContainers, but they are not as rich. The only methods
> that must be there are login and logoff methods as well as a getService
> method. The goal is to isolate application developers from the innards,
> so we don't want a lot of additional methods. It might make sense to
> have ways to determine which services are active or which are
> available. This is the IServiceContainer after all, so we could use it
> to help keep track of services.
>
> Notice that I used the term getService, but I expect that it will follow
> the getAdapter pattern -- pass in the interface needed, cast the return
> value to it, and check for null, just in case. I don't see why we can't
> use the pattern, but change the name to what we think will really be
> retrieved. It is less abstract, but it will make it easier for the
> application developer to understand how he gets what he wants. It's
> probably not necessary, but we might also modify the pattern slightly to
> stipulate that the getService method will return an IService rather than
> an Object. Then we could define an IService method with the minimal set
> of methods that we would expect any service to implement. This would
> not be a lot, since we don't want to impose many requirements on service
> authors. The real purpose of IService is to create a type for
> polymorphism purposes. All services would extend this interface.

Again I would be very concerned with conflicting with OSGI on the use of
the term Service...that's basically why I stuck with the getAdapter
approach (even though it is not based upon IAdaptable so that ECF code
can run outside of Eclipse without any trouble).

>
> The Presence API is now modified so that the IPresenceContainer becomes
> IPresenceService. There is not much else that is needed, but we do need
> to figure out how to activate a service. In the current Presence API
> logging into the container starts activities that relate to the service,
> i.e., presence information is reported from the server. It is important
> to add the presence listener to the service before actually logging in.
> I do not think we can require that all services be created prior to
> login; this would seriously limit our ability to dynamically add
> services. I think a different approach is needed. Here's what I would do:
> 1) require that the Service Conatainer be logged on before allowing
> any services to be retrieved. (This compels an ordering, which will be
> easier than allowing both orderings. It is also easier to enforce.)
> 2) avoid initiatiing any service specific processing prior to
> activating the service.
> 2) provide activate and deactivate methods on the IPresenceService
> that can be used to start and stop this particular service.

Couldn't joinGroup/leaveGroup/dispose take care of this semantics? I
think it would be a poor design to put activate and deactivate in
IPresenceContainer (or whatever we end up calling it), because I suspect
that they will then have to be duplicated in other interfaces that are
extensions similar to IPresenceContainer. That is, activate and
deactivate seem at the wrong level of abstraction compared to
IPresenceContainer. They are container object lifecycle methods, rather
than methods specific to presence per se.

>
> From an information hiding point of view, this approach has a certain
> appeal. In addition, so long as the higher-level service APIs do not
> impose too many base ECF API-isms, it can be supported without much
> impact on the base ECF API.
>
> *** One Factory, A Service Container ***
>
> The approach just mentioned gives me what I am looking for, but is it
> really necessary? Why do we need to define two parallel sets of
> launching machinery? The pattern is the same, so why do it twice? The
> question I find I am asking myself in the end is how to achieve my
> objectives and the ECF objectives without defining a lot of new interfaces.
>
> I believe the answer lies in splitting ISharedObjectContainer into two
> interfaces: one that has all the machinery currently supported and
> another that has only the minimal set of methods needed to activate the
> higher-level APIs. (In essence, something like IServiceContainer
> above.) To keep things as close to the way they currently are, let's
> call the minimalist interface ISharedObjectContainer and the highly
> capable interface, ISharedObjectContainerFull. (It's not the best name,
> but I hope you get the idea.) Let's further stipulate that
> ISharedObjectContainerFull extends ISharedObjectContainer.
>
> Now the ISharedObjectConatiner is indistinguishable from the
> IServiceContainer above. I would still have the getService method on it
> as the means to obtain higher-level services. Now I might add the
> getAdapter method, not as a means to obtain a service, but as a means to
> upgrade my capabilities from a simple ISharedObjectContainer to an
> ISharedObjectContianerFull. If the container is willing to expose the
> innards, I'll be given the desired object. If it is unwilling to expose
> the innards, I will receive null.
>
> The key element of this approach is that it inverts the current approach
> of the base ECF API. Whereas the current approach gives a full
> container and allows one to get an adapter for a specialized service.
> This approach gives a limited container and allows one to get an adapter
> for the richer container. This allows one to block the richer access if
> that seems like the right thing to do.
>
> *** What do I prefer? ***
>
> If there are four models on offer here, which do I prefer? The first
> two are the simplest for the application developer, but they do not
> separatie login from the higher-level service. I'm not yet sure whether
> that is a problem, but I am tentatively inclined to dismiss these
> approaches. Thus, the real question is whether to patch the ECF by
> supporting some capability hiding or provide an alternate higher-level
> model that sits above the ECF.
>
> In truth, I am torn. On the one hand, some urge for efficiency in me is
> offended by the idea of a completely parallel, but higher-level approach
> to service launching. On the other hand, I often find that these urges
> for efficiency are exactly the things that lead me to make things too
> complicated.
>
> Tentatively, I am inclined to think that if we define a parallel model
> at a high-level, we can make sure that we design for ease of use and
> simplicity without requiring that concrete constructs sit next to
> abstract constructs.

We could very easily break ISharedObjectContainer into super/sub
interfaces and have them inherit from one another. I'm just not sure
what the appropriate breakdown is. Please (everyone) let me know what
you think about this and we can contemplate two interfaces related by
inheritance rather than one.

We can name things getService rather than
> getAdapter, because we don't anticipate lots of other uses for this
> pattern.

I don't think we can reasonably use getService(). It would just be too
confusing relative to org.osgi.framework.BundleContext.getService().

I'm open to other namings of this semantics (getAdapter), but I think
it's convenient to use the IAdaptable pattern (and possibly be able to
have implementations that actually use IAdaptable...e.g. eclipse
resources (e.g. IResource)...

And, we can use the 80/20 rule to avoid bringing in
> capabilities that are only needed in obscure cases. In the end, we are
> able to provide a simple model because the base ECF API is available
> when much more is needed..

I agree. I am willing to break down ISharedObjectContainer and have
contemplated doing so...I'm just not sure what breakdown is best/most
understandable (into more/less abstract interfaces...related by
inheritance or delegation).

Thanks,

Scott
Re: Some thoughts about how much the ECF should show through to services and components. [message #582054 is a reply to message #581986] Wed, 08 June 2005 19:28 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

I'll offer a few up front comments and then embed the rest.

Sorry for using the term "service". You warned me about the fact that
this term has been co-opted by OSGi and I forgot. I like the term, but
I am not familiar enough with OSGi to argue for using it. Instead, I am
adopting an alternative placeholder name. For the time being, I will
start using the term "venue" instead of "service". It's not ideal, but
I ran out of steam looking for an alternative. If anyone has a better
name, let's hear about it.

I find it very encouraging that you would consider splitting the
ISharedObjectContainer interface. I would be inclined to refer to the
higher-level (less capable) interface as IVenueContainer. I think you
have told us which methods need to be part of this interface: joinGroup,
leaveGroup, dispose, and getAdapter. I would prefer names like login,
logoff, dispose, and getVenue respectively, but the purpose of the
methods is pretty much the same. The ISharedObjectContainer interface
could extend IVenueContainer or, if it makes more sense, implementations
of IVenueContainer would simply wrap ISharedObjectContainer objects to
do what is needed.

An issue that my original posting touched on, but didn't discuss
adequately is where the higher-level component's "plug in". Ideally, we
would like to ensure that a component or tool developer who does not
know about low-level protocol details could use the ECF to create a new
component and plug it in. Where do they do this? If I use the ECF to
create a new IPresenceVenue implementation, where do I plug it in? I
imagine that I will want to install an IVenueFactory somewhere. The
instantiator of this factory will obtain the ISharedObjectContainer so
that it has access to all capabilities, but it will only expose the
capabilities as defined in the particular Venue. Does the factory plug
in to the ISharedObjectContainerFactory even though it is not a
container factory? Or, does it plug in to the ISharedObjectContainer
even though in some cases it might be protocol neutral and therefore
available to all implementations of ISharedObjectContainer? I think
there are ways to do this, but we need to think it through. Component
authors need ways to add new venues that are sometimes protocol-specific
and other times protocol-neutral, but without requiring a change to the
provider implementation.

jfp

Scott Lewis wrote:

> Hi John,
>
> This is a very interesting posting John. Lots of it
> parallels/summarizes my own struggles with the designs of this part of
> the system...e.g. at the container level of abstraction (very high
> level), what is common to all applications? We don't want to have
> every 'higher' level interface/extension of the container (e.g.
> presence api) be required to reimplement certain functionality
> required of *all* systems (e.g. join/leave aka login/logout), but at
> the same time we would like to keep the base container functionality
> as simple as possible, so that more application-specific APIs (e.g.
> presence again) are not burdened with 'extra' and possibly unnecessary
> functionality to implement.
>
> It's good to know I'm not the only one struggling with these questions
> of abstraction vs. usability of these apis...:).
>
Perhaps this is easy for others, I agree it is a struggle and not
altogether obvious.

I find it is helpful to keep going back to the idea of three types of
developers. Each is trying to implement a mapping. At the protocol
level the mapping is from the socket and wire onto a base object-level
API. The goal is to remap the capabilities onto more familiar terms
(and, wherever possible, protocol-neutral terms), but without losing any
capabilty. The component or tool developer maps from this base API onto
Venue-specific APIs. These APIs are not expected to do everything,
should strive even harder for protocol neutrality, and must emphasize
ease of use. Then, the application developer maps from the high-level
API onto a user interface and application.

Of course, all this needs to occur while adhering to some challenging
principles of design, namely:
1) implementation independence -- the interfaces should define the
contract; introducing capabilites at one level should not require
changes at another,
2) contract sufficiency -- the interfaces at each level should be
sufficient to get the job done at the next level,
3) capability hiding -- the interface at each level should not
expose more than is needed and should not provide access to lower-level
capabilities that could be misused and disrupt the interface contract.
This last point is probably where I tend to part company with others.
Some people like interfaces that are enabling. If there are
opportunities for inconsistencies or misuse of the API, then that is
just a problem for the developer. I do not like to see methods that are
flagged as "for internal use only" or "don't use this while doing that"
or whatever. These situations can be difficult to avoid, but they are
little indictments of the design. Fro this reason, I am fond of black
box interfaces that present a single model of operation and prevent one
from exploring into the details of the implementation.. I believe the
ECF faces the interesting challenge of creating one black-box interface
(the base ECF API) that supports extension via plug-ins (the venues)
that are themselves implementations of black box interfaces.

>
>
> John F. Patterson wrote:
>
>> There must be some law of nature that says you cannot simultaneously
>> be general purpose and easy to use. If we want to make the ECF serve
>> all the purposes that have been idenitified, then it will be
>> abstract. I don't think it can be otherwise. Abstractions are not
>> easy to use, however. Concrete things that relate directly to one's
>> immediate purpose and have an obvious relationship to UI elements in
>> similar applications are much easier. This is why we want the
>> higher-level APIs accompanying the ECF.
>
>
> Yes...and we want to be able to introduce other higher-level APIs
> without having to continuously modify the core abstractions. And we
> need/want to do this at runtime, so that clients can make decisions
> about UIs, etc. based upon the available set of implementations,
> rather than on some ECF compile-time specified set of capabilities.
>
We agree.

>>
>> But the value of the higher-level API is diminished if I must know
>> the base ECF API in order to use the higher-level API. In the
>> Presence API, I must learn about an ISharedObjectContainer to
>> activate, get, and deactivate the Presence Service. This probably
>> seems pretty minimal, but how do I know that I don't also need to:
>> 1) get the ISharedObjectContainerConfig,
>> 2) get the ISharedObjectManager, or
>> 3) use the getGroupMemberIDs method to find out who is online.
>
>
> True enough...but I would argue that of the methods in the
> ISharedObjectContainer interface, the only ones that *require* that
> you learn about them from the get go are the ones most likely to be
> used by all (or nearly all) clients...which is exactly what you would
> want. That is the methods on ISharedObjectContainer:
>
> joinGroup
> leaveGroup
> (critical for 'connect/login' 'disconnect'/logout semantics)
>
> dispose()
> (critical for cleanup/resource mgmt)
>
> getAdapter()
> (critical for runtime extensibility)
>
> are the main ones that I would expect nearly all applications to need
> to use (whether they are applications for user presence, apps for
> real-time chat, etc., etc).
>
As I said earlier, I agree with the choice of methods, but not the
names. We can worry about that later though.

>> Not only am I drawn into learning a number of complex things that I
>> do not need to know simply to do a Presence application, but I also
>> have access to capabilities that must be used correctly or they could
>> break the Presence implementation. It is for this reason that I have
>> been tryiing to figure out how to offer the higher-level APIs without
>> requiring one to know the base ECF API.
>
>
> Well, I agree with your general principal strongly...but I think that
> the 'need to know' the semantics associated with the above operations
> is *pretty strong*, no matter what apps. You can argue (and believe
> me, I have) that the above concepts can be conveyed more
> clearly/better than what the above convey, but I think they are pretty
> minimal...even for applications like presence.
>
I agree with the need to know these things, but that says to me (as you
suggest below) that the right thing to do is to separate them out so
that they are exposed to application developers and the other parts of
ISharedObjectContainer are shown only to the component developers (and
application developers who want to do more complicated things than are
captured by our venue extenions.)

>>
>> I will describe my thoughts in the following five parts, which
>> roughly correspond to the mental journey that I have gone through:
>> 1) One Factory, No Service Container
>> 2) Two Factories, No Service Container
>> 3) Two Factories, A Service Container
>> 4) One Factory, A Service Container
>> 5) What do I prefer?
>>
>> *** One Factory, No Service Container ***
>>
>> My first thought about how to proceed was to simply eliminate the
>> "middle man." Why not expand the capabilities of the
>> SharedObjectContainerFactory to support launching something like the
>> IPresenceContainer directly. Now, all the application developer
>> needs to learn about is the SharedObjectContainerFactory (perhaps
>> renamed to something simple, like ECFFactory or, even simpler, ECF.)
>> There would be no need to study the ISharedObjectContainer and none
>> "in hand" at run-time to mess around with. A simple method like
>> "getService" could be used to hide container creation and getting the
>> appropriate adapter.
>>
>> To do this, the Presence API would need to be modified, since it is
>> not complete. The methods for login and logoff would need to be
>> added. This seems reasonably straightforward, however..
>>
>> In the end, I achieve my objective. Application developers are not
>> drawn into learning a lot more than they need to know and they do not
>> have access to the underpinnings of the Presence Service to use
>> inappropriately.
>>
>> *** Two Factories, No Service Container ***
>>
>> I was pretty happy with the conclusion above until Li-Te Cheng
>> suggested that we create a new singleton for service creation rather
>> than extend the SharedObjectContainerFactory. I think this was
>> partly an aesthetic judgment about the desirability of blending the
>> two levels of abstraction. In addition, it is a way to ensure that
>> we expose only those things we really want to expose.
>>
>> In this view of the world, there are two factories. The
>> SharedObjectContainerFactory stays the way it is and another
>> ServiceFactory is created. New services would plug into this factory
>> and be obtained by calling something like getService that would hide
>> creating the container and getting the adapter. As above, the
>> Presence Service would need to have its own login and logoff
>> capabilities.
>
>
> I would be a little concerned with naming things 'getService' because
> of the conceptual conflict with OSGI itself. I don't want to conflict
> with OSGI's notion of service by introducing the term used in a
> different way in ECF...although I think that perhaps ECF can use the
> OSGI service notion to address some of your concerns articulated in
> this posting. I need to consider this some more, however. It might
> be that an OSGI-level service could be introduced/exposed by ECF that
> would behave very much as you describe...i.e. providing for a way to
> use the OSGI service lookup to 'centralize' the service lookup and
> creation for all kinds of containers (e.g. those that implement
> ISharedObjectContainer and/or those that implement IPresenceContainer).
>
Duly noted, thus the switch to the term venue. I leave it to you to
decide whether a venue is really just an OSGi service. I think your
first instincts are probably correct. It is better to avoid the term
"service".

>>
>> *** Two Factories, A Service Container ***
>>
>> It was at this point that I realized why Scott designed the Presence
>> API the way that he did. There might be other reasons, but one
>> critical one is that he wants to ensure that one
>> ISharedObjectContainer can support several higher-level services
>> without requiring a login for each service. The approaches above
>> associate the login with the service. Multiple services off the same
>> ISharedObjectConatiner will end up logging in multiply, which is
>> probably unencessary, if not incorrect. Undoubtedly, there are ways
>> to put the login into the service and still avoid multiple logins,
>> but Scott's approach is simpler. You get an object that proxies for
>> the service provider; you log on to that provider; and then you
>> request each service that you desire.
>>
>> So how do I retrieve my ECF-hiding and incorporate Scott's pattern?
>> The answer is to recreate the machinery that we find in the base ECF
>> API, but in a manner that is appropriate to the higher-level service
>> APIs.
>>
>> First, we create the analog of the SharedObjectContainerFactory,
>> let's call it the ServiceContainerFactory and its job is to create
>> IServiceContainers. IServiceContainers are the analog of
>> ISharedObjectContainers, but they are not as rich. The only methods
>> that must be there are login and logoff methods as well as a
>> getService method. The goal is to isolate application developers
>> from the innards, so we don't want a lot of additional methods. It
>> might make sense to have ways to determine which services are active
>> or which are available. This is the IServiceContainer after all, so
>> we could use it to help keep track of services.
>>
>> Notice that I used the term getService, but I expect that it will
>> follow the getAdapter pattern -- pass in the interface needed, cast
>> the return value to it, and check for null, just in case. I don't
>> see why we can't use the pattern, but change the name to what we
>> think will really be retrieved. It is less abstract, but it will
>> make it easier for the application developer to understand how he
>> gets what he wants. It's probably not necessary, but we might also
>> modify the pattern slightly to stipulate that the getService method
>> will return an IService rather than an Object. Then we could define
>> an IService method with the minimal set of methods that we would
>> expect any service to implement. This would not be a lot, since we
>> don't want to impose many requirements on service authors. The real
>> purpose of IService is to create a type for polymorphism purposes.
>> All services would extend this interface.
>
>
> Again I would be very concerned with conflicting with OSGI on the use
> of the term Service...that's basically why I stuck with the getAdapter
> approach (even though it is not based upon IAdaptable so that ECF code
> can run outside of Eclipse without any trouble).

>>
>> The Presence API is now modified so that the IPresenceContainer
>> becomes IPresenceService. There is not much else that is needed, but
>> we do need to figure out how to activate a service. In the current
>> Presence API logging into the container starts activities that relate
>> to the service, i.e., presence information is reported from the
>> server. It is important to add the presence listener to the service
>> before actually logging in. I do not think we can require that all
>> services be created prior to login; this would seriously limit our
>> ability to dynamically add services. I think a different approach is
>> needed. Here's what I would do:
>> 1) require that the Service Conatainer be logged on before
>> allowing any services to be retrieved. (This compels an ordering,
>> which will be easier than allowing both orderings. It is also easier
>> to enforce.)
>> 2) avoid initiatiing any service specific processing prior to
>> activating the service.
>> 2) provide activate and deactivate methods on the IPresenceService
>> that can be used to start and stop this particular service.
>
>
> Couldn't joinGroup/leaveGroup/dispose take care of this semantics? I
> think it would be a poor design to put activate and deactivate in
> IPresenceContainer (or whatever we end up calling it), because I
> suspect that they will then have to be duplicated in other interfaces
> that are extensions similar to IPresenceContainer. That is, activate
> and deactivate seem at the wrong level of abstraction compared to
> IPresenceContainer. They are container object lifecycle methods,
> rather than methods specific to presence per se.
>
Here is the problem that I foresee. We have one IVenueContainer
(ISharedObjectContainer), which is capable of supporting multiple IVenue
objects (higher-level things like IPresenceContainer). Login
(joinGroup) is on the IVenueContainer, not on the IVenue object. Now
imagine, as in the IPresenceContainer, that startup begins some action
that must be listened for. If login initiates the startup action for
the IVenue objects, then their listeners must be installed before login
occurs, otherwise the startup replies will be missed. Unfortunately, we
do not want to require that listeners be installed before login occurs,
otherwise it would be necessary for all venues to be instantiated prior
to login, which would mean that we could not dynamically discover that
we need another venue after we have logged into the IVenueContainer
(ISharedObjectContainer). In other words, we face a choice, either we
cannot kick off venue specific events based on login or we must insist
that all venues are started prior to login. (Actually, it is only the
venues for which activities are tied to login.)

Since I believe that dynamic addtion of venues is more important. I am
inclined to argue that venue-specific activities should not be initiated
at login (joining the group). If a venue wants to support some kind of
startup eventing, then the venue's interface should contain an activate
method. Better, however, would be a method like getRoster that
indicates the types of events that will be initiated. I am not thinking
of the activate method as part of lifecycle management, but as a way to
hold off on a startup activity until we know the object exists.

I hope that is clearer.

>>
>> From an information hiding point of view, this approach has a
>> certain appeal. In addition, so long as the higher-level service
>> APIs do not impose too many base ECF API-isms, it can be supported
>> without much impact on the base ECF API.
>>
>> *** One Factory, A Service Container ***
>>
>> The approach just mentioned gives me what I am looking for, but is it
>> really necessary? Why do we need to define two parallel sets of
>> launching machinery? The pattern is the same, so why do it twice?
>> The question I find I am asking myself in the end is how to achieve
>> my objectives and the ECF objectives without defining a lot of new
>> interfaces.
>>
>> I believe the answer lies in splitting ISharedObjectContainer into
>> two interfaces: one that has all the machinery currently supported
>> and another that has only the minimal set of methods needed to
>> activate the higher-level APIs. (In essence, something like
>> IServiceContainer above.) To keep things as close to the way they
>> currently are, let's call the minimalist interface
>> ISharedObjectContainer and the highly capable interface,
>> ISharedObjectContainerFull. (It's not the best name, but I hope you
>> get the idea.) Let's further stipulate that
>> ISharedObjectContainerFull extends ISharedObjectContainer.
>>
>> Now the ISharedObjectConatiner is indistinguishable from the
>> IServiceContainer above. I would still have the getService method on
>> it as the means to obtain higher-level services. Now I might add the
>> getAdapter method, not as a means to obtain a service, but as a means
>> to upgrade my capabilities from a simple ISharedObjectContainer to an
>> ISharedObjectContianerFull. If the container is willing to expose
>> the innards, I'll be given the desired object. If it is unwilling to
>> expose the innards, I will receive null.
>>
>> The key element of this approach is that it inverts the current
>> approach of the base ECF API. Whereas the current approach gives a
>> full container and allows one to get an adapter for a specialized
>> service. This approach gives a limited container and allows one to
>> get an adapter for the richer container. This allows one to block
>> the richer access if that seems like the right thing to do.
>>
>> *** What do I prefer? ***
>>
>> If there are four models on offer here, which do I prefer? The first
>> two are the simplest for the application developer, but they do not
>> separatie login from the higher-level service. I'm not yet sure
>> whether that is a problem, but I am tentatively inclined to dismiss
>> these approaches. Thus, the real question is whether to patch the
>> ECF by supporting some capability hiding or provide an alternate
>> higher-level model that sits above the ECF.
>>
>> In truth, I am torn. On the one hand, some urge for efficiency in me
>> is offended by the idea of a completely parallel, but higher-level
>> approach to service launching. On the other hand, I often find that
>> these urges for efficiency are exactly the things that lead me to
>> make things too complicated.
>>
>> Tentatively, I am inclined to think that if we define a parallel
>> model at a high-level, we can make sure that we design for ease of
>> use and simplicity without requiring that concrete constructs sit
>> next to abstract constructs.
>
>
> We could very easily break ISharedObjectContainer into super/sub
> interfaces and have them inherit from one another. I'm just not sure
> what the appropriate breakdown is. Please (everyone) let me know what
> you think about this and we can contemplate two interfaces related by
> inheritance rather than one.
>
I hope others will express an opinion. I am in favor of this.

> We can name things getService rather than
>
>> getAdapter, because we don't anticipate lots of other uses for this
>> pattern.
>
>
> I don't think we can reasonably use getService(). It would just be
> too confusing relative to org.osgi.framework.BundleContext.getService().
>
Is venue working for you? I still find it a little awkward, but it
grows on you.

> I'm open to other namings of this semantics (getAdapter), but I think
> it's convenient to use the IAdaptable pattern (and possibly be able to
> have implementations that actually use IAdaptable...e.g. eclipse
> resources (e.g. IResource)...
>
If venue were the right name, would a getVenue method be OK? How about
having it return an IVenue? In other words, the signature would be
something like:

public IVenue getVenue(Class venueInterface);

Like getAdaper you would pass in an interface that extends IVenue and
cast to the result to the same interface. A null return value would
indicate an unwillingness or inability to support the requested interface.

> And, we can use the 80/20 rule to avoid bringing in
>
>> capabilities that are only needed in obscure cases. In the end, we
>> are able to provide a simple model because the base ECF API is
>> available when much more is needed..
>
>
> I agree. I am willing to break down ISharedObjectContainer and have
> contemplated doing so...I'm just not sure what breakdown is best/most
> understandable (into more/less abstract interfaces...related by
> inheritance or delegation).
>
> Thanks,
>
> Scott
>
>
Re: Reflections on the presence API as a service or component interface. [message #582093 is a reply to message #581914] Wed, 08 June 2005 23:17 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

A few comments up front and others embedded.

1) As indicated in another posting, I agree that we should avoid the
term "service" for the reasons you state. As a place holder, I have
adopted the term venue to take it's place. It is not ideal, but I got
tired of searching for another name. Alternative proposals are welcome.

2) I am not advocating the use of CORBA, rmi, or other rpc-based
approaches. I am familiar with the problems that these approaches
introduce and I am not an advocate of these approaches. You correctly
point out that hiding all aspects of the distributed nature of the
offerings can be a dubious exercise, since it can establish expectations
about timeliness that cannot be met. I am not advocating using blocking
approaches even when there is a simple distributed query. I realize
this means that the replies for most methods will arrive on listeners
rather than in the return value of the method. I consider this
appropriate (and, yes, it is somewhat inconsistent with my desire for
familiar object models.)

3) I am advocating that we step back from Smack and the ECF and ask
ourselves what the objects are that an application developer will think
in terms of and build our interface around those objects. Right now I
am thinking in terms of IPresenceVenue, IPresentity (a term adopted from
SIMPLE that corresponds to a presence entity), IMyPresentity (a
specialization of IPresentity that allows me to control my appearance to
others), and IChat. I do not believe that the fact that the API is
asynchronous in nature forces us to have objects named sender or to
expose objects that correspond to messages. Having said that, you have
a good point that methods that do not act locally might benefit from
prefixes like "request" rather than the usual imperative sounding verb.
This can help to relocate expectations about where the reply will arrive
and prepare one for dealing with denial as a regular possibility.

4) I intend to honor your request to propose an alternative, but I want
to take a little more time on it. Sorry for the delay.

jfp

Scott Lewis wrote:

> Hi John,
>
> John F. Patterson wrote:
> <stuff deleted>
>
>>
>> Ok, let's go. I will organize my observations into three sections
>> characterized by the following headings:
>> 1) Object Models vs. Protocol Models
>> 2) The API as Training manual
>> 3) Should the application developer need to know much about the ECF?
>>
>> *** Object Models vs Protocol Models ***
>>
>> My single biggest complaint about the Presence API is that it allows
>> too many protocol details to leak through the API. I believe that
>> these higher-level APIs are our opportunity to offer a fuly
>> object-oriented approach to services and components and that we
>> should not be asking the application developer to think like a
>> protocol-level programmer.
>
>
> I agree that we should be trying to abstract protocol-level details
> away as much as possible, and it's certainly possible that the
> presence API can do more/better of that, but it's hard for me to
> imagine that *all* protocol aspects can be hidden...precisely because
> protocols at this level of abstraction (at the 'application' layer)
> have specific semantics...rather than being completely application
> generic (unlike the link layer, network layer, session layer, etc
> within the OSI reference model). The protocols themselves *provide*
> the presence sharing functionality.
>
I do not believe that I understand what you mean here. I think I agree
that the distributed nature of the API compels some patterns that might
not otherwise be needed, e.g., the heavy use of listeners.

>>
>> Let me give two examples of what not to do, drawn from the Presence
>> API. First, there are many places in the Presence API where shared
>> entities are identified by an ID. While this might be appropriate at
>> the ECF-level, I believe we should try to operate in terms of objects
>> in the Presence API.
>
>
> So I need to ask some questions about this idea/suggestion. One
> meaning to your thought could be the object references themselves are
> cross process (e.g. like rmi or other rpc implementations). That is,
> objects that represent things like state changes (like IRosterEntry)
> could be referred to with an object reference and those references
> could be passed around...so that I could be 'given' a reference to
> your presence state (e.g. when you arrived), and then 'consult' that
> present state to show things in the UI (i.e. access a method on 'your'
> presence object to show your presence state changing in my UI).
> First, I should ask: is this what you mean by 'operating in terms of
> objects'? It might not be, so please clarify if that's what you mean.
>
I am not advocating rmi or rpc-based approaches. I have no problem with
the use of string references at the base ECF-level. I feel these
references should be wrapped at higher-levels so that application
authors can deal with a familiar object reference rather than deal with
a string. This does not mean that these object references will have a
direct meaning on multiple machines.

> If this *is* what you mean, then I think this would/could be a very
> poor design as the basis for a multipoint real-time communications
> system. The reason I think this is *not* the way to go is that if
> object references are passed around directly across process it has a
> number of consequences: 1) an rpc mechanism has to be present to
> provide 'remote references' to objects, and these mechanisms are
> typically (although not necessarily) rather heavy weight in terms of
> both code size and runtime performance; 2) garbage collection becomes
> very complicated (see, for example, the problems with RMI); 3) failure
> handling becomes much more complicated (i.e. how does a direct object
> reference become 'stale'?...and how/when do remotes get notified when
> that object 'goes away'?); 3) the scaling and performance
> characteristics of such system can be very problematic (e.g. when is
> the last time you saw a real-time messaging system implemented with RMI)?
>
Whew ... I'm glad I'm not proposing that:-)

> The approach taken with ECF (and many other messaging systems) is to
> have object instances which are passed by *copy* rather than by
> reference. The IPresence instances are of this nature (e.g.
> IPresenceSender.sendPresenceUpdate(...)). Note this is conveyed in
> the IPresence declaration via having the IPresence interface extend
> Serializable.
>
That's fine, but why must I think of it that way as an appllication
developer? I want to go to my Presentity object and invoke a change
status method. If this is wrapped in some lower-level IPresence
instance that is copied among machines, that's fine. Why do I need to
think that way?

> Passing objects across process by value rather than by reference gets
> around all the nasty problems with object-reference passing systems
> (some described above)...particularly in the multipoint case (what is
> the effect of providing X references to my presence to remote users?
> What is the timing relationship of method calls on my remote object
> between those users?, Is there any local /replicated state associated
> with these proxy objects? If so, which state is local and which
> remote? How is such state kept consistent? If the authoritative copy
> of an object 'goes away'...e.g. user turns off their client...how does
> that information get conveyed to remote proxies?).
>
Again, I am not objecting to pass by value, but to forcing the
application developer to think that way. One thing I do not want to do
is to take nasty problems like state consistency maintenance or drop
detection and pass them along to the application developer. We are more
likely to know how to deal with these concerns than they are. The
concerns do not go away because we pass them along. What I want to do
is present to the application developer a model (in the MVC sense) that
they can attach to and create the desired venue. If there are error
conditions that we need to flag, let's try to detect them ouselves and
pass them along in an appropriate listener.

> The notion of having an ID for an object (like Presence info) allows
> it to be uniquely identified across processes. That is, even if there
> are two hundred copies of *my* presence info, it can have one globally
> unique ID that indicates that it is mine. It may be stale (i.e. it
> might be yesterday's presence state for me), but at least users of
> that copy of my presence know that it is mine without having to be
> connected to me. The ID (for users, services, containers, whatever)
> serves as a persistent, but fragile reference.
>
I am not arguning against the use of IDs beneath the venue API. I just
think most of the time we will want to wrap these IDs with an object.

> The Eclipse plugin model itself is filled with ids...e.g. ids for
> plugins...and this makes it possible to do things like have lazy
> loading for plugin code (i.e. only actually loading the plugin code at
> time of first usage). This is also a very helpful property for
> extensible distributed applications where clients/servers may need to
> run for a very long time, with all kinds of new object instances
> introduced by both the local user and remote user. Having an ID for
> the object (e.g. Presence) provided by you, for example, allows me to
> only load the code for the presence plugin if I happen to need it for
> my application.
>
> Thus, for example, instead of calling
>
>> sendRosterRemove on the PresenceSender with an ID associated with the
>> RosterEntry, why can't I just call a remove method on the
>> RosterEntry? When I send a presence update, why must I give a fromID
>> and a toID? We know the from ID; it's mine!
>
>
> This 'fromID' can probably be removed, although it may very well be
> the case that a given application that uses this api would want to
> actually be the 'source' of presence updates. In that case, it would
> make sense that the client application that uses IPresenceSender would
> want to specify which of the (local) users the presence update was from.
>
I assume this use case is something like a server that is keeping track
of presence for its users. I guess I think of this as one of those
obscure cases that would not make the 80/20 rule cut. I might define a
different API for use on servers or let people rely on the lower-level
API to achieve this kind of functionality. For most applications, the
ability to provide a fromID seems unnecessary or like an opportunity to
spoof.

> As for the toID, when I am truly updating
>
>> my presence (as opposed to negotiating subscriptions) is it even used?
>
>
> Well, if you are updating your presence relative to a specific target,
> then yes it would be used. That is, what if I want to only update my
> presence state for *you* (as opposed to all my other buddies?). It
> should be possible for me to specify a target for the presence
> update...even if the typical case is for me to send a presence update
> to 'everyone' (whoever is on my 'buddy list').
>
I understand this capability, but I question whether it is really
supported in Smack. To do this properly, you need to be able to place
subscribers into equivalence classes (multicast groups) so that it is
not necessary for the application to handle multicast. I question
whether we really want to model this capability. Even if we do, I doubt
this is how to model it. We might, for example, allow one to define
multiple IMyPresentity objects, one for each presence equivalence
class. Then, when a request for subscription arrives it would be
honored by associating it with one of the IMyPresentity objects. After
that updates to one of my presentities would be multicast to all the
subscribers associated with it.

>> (Frankly, the current collection of methods all looks like
>> opportunities to spoof becasue they require a fromID.)
>
>
> The fromID for this api is a user identifier (e.g.
> slewis@cerf.composent.com) rather than a client/process identifier
> (e.g. A12234@216.99.211.99). The user identifier specifies who on the
> local machine (if the application has/requires multiple users) is
> responsible for the message. Of course the local application that
> uses this API has to guarantee the validity of the userid if it
> has/uses several of them.
>
This feels once more like a server case and I am inclined to say it is
too esoteric for this API.

>>
>> Under most circumstances, an ID will be wrapped by some object (and
>> if it is worth talking about, it probably should be.) The
>> containerID is wrapped by the IPresenceContainer so we do not need
>> it. Methods that need it should either be on the IPresenceContainer
>> or pass in the IPresenceContainer. User IDs will usually be wrapped
>> by something akin to the IRosterEntry. Methods that need it will be
>> on these objects or pass them in. The one exception to this is when
>> we want to refer to an object that we do not yet have. In these
>> cases, the ID is akin to an address and must be provided usually for
>> the purpose of obtaining the object. After that, we will act on the
>> object.
>
>
> I agree. Actually, I think that wrapper classes should probably be
> created, and used in the IPresenceSender API (for example) rather than
> specifying things like I did. That is,
>
> IPresenceSender.sendPresenceUpdate(ID fromID, ID toID, IPresence
> presence)
>
> could/should probably be changed to something like
>
> IPresenceSender.sendPresenceUpdate(PresenceUpdate update);
>
> where PresenceUpdate is a (new) wrapper class. This is a matter of
> API taste, however, as I've had people complain about the need to
> create instances of wrapper classes for objects that are simply 'throw
> away'. Of course there is some a) programmer overhead for creating
> such instances; b) some runtime overhead (i.e. memory creation/garbage
> collection). BUT, I'm basically in agreement with you about the
> creation/use of such wrapper classes.
>
I don't think I am using the term "wrapped" as implying wrapper
classes. All I mean is that the application developer interacts with an
object that uses the ID to achieve its functionality. The ID is
contained by or "wrapped" within the object and need no longer be
referenced.

>>
>> In the Base ECF APIl, IDs are used to ensure flexibility about the
>> association of runtime objects to the shared abstractions. I believe
>> that these are obscure cases that higher-level APIs do not need to
>> support and should not support. The programming model for the
>> Presence API would be much simpler if IDs were only used when
>> essential (as addresses for unavailable objects). As it is right
>> now, these IDs will need to be wrapped by some application-side
>> object when there are perfectly good constructs within the API for
>> wrapping these IDs.
>
>
> Sure. I think you are making the same point as above...but not quite
> certain. If not then please clarify.
>
My guess is that I am not, but I am not certain either.

At this point my summary of this discussion goes as follows, you see
value in talking about IDs because that is the reality of how the
distributed objects are bound together. My reaction is that every time
you discovered a need for an ID, you probably have a distributed object,
so why not expose this as an object to the developer even if underneath
it is IDs that tie it all together.

I once read a paper (sorry, I can't remember the reference) that claimed
that every interprocess communication problem can be thought of as
either a problem in messaging or a problem in shared memory. The idea
was that the solution achieved by one approach always had a dual
solution using the other approach. It feels a little like you are
arguing for messaging models for interprocess communication and I am
arguing for shared memory approaches. I don't feel this implies rmi or
rpc. Perhaps, I am arguing for something more akin to Linda or JavaSpaces.

>>
>> My second example comes from the use of the IPresence object. If I
>> want to mark myself as do-not-disturb, I don't go to my IRosterEntry
>> and select the do-not-distrub method. Instead, I create an IPresence
>> object and mark it as do-not-disturb. This is klunky, non-intuitive,
>> and completely surprising to an application developer schooled in the
>> arts of OO programming. The verb, DoNotDistrub, is hidden as an
>> adjective on a noun, IPresence. I understand from a protocol-level
>> why this happens, but there is no reason why we should carry this
>> into our higher-level APIs. There should be a do-not-distrub or
>> changeStatus method either directly on the service or on an object
>> representing my RosterEntry that I can directly invoke.
>
>
> This can/could be added to the existing interfaces, but I think there
> are some reasons to reconsider from the point of view of programmer
> understandability.
>
> The thing that I think gets particularly confusing (to the programmer)
> about methods such as
>
> public void doNotDisturb(IPresenceSender sender) {
> // code here to construct a PresenceUpdate instance and send it
> // this could be implemented easily, BTW with current interfaces
> }
>
> is a) timing; b) failure handling. Does this method block? (because
> it requires 10,000 messages to be sent?). Doesn't that mean that the
> caller should 'know' that this method will take 10 orders of magnitude
> longer to execute than a method that just sets local state? What if
> the communication fails? Will the sender ever know about it?
>
You have an interesting point here. How do we ensure that a user
understands that this method is not synchronous, it can take time, and
there are failure possiblities. Having used many asynchronous APIs, I
can tell you the first thing I look for is a pairing of methods with
listener events. This would imply a need for a doNotDisturbSuccess and
a doNotDisturbFailure event. I'm not sure I want to argue for managing
things at this granularity, but it is a logical conclusion from the example.

Another way to help flag that a method is asynchronous is to use a
prefix that implies uncertainty about the outcome. For example, we
might want requestDoNotDisturb or proposeDoNotDisturb. Again, I am not
advocating these particular methods. I'm just trying to follow on on
your point.

I think these techniques can help deal with the asynchronous nature of
the methods (combined with good documentaion indicating where to look
for an outcome). With them, I believe we can put the methods on the
objects of interest without misleading the users. In any case, I still
find the alternative even more confusing, i.e., creating an IPresence
object, marking it as do-not-disturb and sending it out.

> Yes, the mapping wil be a little tougher for the
>
>> service or component implementer, but the resulting model will make
>> much more sense to the application developer.
>
>
> I'm not sure that's right. AFAICT, all network API efforts to
> 'abstract away' things like network failures, timing issues, etc. via
> pure replicated object abstractions have resulted in far more
> confusion...I think that when it becomes apparent (at run time) that
> the application programmer *really does care* about whether a given
> abstraction executes 10 orders of magnitude slower (i.e. blocks), or
> whether the communication itself fails. I'm thinking here of the
> research:
>
> http://www.cs.cornell.edu/vogels/papers/ew98.pdf
>
> and http://research.sun.com/techrep/1994/abstract-29.html
>
> To some degree, though, I agree with you...it *is* in some cases
> desireable to have an API that 'hides' from the programmer that there
> is network communication going on, but I think one has to do it very
> carefully, because things like timing and failure handling are
> *vastly* different and such APIs can be very surprising to users when
> their app just 'behaves differently' because network communication is
> going on underneath the covers...and that network communication has
> some characteristics that are very different from within-process
> behavior (e.g. partial failure basically doesn't happen).
>
I looked over the papers and I believe they are complaining about
CORBA-like functionality. That is not what I am advocating. I have
softened the statement of my position to align with yours regarding the
need for aynchronous APIs. Furthermore, I am not particularly troubled
by a listener on the venue (or is it the IVenueContainer) that lets me
know when the connection is lost. I agree that we will need to expose
the developer to this level of awareness. I just don't see why the
details of message passing and the mechanisms for binding (IDs) need to
be exposed so aggressively.

>>
>> I guess my first principle of higher-level API design is that these
>> APIs should be object-oriented, not protocol-oriented. If we lose
>> some capability along the way, so-be-it. I doubt we will lose
>> anything really critical. Frankly, if we want these APIs to be
>> protocol-neutral, we will need to avoid letting protocol-isms into
>> the APIs. Every time we do it, we are probably leaning towards one
>> protocol and away from another. Object models might just be our best
>> shot at a representation that is truly neutral with regards to
>> alternative protocols.
>
>
> I would like to see what you mean by 'object oriented'. Could you
> describe/present a simple Presence API that that you would consider to
> be object oriented in the sense that you are looking for?
>
Not now, but I will try to pull this together.

> The APIs that we have in ECF, like the presence API *is* object
> oriented in that all data structures are represented as objects. This
> is really valuable for encapsulation, security (type safety), etc.
>
> BUT, method calls on object instances don't necessarily correspond to
> network operations...and this is for several very good reasons as
> discussed above. This makes it slightly less object oriented in that
> we are not trying to make transparent the network operations
> (messaging...both sending and receiving)...rather we are making them
> explicit (by providing specific methods on objects that represent the
> communications session/container itself). For the reasons expressed
> in the papers above, I would be very hesitant to try to create
> abstractions that fundamentally hide the network, because it's been
> basically a dismal failure in terms of programmer adoption to this point.
>
I am not advocating CORBA, rmi, or any other rpc approach. Since it is
now clear that these techniques are what came to mind by my discussion,
it seems fair to say that I overstated my case. What I am advocating is
much simpler. Try to think about the Presence API from the point of
view of someone who will implement an application. They want a model to
tie their UI to. Let's give them that model and only expose those
aspects of the underlying distributed reality that are unavoidable,
e.g., asynchronous method calls, network failure events.

>>
>> *** The API as Training Manual ***
>>
> <stuff deleted>
>
>>
>> Let's look at some of the aspects of the Presence API that could bear
>> improvement. First, there is the issue of naming. Just because the
>> ECF uses the name container, we have no obligation to use that name
>> for the top level of the Presence API. For my money, we would be
>> better off calling the IPresenceContainer, the IPresenceService.
>> Admittedly, the term service is not enormously more precise than
>> container, but I think it is somewhat better. In the ECF there will
>> be many forms of containers some of which will feel like services and
>> some of which will feel like components, e.g., an online meeting. I
>> am inclined to use the term service for those cases when the
>> container encapsulates a capability from login to logoff.
>
>
> OK. I'm more than OK with renaming based upon consensus about
> appropriate names. The one thing about naming that I've found is that
> mine (or anyone's) rarely are universally agreed upon initially.
>
> With IPresenceContainer I was trying to introduce the notion that
> extensions of base container functionality (represented by
> ISharedObjectContainer) would have I<semantics>Container rather than
> to introduce a new concept ('service'), BUT I'm OK with the notion of
> having adapter interfaces that are extensions of the basic container
> be referred to as 'services'. OTOH, I think we might be a little
> careful here...because the notion of having a service is already quite
> well defined in the OSGI component model, and it might be best to
> *not* use that word (service) unless it's associated with the same
> kind of behavior implied by the OSGI service model (i.e. it's really
> an OSGI 'service'). If we used service in a completely different way
> thatn OSGI did (e.g. see org.osgi.framework.*) this might be *very*
> confusing.
>
Your concern about the term "service" was noted and I am trying
"venue". Is it working?

>
>>
>> Second, why are there so many interfaces in the Presence API?
>> Putting aside the listeners, it seems like there only need to be
>> three main interfaces: IPresenceService, IRosterEntry, and IChat
>> (which isn't there.)
>
>
> aka IMessageSender/Listener
>
> If we throw in IRosterGroup, we have four.
>
> With listeners corresponding to both, we then have a total of 8. In
> the existing API there are a total of 10...an additional interface
> IAccountManager, which provides some 'meta operations' for user
> account management (e.g. password changes, etc). Basically this
> brings it up to ~10. The account management utility interface could
> be consolidated (i.e. just made a part of IPresenceContainer), but
> this kinds of complicates the core interface IPresenceContainer with a
> number of functions that are relatively peripheral.
>
>
> As mentioned above,
>
>> IPresence is primarily a messaging format that should be expanded
>> into more methods on the primary objects. IPresenceSender,
>> IMessageSender, and IAccountManager do not seem necessary. Their
>> methods could safely be moved onto the IPresenceService without loss
>> of clarity.
>>
>> I am amenable to this form of additional control objects if it serves
>> some purpose, but I don't see the purpose in the Presence API. It
>> can't be to reduce the method count to something tractable.
>> altogether they add 11 methods, seven of which come from the
>> AccountManager. So leave the account manager separate and bring in
>> the others. It will make it easier to see all the service level
>> methods in one place. Moreover, pulling out IPresenceSender and
>> IMessageSender as distinguished seems like a continuing nod to the
>> protocol-level design.
>
>
> I'm amenable to consolidation...but I think there is good reason (for
> 'semantic coherence') to have things grouped a little bit. I
> personally despise interfaces that have all kinds of methods...from
> soup to nuts...which I then have to wade through to figure out which
> are important. I think that others have these concerns as well. BUT
> like I said I'm open to some consolidation upon lines that the
> community finds appropriate/desireable. Also with the IAccountManager
> there is an issue of access control/permission and, in fact,
> 'optional' functionality (IAccountManager) vs. required functionality
> (IPresenceContainer).
>
You probably have a good point here. Putting aside IAccountManager, I'm
not sure I would achieve much reduction in interfaces. It's not a
one-for-one mapping, but your IPresenceSender would turn into my
IMyPresentity, your IMessageSender would turn into my IChat, and your
IRosterEntry would turn into my IPresentity. Maybe IPresence would go
away. My complaint is probably less with the number of interfaces than
with how the problem is factored.

>>
>> Finally, one quick comment. When I argued with Li-Te Cheng for
>> flattening the IAccountManager into the IPresenceContainer, he
>> suggested that one reason to keep it separate might be to be able to
>> return null if you lack permission to manage accounts.
>
>
> Yes.
>
> This makes sense, so I
>
>> relented in this case, only to notice later that the IAccountManager
>> contains both administrator functions and the changePassword method
>> that everyone will need to access. Even if Li-Te is right, we need
>> to get the changePassword method out of the IAccountManager and onto
>> the IPresenceContainer.
>
>
> True enough. I would be fine with moving such methods around if we
> keep the separation between these interfaces.
>
>>
>> I guess my second design principle is to avoid allowing the base ECF
>> API or other models. like Smack lead us astray. For higher-level
>> APIs, we still need good OO design that uses clear and precise
>> language, avoids excessive interfaces, and factors things sensibly.
>
>
> I agree completely!! I would like to understand better, however, what
> you mean by 'good OO design'...as I think the key thing to figure out
> is *which* OO concepts are appropriate for a network API...e.g. data
> structures represented as classes, serializability, class/method
> naming conventions, inheritance and/or delegation for extensibility,
> adapters, etc are just great for me. My main concern about your
> points above is that *if* we try to create an abstraction that
> gives/attempts to give network transparency that we might be going
> down a well-worn and poor path. BUT, I do agree that we can introduce
> more/other OO concepts short of network transparency...and I'm open to
> any such detailed ideas there.
>
I wish I were doing a better job of suggesting principles of good OO
design, but I fear all I am really saying is that the API should be more
like the way I think about the problem. If that is the case, then it is
important that I propose an alternative. I'll work on that.

For now, all I can say is, try to think about this like an application
author. What are the nouns? What are the verbs? etc. If the realities
of distributed computing take us away from that point of view, ok? But,
let's at least start from that viewpoint.

>>
>> *** Should the application developer need to know the ECF? ***
>>
>> Why should I need to know a complex, general purpose API, like ECF,
>> to use a specialized, simple API, like the Presence API? I don't
>> think I should have to know the base ECF API to use its higher-level
>> APIs. This has several implications for the Presence API. I'll
>> discuss only one here and expand on this theme further in my next
>> posting.
>
>
> Well, here's a minimal use of the presence API...and this doesn't
> require much knowledge of the core API:
>
> ISharedObjectContainer container =
> SharedObjectContainerFactory.makeSharedObjectContainer();
>
> IPresenceContainer presence =
> container.getAdapter(IPresenceContainer.class);
>
> // Use presence API in any way desired here
>
> If you are looking for something shorter it would be easy to do
> something like this (which I contemplated adding to the presence plugin):
>
> IPresenceContainer presence =
> PresenceContainerFactory.makePresenceContainer();
>
> // Use presence API in any way desired here
>
> This might be good...and would be trivial to implement. Actually, I
> was thinking of using the Eclipse adapterfactory to do this without
> even needing a new factory class.
>
It is not just a question of what you can do, but also a question of
what you are advocating via your examples. When someone creates the
next venue (not presence) do you want them to create another venue
factory or do you want them to plug in somewhere? If they want to run
two venues off the same login, how will that be achieved? I'm not sure
what is the best way to do it, but we have an opportunity to think it
through and steer people towards a prefereable approach through our
examples.

>>
>> I question whether it is advisable to expose ECF functionality
>> through the Presence API. This shows up in the form of the
>> makeSharedObjectContainer and addSharedObjectListener methods on the
>> IPresenceContainer. These methods do not seem to be essential to the
>> Presence Service and could be accomplished by a knowledgable
>> developer by working directly with the base ECF API. For any newbie
>> application developer exploring the Presence API, however, they pose
>> a problem. They draw the application developer into the base ECF API
>> for fear that it is critical to the operation of the Presence API.
>> This simply leads them astray. By keeping unnecessary ECF features
>> out of the Presence API, we can help the application developer keep
>> focused on what the presence model really is.
>
>
> Well, I think most of the functions exposed by ISharedObjectContainer
> *are* in fact critical to most real applications...i.e. those that
> need to dispose the container when done with it, need to 'disconnect'
> (leaveGroup) and 'reconnect' (joinGroup) via user actions and/or
> network actions. With the possible exception of
> getSharedObjectManager(), I think that the basic container semantics
> are pretty necessary for most/many real apps...even those that only
> use IPresenceContainer.
>
I think this has been covered in another thread. I do not believe most
higher level uses of the ECF will need direct access to the
ISharedObjectContainerConfig, the ISharedObjectManager, or the
GroupMemberIDs. These will be used by the venues as part of their
implementation, but they do not need to be exposed to most users of
those venues.

>>
>> So, this is my third design principle: keep unnecessary ECF elements
>> out of the higher-level APIs.
>
>
> I think this is, in general, a good principle, but I'm not sure if I
> agree with your assessment of ISharedObjectContainer in this case.
> OTOH, I'm OK with models where access to the ISharedObjectContainer
> reference directly are 'hidden' by a given provider. But I think that
> WRT methods like 'joinGroup' and 'leaveGroup' and several others in
> ISharedObjectContainer we would quickly find that any application
> would require these to be made available so that applications can
> control such operations.
>
Like I said this is probably better discussed in another discussion thread.

> Thanks again for the comments. I'll respond to your other posted
> comments and your/the community's comments in response to this note as
> quickly as possible. Please be patient and we'll work through all of
> these things to get to an much improved API.
>
> Scott
>
Re: Some thoughts about how much the ECF should show through to services and components. [message #586824 is a reply to message #582054] Fri, 17 June 2005 07:05 Go to previous messageGo to next message
Peter Nehrer is currently offline Peter NehrerFriend
Messages: 241
Registered: July 2009
Senior Member
John, Scott,

I have a couple of thoughts that are not directly related to Presence
API, but I think they're relevant to this discussion nevertheless.

First, I'd like to try to rescue the term "service". It is such a
generic term that it would be a shame to give it away to OSGi. As far as
I understand, services in OSGi are basically public interfaces that a
bundle exposes. Bundles publish services in a common registry, which can
then be queried by other bundles (and service references obtained).
Services are what bundles do.

An ECF container, on the other hand, is not a bundle. The services it
may or may not provide are not in the same category as OSGi bundle
services. Typically, a conversation is carried in one of the two
contexts, so it shold not cause much confusion, and in the cases that it
might, we could say "ECF service" vs. "OSGi service". I don't really
have strong feelings toward one term or another and I'll get used to any
one we settle on, but I thought I'd bring this up.

Now, about the getAdapter(Class) method (and its clones): In Eclipse,
and I think this would extend to any component-based architecture, the
adapter mechanism essentially allows downstream plugins to tack on new
interfaces to objects originating in upstream plugins. For example, you
may have a plugin org.eclipse.ecf.provider.foo, which depends on
org.eclipse.ecf.provider (the Generic Provider) and implements the Foo
API specifically for that provider (i.e., it registers its own class as
the container's IFoo adapter). Now, some other plugin, say
org.eclipse.ecf.example.collab, is using a container (but doesn't know
or care which one it is) and also wants to use its Foo API. So it asks
the container for it by calling getAdapter(IFoo.class) and gets the
implementation from org.eclipse.ecf.provider.foo. As you can see, that's
the only implementation it can get -- it doesn't get to parameterize its
selection in any way. It also doesn't care if it's the container itself
that implements Foo API, or some other plugin that does it on its
behalf. As a result, this approach is not really an abstract factory
pattern substitute.

On the other hand, I'm not sure that there's a need for a generic "venue
factory". Assuming that something like the Presence API could have
generic implementations (and I'm not saying that it is or isn't the
case), you could simply have a PresenceFactory, or PresenceRegistry,
which would manage all available implementations. Once you get an
implementation, you could activate/deactivate it in a container instance
(this is what I'd call an external service -- the container
implementation knows nothing of it). It could get repetitive with more
and more services coming around, and it's tempting to try to generalize
it at the outset, but is there a fundamental need?

About splitting of the interfaces: You'll notice that
ISharedObjectContainer basically provides the group membership API,
while ISharedObjectManager provides the container content management
part. On the other hand, ISharedObjectContext doesn't have that
separation. The two are fundamentally different in that when using
ISharedObjectContainer, you are looking into the group communication
system from the outside (i.e., you "own" it); when using
ISharedObjectContext, you are already participating in it (i.e., it owns
you). While it's very enabling, it may be trouble to let shared objects
join/leave the group as they please -- it affects all other shared
objects. I'd take those methods out and put them into another interface,
which you could obtain by calling a getter method (and you may or may
not get it based on your permissions).

More later. I'm working on the DataShare service (docs TBD), which is
where I draw my ECF lessons learned, and some similar issues have surfaced.

Thanks!

--Peter
Re: Some thoughts about how much the ECF should show through to services and components. [message #586833 is a reply to message #586824] Fri, 17 June 2005 19:06 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John/Peter,

Peter Nehrer wrote:
> John, Scott,
>
> I have a couple of thoughts that are not directly related to Presence
> API, but I think they're relevant to this discussion nevertheless.
>
> First, I'd like to try to rescue the term "service". It is such a
> generic term that it would be a shame to give it away to OSGi. As far as
> I understand, services in OSGi are basically public interfaces that a
> bundle exposes. Bundles publish services in a common registry, which can
> then be queried by other bundles (and service references obtained).
> Services are what bundles do.
>
> An ECF container, on the other hand, is not a bundle. The services it
> may or may not provide are not in the same category as OSGi bundle
> services. Typically, a conversation is carried in one of the two
> contexts, so it shold not cause much confusion, and in the cases that it
> might, we could say "ECF service" vs. "OSGi service". I don't really
> have strong feelings toward one term or another and I'll get used to any
> one we settle on, but I thought I'd bring this up.

I also don't have particularly strong feelings about what ECF 'services'
are called. I just don't want to cause confusion by overloading an
already overloaded term. I do think that it would be nice to somehow
use the concepts of a 'containerservice' or 'shared object service' somehow.

>
> Now, about the getAdapter(Class) method (and its clones): In Eclipse,
> and I think this would extend to any component-based architecture, the
> adapter mechanism essentially allows downstream plugins to tack on new
> interfaces to objects originating in upstream plugins. For example, you
> may have a plugin org.eclipse.ecf.provider.foo, which depends on
> org.eclipse.ecf.provider (the Generic Provider) and implements the Foo
> API specifically for that provider (i.e., it registers its own class as
> the container's IFoo adapter). Now, some other plugin, say
> org.eclipse.ecf.example.collab, is using a container (but doesn't know
> or care which one it is) and also wants to use its Foo API. So it asks
> the container for it by calling getAdapter(IFoo.class) and gets the
> implementation from org.eclipse.ecf.provider.foo. As you can see, that's
> the only implementation it can get -- it doesn't get to parameterize its
> selection in any way. It also doesn't care if it's the container itself
> that implements Foo API, or some other plugin that does it on its
> behalf. As a result, this approach is not really an abstract factory
> pattern substitute.

I think the adapter mechanism is particularly useful for having runtime
addition...along with a general query. And it's got the added benefit
of being used (heavily) already within Eclipse itself.

One thought that I've had (but not yet persued) is the use of the
AdapterFactory extension point in the platform. This *may* provide a
way to introduce other/more specific factories, all based upon the
adapter pattern, but without the extra overhead introduced by the
getAdapter patter (i.e. with the getAdapter call, followed by check for
null and cast to appropriate interface).

>
> On the other hand, I'm not sure that there's a need for a generic "venue
> factory". Assuming that something like the Presence API could have
> generic implementations (and I'm not saying that it is or isn't the
> case), you could simply have a PresenceFactory, or PresenceRegistry,
> which would manage all available implementations. Once you get an
> implementation, you could activate/deactivate it in a container instance
> (this is what I'd call an external service -- the container
> implementation knows nothing of it). It could get repetitive with more
> and more services coming around, and it's tempting to try to generalize
> it at the outset, but is there a fundamental need?
>
> About splitting of the interfaces: You'll notice that
> ISharedObjectContainer basically provides the group membership API,
> while ISharedObjectManager provides the container content management
> part. On the other hand, ISharedObjectContext doesn't have that
> separation. The two are fundamentally different in that when using
> ISharedObjectContainer, you are looking into the group communication
> system from the outside (i.e., you "own" it); when using
> ISharedObjectContext, you are already participating in it (i.e., it owns
> you). While it's very enabling, it may be trouble to let shared objects
> join/leave the group as they please -- it affects all other shared
> objects. I'd take those methods out and put them into another interface,
> which you could obtain by calling a getter method (and you may or may
> not get it based on your permissions).

RE: ISharedObjectContext. Yes, the ISharedObjectContext could (and
perhaps should) be broken up into two interfaces (analogous to
ISharedObjectContainer and ISharedObjectManager). This would be an easy
change and might provide a good deal more consistency.

One thing to point out though...the ISharedObjectContext instance
provides a way for providers to build in security checks (i.e. runtime
permissions checks) into the calls to ISharedObjectContext (e.g.
joinGroup/leaveGroup). I'm expecting that some providers will have
permissions associated with these operations, and that those permissions
will be associated with the shared object upon construction. Then the
permissions can/will be checked at method call time and if not allowed
for a given shared object instance will result in SecurityExceptions.
This way providers can define the runtime constraints on the shared
objects that they contain, and enforce those constraints with the java2
security mechanisms (if the provider implementation so desires).

It still may be a good thing to separate ISharedObjectContext into two
interfaces as Peter describes, but I don't think it will be critical for
security per se.

Thanks. With some consensus on these issues I think it will soon make
sense to change some of the API designs to support e.g. creating a
super-interface for ISharedObjectContainer (to hold the even more basic
container semantics), and ISharedObjectContext as Peter describes.

Also we can easily migrate the presence API forward with some more input
from John and Li Te about design considerations for the presence API
specifically.

I would also request that people begin looking at the discovery API for
critique/thoughts (org.eclipse.ecf.discovery). The intention here is to
have (similar to the presence API) an abstract definition of basic
network discovery services, and then have ECF providers that implement
those services using underlying protocols (in this case, we've been
working on one based upon zeroconf/bonjour initially).

Same thing with the datashare service I expect...i.e. an abstract
capability/service with multiple possible implementations...but Peter
will correct me if this is not a fair characterization of datashare.

Thanks,

Scott
A Componentry Pattern [message #586842 is a reply to message #586833] Mon, 20 June 2005 22:38 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott, Peter:

I think that one of the most important aspects of the ECF is the manner
in which it supports componentry. By this, I mean Peter's concept of a
downstream extension or a plug-in. One of my objecives in a component
model is to allow the component developer to expose only what he or she
cares to expose, while building on a rich platform of capabilities. In
other words the component is a mapping from the rich capabilities onto a
specialization and the component author should not be compelled to
expose more than he or she wants to.

I am not that familiar with the eclipse plug-in mechanisms, so I seek
your guidance about the best way to do things. I have proposed a
particular approach to extensions in a different context, but I am not
sure how well it conforms. I thought I would just describe the
mechanism and let you tell me the best way to do this in eclipse.

For concreteness, I will assume we are interested in supporting
alternative "venues" as extensions of the ISharedObjectContainer.
(BTW: I'm ready for ECFService whenever you are:-) I'm not proposing
this as an API as much as I am using it to illustrate the pattern.

Here goes:

First, let's assume we have an IVenueContainer that does little more
than what we think every such container will do. In other words,
public interface IVenueContainer {
public void login(...);
public void logoff();
public IVenue getVenue(Class venueInterface);
public Object getAdapter(Class subInterface);
}
Let's assume we can get one of these from a singleton named
IVenueContainerFactory that completely mimics the pattern of
ISharedObjectContainerFactory.

Now, when an IVenueFactory is created it will recruit an
ISharedObjectContainer to do its work, but the application developer
doesn't need to know that. That's for the component author to know
about. Conceivably, some IVenueContainers might want to support casting
to a more specialized form of IVenueContainer. I have included
getAdapter for this. I do not really expect it to be used to get venues.

To obtain one of the venues supported by this IVenueContainer, we call
getVenue. Like the adapter pattern, we provide the interface of the
venue type we desire and we will cast to that interface when we get the
return value, e.g., presenceVenue = (IPresenceVenue)
getVenue(IPresenceVenue.class);

The fact that the return value from getVenue is IVenue is not and should
not be very constraining. It simply imposes on authors that they
provide certain basic functionality. For example, we might have:
public interface IVenue {
public ID getID();
public void activate();
public boolean isActive();
public void deactivate();
}
The purpose of IVenue is to define a polymorphic type, not to impose
very many requirements for that type. The requirements should be the
lowest common denominator of whatever we think any IVenue will need to
provide. If we do not want to impose any requirements, I would propose
that we create an interface with no methods.

Now comes the interesting part. We are asking IVenueContainer for an
IVenue that must build on its capabilities, but not be required to
expose any more than the component builder deems appropriate. Put
another way, our new IVenue must have access to something about the
innards of IVenueContainer, but without exposing it.

Do do this, I assume a registry of IVenueFactories. Presumably this is
a singleton that IVenueContainer kinows about. Moreover, I assume that
the registry records an association of an IVenueFactory with an IVenue
interface. When the IVenueContainer recieves the IVenue request, it
finds the registry and gets the appropriate factory. Now, let's imagine
that the factory looks like this:
public interface IVenueFactory {
public Class getKey();
public IVenue createVenue(ISharedObjectContainer soc) throws
VenueFactoryException;
}
The factory is prepared to create something of type IVenue, but first it
wants access to the implementation platform upon which it will build.
In a sense, it "wraps" itself around the implementation core and
provides access to it according to its own rules (e.g., its own
interface). The implementation upon which it builds remains opaque to
the application developer who chooses to use this venue. The platform
can be a "black box" and the component that extends it can be a "black
box". If the factory requires a specialization of
ISharedObjectContainer (such as to do something implementation specific,
it will call the getAdapter on ISharedObjectContainer and throw an
exception if it cannot get what it needs.

This is the component approach that I would advocate for the ECF. It
means that every time we see an opportunity for extension, call it XXX,
we do the following:
1) Create a highly generic IXXX to represent the lowest common
denominator of capability.
2) Add a getXXX method to whatever interface will be the logical
container for the new XXX capability.
3) Provide a generic factory interface of type IXXXFactory that supports
creating the new capability around a provided infrastructure object.
4) Provide a registry for factories of type IXXXFactory.
I think the infrastructure object that is wrapped by the new object will
vary depending on the purpose of XXX. Sometimes it will be
ISharedObjectContainer, sometimes ISharedObject, and other times a
derived object from some higher level API.

Notice that I am treating getAdapter as a way to get alternate forms of
an object, not as a way to obtain the extensions.

Now, if this conforms to the eclipse plug-in model and we are willing to
rely on it, then I can get what I want. Otherwise, I would advocate
that we start creating the mechanisms to support this kind of plug-in.
In either case, we should start demonstrating the pattern in the way we
build components on top of the ECF.

Let me know what you think.
Re: A Componentry Pattern [message #586855 is a reply to message #586842] Tue, 21 June 2005 03:48 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

I'm going to intersperse my comments in John's posting...just to try to
keep things manageable.

John F. Patterson wrote:
> Scott, Peter:
>
> I think that one of the most important aspects of the ECF is the manner
> in which it supports componentry. By this, I mean Peter's concept of a
> downstream extension or a plug-in. One of my objecives in a component
> model is to allow the component developer to expose only what he or she
> cares to expose, while building on a rich platform of capabilities. In
> other words the component is a mapping from the rich capabilities onto a
> specialization and the component author should not be compelled to
> expose more than he or she wants to.

I agree strongly. The notion of supporting 'communications componentry'
is really what ECF is going after.

The trick here, I think, is mapping various notions of extension and
specialization onto the mechanisms provided by java/java classes, by the
OSGI and Eclipse runtime, and by ECF API itself. So, for example, using
just java it's possible to extend the functionality of a class (e.g. a
container in ECF parlance), but with java this results in a compile-time
dependency. Further, it's possible to define a plugin with an extension
that extends an extension point exposed by another plugin...but this
(typically) results in a plugin 'resolve' (aka load) time dependency
between plugins. Even further a mechanism like getAdapter(<interface>)
or the OSGI 'service lookup' interfaces (on
org.osgi.framework.BundleContext) provides runtime query capabilities.

It's my expectation that ECF will need to use some/all of these
mechanisms to provide a range of ways for infrastructure/protocol
providers to extend ECF itself, for communications component developers
to create new 'building block' components and also to create 'aggregate'
components, and for client/application programmers to be able to use,
extend, and customize existing components.

>
> I am not that familiar with the eclipse plug-in mechanisms, so I seek
> your guidance about the best way to do things. I have proposed a
> particular approach to extensions in a different context, but I am not
> sure how well it conforms. I thought I would just describe the
> mechanism and let you tell me the best way to do this in eclipse.

The basic mechanisms introduced by the OSGI model are the 'bundle' (all
eclipse plugins are bundles), and the 'service'. In 'pure' OSGI (not
necessarily eclipse), bundles have a lifecycle (installed,
resolved/loaded, start, stop) that is provided by the framework itself.
Also bundles are provided with a way to register and query the
framework about the existence/availability of 'services' which other
bundles have registered with the framework.

In Eclipse, bundles/plugins also expose 'extension points'...and other
plugins provide 'extensions' of those extension points. Typically
extension points are named references that are resolved by the
framework...frequently at plugin load time (plugins are loaded lazily by
the framework, so that they don't take up memory unnecessarily). So an
extension point defines a java interface that it will accept, and the
extension provides an implementation class for that interface (which
must implement that interface, of course) and declares the class as an
extension within it's plugin.xml. When the extension plugin is loaded
an instance of the implementing class will be created and made available
to the extension point plugin (by Eclipse).

An ECF example of the usage of the extension point/extension mechanism
is the container factory. ECF has defined an extension point that
delegates the construction of new containers to extension plugins, so
that support for completely new types of containers can be introduced
without modifying ECF at all. All the extension plugin creator must do
is a) define an implementation of the ISharedObjectContainer interface;
b) declare a class that implements
org.eclipse.ecf.core.provider.ISharedObjectContainerInstanti ator, c)
declare this implementing class as providing an extension to the ECF
extension point. Then, when a client creates a container instance (via
SharedObjectContainerFactory), the class from 'a' is returned to the client.

>
> For concreteness, I will assume we are interested in supporting
> alternative "venues" as extensions of the ISharedObjectContainer.

Although I'm OK with interfaces like 'venues' being extensions of
ISharedObjectContainer (as in 'public interface IVenue extends
ISharedObjectContainer'), there are good reasons to consider using the
runtime mechanisms like ISharedObjectContainer.getAdapter(IVenue.class).
1) If everything extends ISharedObjectContainer (and then extensions
of IVenue, etc. etc) things can be quite 'brittle' in that any changes
to such interfaces can/will result in the need to recompile everything;
2) the extends relationship might not really be the most natural model
for the relationship between IVenues and ISharedObjectContainer...that
is the 'isa' relationship (inheritance) might not be as appropriate as
the some other relationship (e.g. composition/delegation).

So, for example, the IPresenceContainer interface (in
org.eclipse.ecf.presence.IPresenceContainer) does not inherit from
ISharedObjectContainer. I am still struggling with this but I think
it's probably the right choice. But, as a consequence, it does require
the programmer to use the following idiom to get at the
IPresenceContainer functionality:

ISharedObjectContainer cont =
SharedObjectContainerFactory.createSharedObjectContainer("presencecontainer ");

IPresenceContainer presenceContainer = (IPresenceContainer)
cont.getAdapter(IPresenceContainer.class);

if (presenceContainer != null) {
// use it!
} else {
// we don't have a valid presence container
}

This is (of course) more code than:

IPresenceContainer pc = (IPresenceContainer)
SharedObjectContainerFactory.makeSharedObjectContainer("presencecontainer ");

But the intended benefit here is that a) IPresenceContainer has no
dependency on ISharedObjectContainer and so it is less 'brittle' because
of the lack of compile-time dependencies; and b) implementers if
IPresenceContainer can be added/composed onto existing classes by simply
changing the implementation of getAdapter(...). Thus it's (hopefully)
possible to have greater code reuse (e.g. people can reuse the
implementation classes for ISharedObjectContainers in the
org.eclipse.ecf.provider plugin and 'add on' IPresenceContainer
functionality easily. This is what I do, for example, in the
org.eclipse.ecf.provider.xmpp plugin, which defines an xmpp-based
implementation of IPresenceContainer.

So...to sum up...I think the design choices in interface inheritance vs.
composition/delegation are potentially kind of subtle...and interface
inheritance is not always the best answer...even if it has some 'cost'
in complexity/lines of required code. I'm sure this isn't
controversial, but stating the obvious is sometimes useful...at least to
me :-).


> (BTW: I'm ready for ECFService whenever you are:-) I'm not proposing
> this as an API as much as I am using it to illustrate the pattern.
>
> Here goes:
>
> First, let's assume we have an IVenueContainer that does little more
> than what we think every such container will do. In other words,
> public interface IVenueContainer {
> public void login(...);
> public void logoff();
> public IVenue getVenue(Class venueInterface);
> public Object getAdapter(Class subInterface);
> }

I would argue that everything you are looking for as you've defined
IVenueContainer is actually already there in ISharedObjectContainer:

public void login() --> public void joinGroup(ID,IJoinContext);
public void logoff() --> public void leaveGroup();
# The reason these are named joinGroup/leaveGroup rather than
login/logoff is that the 'joinGroup' notion is intended to include both
'connect to group' and 'login to group' semantics (since these need to
be tightly bound for security)
# continuing...
public Object getAdapter(Class) --> public Object getAdapter(Class);
public IVenue getVenue(Class) --> public Object getAdapter(Class);

It seems to me that public IVenue getVenue(Class) is completely
redundant to public Object getAdapter(Class). The typical contract of
the getAdapter (IAdaptable in Eclipse parlance) is that the (non-null)
Object returned from the getAdapter call *must* be castable to the java
interface class provided as the param to the getAdapter call. So the
getVenue method would be trivially implemented as:

public IVenue getVenue(Class mustbeivenuesubclass) {
return (IVenue) super.getAdapter(mustbeivenuesubclass);
}

One way to address your previously articulated concern about the
complexity of the ISharedObjectContainer interface (i.e. too many
methods) would be to create a super interface (call it IContainer):

public interface IContainer {

public void joinGroup(ID,IJoinContext);
public void leaveGroup();
public Object getAdapter(Class intf);

}

public interface ISharedObjectContainer extends IContainer {
// All current ISharedObject methods *except* the three defined above
}

This would be a fine thing to do (and I'm certainly willing to do it),
but it seems to me it's kind of unnecessary. That is,
ISharedObjectContainer has the following methods (in addition to
joinGroup,leaveGroup,and getAdapter methods):

addListener/removeListener (useful for getting notification of container
events)
getConfig, dispose (necessary for managing the lifecycle of the
container itself)
getGroupID,getGroupMemberIDs,isGroupManager (basic query interfaces
about the current group membership...important for reliability)
getSharedObjectManager (to get the ISharedObjectManager reference)

So I would argue that *if* we had an IContainer super interface for
ISharedObjectManager that we would soon find that we would need to 'move
up' the addListener/removeListener,getConfig/dispose and probably
ultimately the getGroupID,getGroupMemberIDs,isGroupManager methods into
IContainer...and that would leave just getSharedObjectManager as defined
in ISharedObjectContainer.

So that's my concern about adding a super interface to
ISharedObjectContainer...that we would end up just moving nearly
everything in ISharedObjectContainer into that super interface and not
have added anything by having another layer in the interface hierarchy.

Now one thing I should add...of course, if we want to add an
IVenueContainer as a subclass of ISharedObjectContainer that's perfectly
fine...but I would say that it would not be very useful as a distinct
interface with the semantics described above, because it overlaps so
strongly with what ISharedObjectContainer already provides.


> Let's assume we can get one of these from a singleton named
> IVenueContainerFactory that completely mimics the pattern of
> ISharedObjectContainerFactory.
>
> Now, when an IVenueFactory is created it will recruit an
> ISharedObjectContainer to do its work, but the application developer
> doesn't need to know that. That's for the component author to know
> about. Conceivably, some IVenueContainers might want to support casting
> to a more specialized form of IVenueContainer. I have included
> getAdapter for this. I do not really expect it to be used to get venues.

I would be completely fine with this. I'm wouldn't/not going to argue
that we should have only one SharedObjectContainerFactory. Actually,
there can easily be patterns like this:

public interface IFooContainer {
// whatever
}

public class FooFactory {
public static final String FOO_TYPE = "whatever";

public static IFooContainer makeFooContainer() {
ISharedObjectContainer cont =
SharedObjectContainerFactory.makeSharedObjectContainer(FOO_T YPE);
IFooContainer fooContainer = cont.getAdapter(IFooContainer.class);
if (fooContainer == null) throw new Exception("doesn't have IFoo
nature"); <-- this is not the right exception type but anyway
return fooContainer;
}

I would expect that some ECF plugins would have their own factories and
to the work of the construction/casting for the programmer. In fact, it
would be really good (I think) if when constructing a plugin developers
could choose to create a plugin from an ECF template, and the above
factory class/methods could/would be created automatically for them.

>
> To obtain one of the venues supported by this IVenueContainer, we call
> getVenue. Like the adapter pattern, we provide the interface of the
> venue type we desire and we will cast to that interface when we get the
> return value, e.g., presenceVenue = (IPresenceVenue)
> getVenue(IPresenceVenue.class);

So I still see this as redundant to Object getAdapter(Class), but I
guess you are saying that getVenue (because the method name is specific)
would be more understandable than getAdapter() (as getAdapter is more
abstract?). Is this right? I don't know how comfortable
people/programmers would be with the propgation of lots of individually
named 'interface query' mechanisms that all do similar/identical things.

>
> The fact that the return value from getVenue is IVenue is not and should
> not be very constraining. It simply imposes on authors that they
> provide certain basic functionality. For example, we might have:
> public interface IVenue {
> public ID getID();
> public void activate();
> public boolean isActive();
> public void deactivate();
> }
> The purpose of IVenue is to define a polymorphic type, not to impose
> very many requirements for that type. The requirements should be the
> lowest common denominator of whatever we think any IVenue will need to
> provide. If we do not want to impose any requirements, I would propose
> that we create an interface with no methods.

As you've defined IVenue above, it bares a very strong resemblance to an
ISharedObject. Here are the methods on ISharedObject:

public void init(ISharedObjectConfig config);
public void handleEvent(Event evt);
public void dispose();
public Object getAdapter(Class);

With ISharedObjects, activated and deactivated events are delivered
asynchronously via the handleEvent method. The config has methods for
determining the ISharedObject's ID, and it's current state (ala your
getID and isActive()).

So I don't think we're that far off here...as the ISharedObject
interface is intended to be the minimal contract for objects to 'live'
in the context of a container.

AHA...so with your getVenue method you are intending to provide a lookup
of a given 'venue' component...OK, I didn't get that initially...SORRY!

But what you are describing could easily be done with ISharedObjects by:

a) Having a container (call it IVenueContainer ;) that has an
Interface->SharedObject ID map.
b) Having IVenue extend ISharedObject
c) Implementing your getVenue(Class) method like this (on
IVenueContainer implementation class):

public IVenue getVenue(Class clazz) {
ID objectID = map.get(clazz);
// No object implementing that class found
if (objectID == null) return null;
return (IVenue) super.getSharedObjectManager().getSharedObject(objectID);
}

Is this what you were thinking with getVenue(Class clazz)? Sorry if I
misunderstood earlier. If this is what you intend though (a lookup to
find a service), then perhaps we should have a container that registers
it's sharedobjects via the OSGI BundleContext.registerService(), so that
any other plugin can use its own BundleContext.getService() call rather
than have to have a special additional registry? So, maybe we just have
an IVenueContainer register it's IVenue implementers with the OSGI
registry, and have plugins that are interested in getting a specific
service (IVenue) off of a given container use the OSGI service lookup
mechanisms?

>
> Now comes the interesting part. We are asking IVenueContainer for an
> IVenue that must build on its capabilities, but not be required to
> expose any more than the component builder deems appropriate. Put
> another way, our new IVenue must have access to something about the
> innards of IVenueContainer, but without exposing it.
>
> Do do this, I assume a registry of IVenueFactories. Presumably this is
> a singleton that IVenueContainer kinows about. Moreover, I assume that
> the registry records an association of an IVenueFactory with an IVenue
> interface. When the IVenueContainer recieves the IVenue request, it
> finds the registry and gets the appropriate factory. Now, let's imagine
> that the factory looks like this:
> public interface IVenueFactory {
> public Class getKey();
> public IVenue createVenue(ISharedObjectContainer soc) throws
> VenueFactoryException;
> }
> The factory is prepared to create something of type IVenue, but first it
> wants access to the implementation platform upon which it will build.
> In a sense, it "wraps" itself around the implementation core and
> provides access to it according to its own rules (e.g., its own
> interface). The implementation upon which it builds remains opaque to
> the application developer who chooses to use this venue. The platform
> can be a "black box" and the component that extends it can be a "black
> box". If the factory requires a specialization of
> ISharedObjectContainer (such as to do something implementation specific,
> it will call the getAdapter on ISharedObjectContainer and throw an
> exception if it cannot get what it needs.
>
> This is the component approach that I would advocate for the ECF. It
> means that every time we see an opportunity for extension, call it XXX,
> we do the following:
> 1) Create a highly generic IXXX to represent the lowest common
> denominator of capability.
> 2) Add a getXXX method to whatever interface will be the logical
> container for the new XXX capability.
> 3) Provide a generic factory interface of type IXXXFactory that supports
> creating the new capability around a provided infrastructure object.
> 4) Provide a registry for factories of type IXXXFactory.
> I think the infrastructure object that is wrapped by the new object will
> vary depending on the purpose of XXX. Sometimes it will be
> ISharedObjectContainer, sometimes ISharedObject, and other times a
> derived object from some higher level API.

I'm with you here (really). But I think that we might want to use the
existing OSGI/Eclipse mechanisms for some of these things, however, in
preference to providing an alternative implementation in ECF. So:

1) Create a highly generic IXXX to represent the lowest common
denominator of capability
Scott: yes, I buy this. I think the key thing is whether it's a) new
capability for the *container itself*, or whether it's new capability
for the components/applications that 'exist' within that container (i.e.
are contained by the given container).
2) Add a getXXX method to whatever interface will be the logical
container for the new XXX capability
Scott: I'm a little hesitant on this, primarily because I think that we
might be able to a) get away with use of getAdapter and the use of the
Eclipse AdapterFactory and AdapterManager mechanisms (which provide some
of the facilities you are looking for I believe...I'll look into
further); OR b) use the OSGI registerService/getService methods (since
there is already a generic service registry there).
c) Provide a generic factory interface of type IXXXFactory that supports...
Scott: I think that we may be able to get this by using the
AdapterFactory and AdapterManager platform extension points. Let me
look at this closer
4) Provide a registry for factories of type IXXXFactory...
Scott: Again, I think this may be redundant with either/both the
Eclipse AdapterFactory, AdapterManager and/or the OSGI
registerService/getService registry. I'll try to sort this out upon
closer inspection.

>
> Notice that I am treating getAdapter as a way to get alternate forms of
> an object, not as a way to obtain the extensions.

But getAdapter (as defined in IAdaptable) does already behave this
way...it's completely up to the getAdapter implementer to determine what
class/instance is used to return the Object...it can be an extension of
the class implementing getAdapter(), or it can be a new instance of some
completely different class...i.e. an alternate form...as long as it
implements the interface specified as the getAdapter parameter.

>
> Now, if this conforms to the eclipse plug-in model and we are willing to
> rely on it, then I can get what I want. Otherwise, I would advocate
> that we start creating the mechanisms to support this kind of plug-in.

I think that with a judicious combination of the Eclipse
AdapterFactory/AdapterManager registry AND the OSGI
registerService/getService we can expose via ECF an API that provides
you what you want, and requires minimal additional implementation (i.e.
I don't think it's necessary or desireable for ECF to implement a bunch
of additional registries that are queried/provide 'services' to other
plugins at runtime). I'll verify this by looking at both the OSGI
registerService/getService mechanisms on BundleContext, and the Eclipse
AdapaterFactory/AdapterManager extension points.

> In either case, we should start demonstrating the pattern in the way we
> build components on top of the ECF.

I agree! (whew)

I would say, however, that what we are talking about is really sets of
components (i.e. like presence api, or discovery api, etc). These are
really whole subsystems...which, naturally consist of 1 or more
components/services. But in any event I think we're on the same page here.

>
> Let me know what you think.

Hopefully this is enough for now. I will look at the OSGI and Eclipse
mechanisms described above and do a cursory assessement. If others
would like to contribute to this please feel free to do so...I've only
just begun to use the OSGI service registration/lookup facilities and
although they look perfect for the job I may be missing something.
Please let me know if so.

Thanks,

Scott
Re: A Componentry Pattern [message #586867 is a reply to message #586855] Wed, 22 June 2005 04:12 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

My comments are interspersed.

jfp

Scott Lewis wrote:

> Hi John,
>
> I'm going to intersperse my comments in John's posting...just to try
> to keep things manageable.
>
> John F. Patterson wrote:
>
>> Scott, Peter:
>>
>> I think that one of the most important aspects of the ECF is the
>> manner in which it supports componentry. By this, I mean Peter's
>> concept of a downstream extension or a plug-in. One of my objecives
>> in a component model is to allow the component developer to expose
>> only what he or she cares to expose, while building on a rich
>> platform of capabilities. In other words the component is a mapping
>> from the rich capabilities onto a specialization and the component
>> author should not be compelled to expose more than he or she wants to.
>
>
> I agree strongly. The notion of supporting 'communications
> componentry' is really what ECF is going after.
>
> The trick here, I think, is mapping various notions of extension and
> specialization onto the mechanisms provided by java/java classes, by
> the OSGI and Eclipse runtime, and by ECF API itself. So, for example,
> using just java it's possible to extend the functionality of a class
> (e.g. a container in ECF parlance), but with java this results in a
> compile-time dependency. Further, it's possible to define a plugin
> with an extension that extends an extension point exposed by another
> plugin...but this (typically) results in a plugin 'resolve' (aka load)
> time dependency between plugins. Even further a mechanism like
> getAdapter(<interface>) or the OSGI 'service lookup' interfaces (on
> org.osgi.framework.BundleContext) provides runtime query capabilities.
>
> It's my expectation that ECF will need to use some/all of these
> mechanisms to provide a range of ways for infrastructure/protocol
> providers to extend ECF itself, for communications component
> developers to create new 'building block' components and also to
> create 'aggregate' components, and for client/application programmers
> to be able to use, extend, and customize existing components.
>
>>
>> I am not that familiar with the eclipse plug-in mechanisms, so I seek
>> your guidance about the best way to do things. I have proposed a
>> particular approach to extensions in a different context, but I am
>> not sure how well it conforms. I thought I would just describe the
>> mechanism and let you tell me the best way to do this in eclipse.
>
>
> The basic mechanisms introduced by the OSGI model are the 'bundle'
> (all eclipse plugins are bundles), and the 'service'. In 'pure' OSGI
> (not necessarily eclipse), bundles have a lifecycle (installed,
> resolved/loaded, start, stop) that is provided by the framework
> itself. Also bundles are provided with a way to register and query
> the framework about the existence/availability of 'services' which
> other bundles have registered with the framework.
>
> In Eclipse, bundles/plugins also expose 'extension points'...and other
> plugins provide 'extensions' of those extension points. Typically
> extension points are named references that are resolved by the
> framework...frequently at plugin load time (plugins are loaded lazily
> by the framework, so that they don't take up memory unnecessarily).
> So an extension point defines a java interface that it will accept,
> and the extension provides an implementation class for that interface
> (which must implement that interface, of course) and declares the
> class as an extension within it's plugin.xml. When the extension
> plugin is loaded an instance of the implementing class will be created
> and made available to the extension point plugin (by Eclipse).
>
> An ECF example of the usage of the extension point/extension mechanism
> is the container factory. ECF has defined an extension point that
> delegates the construction of new containers to extension plugins, so
> that support for completely new types of containers can be introduced
> without modifying ECF at all. All the extension plugin creator must
> do is a) define an implementation of the ISharedObjectContainer
> interface; b) declare a class that implements
> org.eclipse.ecf.core.provider.ISharedObjectContainerInstanti ator, c)
> declare this implementing class as providing an extension to the ECF
> extension point. Then, when a client creates a container instance
> (via SharedObjectContainerFactory), the class from 'a' is returned to
> the client.
>
>>
>> For concreteness, I will assume we are interested in supporting
>> alternative "venues" as extensions of the ISharedObjectContainer.
>
>
> Although I'm OK with interfaces like 'venues' being extensions of
> ISharedObjectContainer (as in 'public interface IVenue extends
> ISharedObjectContainer'), there are good reasons to consider using the
> runtime mechanisms like
> ISharedObjectContainer.getAdapter(IVenue.class). 1) If everything
> extends ISharedObjectContainer (and then extensions of IVenue, etc.
> etc) things can be quite 'brittle' in that any changes to such
> interfaces can/will result in the need to recompile everything; 2) the
> extends relationship might not really be the most natural model for
> the relationship between IVenues and ISharedObjectContainer...that is
> the 'isa' relationship (inheritance) might not be as appropriate as
> the some other relationship (e.g. composition/delegation).
>
Mostly due to the name, I tend to think of getAdapter as being for
getting an alternate form of the object, i.e., an alternate cast. You
and Peter are both arguing that this is a narrow view of the role of
getAdapter. Sadly, the name is too abstract to make it easy to
understand that it is the way to get something for a particular
purpose. On the other hand, it has the advantage of not requiring a new
approach for each new type of extension.

> So, for example, the IPresenceContainer interface (in
> org.eclipse.ecf.presence.IPresenceContainer) does not inherit from
> ISharedObjectContainer. I am still struggling with this but I think
> it's probably the right choice. But, as a consequence, it does
> require the programmer to use the following idiom to get at the
> IPresenceContainer functionality:
>
> ISharedObjectContainer cont =
> SharedObjectContainerFactory.createSharedObjectContainer("presencecontainer ");
>
>
> IPresenceContainer presenceContainer = (IPresenceContainer)
> cont.getAdapter(IPresenceContainer.class);
>
> if (presenceContainer != null) {
> // use it!
> } else {
> // we don't have a valid presence container
> }
>
> This is (of course) more code than:
>
> IPresenceContainer pc = (IPresenceContainer)
> SharedObjectContainerFactory.makeSharedObjectContainer("presencecontainer ");
>
>
> But the intended benefit here is that a) IPresenceContainer has no
> dependency on ISharedObjectContainer and so it is less 'brittle'
> because of the lack of compile-time dependencies; and b) implementers
> if IPresenceContainer can be added/composed onto existing classes by
> simply changing the implementation of getAdapter(...). Thus it's
> (hopefully) possible to have greater code reuse (e.g. people can reuse
> the implementation classes for ISharedObjectContainers in the
> org.eclipse.ecf.provider plugin and 'add on' IPresenceContainer
> functionality easily. This is what I do, for example, in the
> org.eclipse.ecf.provider.xmpp plugin, which defines an xmpp-based
> implementation of IPresenceContainer.
>
> So...to sum up...I think the design choices in interface inheritance
> vs. composition/delegation are potentially kind of subtle...and
> interface inheritance is not always the best answer...even if it has
> some 'cost' in complexity/lines of required code. I'm sure this isn't
> controversial, but stating the obvious is sometimes useful...at least
> to me :-).
>
I agree with your arguments against interface inheritance as presented
here, primarily because ISharedObjectContainer contains the superset of
all capabilities. As I have argued elsewhere, if it is split so that
some simpler form contains only those capabilities that you believe any
container will need, then using that as a common type for all container
variants should be acceptable.

Having said that, I would not argue that venues are container variants.
I assume part of what we are trying to achieve with the container vs.
venue distinction is to allow the container to manage login and support
multiple venues. I am starting to believe that venues are really
wrapped around an ISharedObject (as you suggest later.)

>
>> (BTW: I'm ready for ECFService whenever you are:-) I'm not
>> proposing this as an API as much as I am using it to illustrate the
>> pattern.
>>
>> Here goes:
>>
>> First, let's assume we have an IVenueContainer that does little more
>> than what we think every such container will do. In other words,
>> public interface IVenueContainer {
>> public void login(...);
>> public void logoff();
>> public IVenue getVenue(Class venueInterface);
>> public Object getAdapter(Class subInterface);
>> }
>
>
> I would argue that everything you are looking for as you've defined
> IVenueContainer is actually already there in ISharedObjectContainer:
>
> public void login() --> public void joinGroup(ID,IJoinContext);
> public void logoff() --> public void leaveGroup();
> # The reason these are named joinGroup/leaveGroup rather than
> login/logoff is that the 'joinGroup' notion is intended to include
> both 'connect to group' and 'login to group' semantics (since these
> need to be tightly bound for security)

The reason that I keep coming back to login and logoff is that the name
joinGroup is not very compelling for most people. What is the group?
Even if login is tied to connecting, I think we are better off going
with a familiar sounding name and explaining its extra functionality.
For me, connect/disconnect or login/logoff would be preferable to
joinGroup/leaveGroup.

> # continuing...
> public Object getAdapter(Class) --> public Object getAdapter(Class);
> public IVenue getVenue(Class) --> public Object getAdapter(Class);
>
> It seems to me that public IVenue getVenue(Class) is completely
> redundant to public Object getAdapter(Class). The typical contract of
> the getAdapter (IAdaptable in Eclipse parlance) is that the (non-null)
> Object returned from the getAdapter call *must* be castable to the
> java interface class provided as the param to the getAdapter call. So
> the getVenue method would be trivially implemented as:
>
> public IVenue getVenue(Class mustbeivenuesubclass) {
> return (IVenue) super.getAdapter(mustbeivenuesubclass);
> }
>
I understand this, but getAdapter says nothing about the nature of the
contract. There is no implied type to pass in or clear objective for
the method. Moreover, it can only work for a "no argument"
constructor. As I mention later, I am interested in some factories that
are given an argument to build their implementation around. Yes,
getAdapter is very abstract and powerful, but it doesn't say much about
how it will be used.

> One way to address your previously articulated concern about the
> complexity of the ISharedObjectContainer interface (i.e. too many
> methods) would be to create a super interface (call it IContainer):
>
> public interface IContainer {
>
> public void joinGroup(ID,IJoinContext);
> public void leaveGroup();
> public Object getAdapter(Class intf);
>
> }
>
> public interface ISharedObjectContainer extends IContainer {
> // All current ISharedObject methods *except* the three defined above
> }
>
I hate the name IContainer, but I like the point. This provides the
lowest common denominator type from which all ECF container types may
grow. This is what I have been arguing for.

> This would be a fine thing to do (and I'm certainly willing to do it),
> but it seems to me it's kind of unnecessary. That is,
> ISharedObjectContainer has the following methods (in addition to
> joinGroup,leaveGroup,and getAdapter methods):
>
> addListener/removeListener (useful for getting notification of
> container events)
> getConfig, dispose (necessary for managing the lifecycle of the
> container itself)
> getGroupID,getGroupMemberIDs,isGroupManager (basic query interfaces
> about the current group membership...important for reliability)
> getSharedObjectManager (to get the ISharedObjectManager reference)
>
I want to allow those who create new container types to decide how much
of this and in what way they care to expose the functionality. Your
IContainer type allows me a way to use ContainerFactory to create an
IContainer of my type with only the limited requirements. In the
instantiator for this IContainer, I will recruit an
ISharedObjectContainer for my internal use, but only expose its
capabilities as needed and in the manner that suits my IContainer
extension. If you make people first get an ISharedObjectContainer and
then use getAdapter to get my alternate container type, then what's the
point of hiding any capability. They are all hanging out there on the
ISharedObjectConatiner for anyone to misuse.

> So I would argue that *if* we had an IContainer super interface for
> ISharedObjectManager that we would soon find that we would need to
> 'move up' the addListener/removeListener,getConfig/dispose and
> probably ultimately the getGroupID,getGroupMemberIDs,isGroupManager
> methods into IContainer...and that would leave just
> getSharedObjectManager as defined in ISharedObjectContainer.
>
If you think of IContainer (still a bad name) as *the* type that
application developers will use, then you are probably right. Don't
think of it that way. It is more like an abstract type that we expect
component developers to extend. Our factories for IContainer will be
designed to give you the type of container that you ask for. If you
want all the capabilities, then ask for an ISharedObjectContainer. But,
I, as a component developer, might want to create a new type of
container that remaps the capabilities of ISharedObjectContainer. I
want to fit into the ECF factory story, but I don't want my users
(application developers) to have access to the innards of the
container. I want them to use the capabilities in the ways that I have
made them available.

> So that's my concern about adding a super interface to
> ISharedObjectContainer...that we would end up just moving nearly
> everything in ISharedObjectContainer into that super interface and not
> have added anything by having another layer in the interface hierarchy.
>
> Now one thing I should add...of course, if we want to add an
> IVenueContainer as a subclass of ISharedObjectContainer that's
> perfectly fine...but I would say that it would not be very useful as a
> distinct interface with the semantics described above, because it
> overlaps so strongly with what ISharedObjectContainer already provides.
>
For the purpose of this discussion, my IVenueContainer is equivalent to
your IContainer above. I do not offer it as a final type, but as a
common base type for all ECF containers.

>
>> Let's assume we can get one of these from a singleton named
>> IVenueContainerFactory that completely mimics the pattern of
>> ISharedObjectContainerFactory.
>>
>> Now, when an IVenueFactory is created it will recruit an
>> ISharedObjectContainer to do its work, but the application developer
>> doesn't need to know that. That's for the component author to know
>> about. Conceivably, some IVenueContainers might want to support
>> casting to a more specialized form of IVenueContainer. I have
>> included getAdapter for this. I do not really expect it to be used
>> to get venues.
>
>
> I would be completely fine with this. I'm wouldn't/not going to argue
> that we should have only one SharedObjectContainerFactory. Actually,
> there can easily be patterns like this:
>
I think I am arguing for one type of container factory, but that it
should be used to build objects of type IContainer as defined above. I
would use the pattern you describe below as the means to ensure that my
IContainer type has an ISharedObjectContainer within it, so that it can
do what it needs to do. What it exposes about the
ISharedObjectContainer (other than the minimal IContainer requirements)
is up to this IContainer extension.

> public interface IFooContainer {
> // whatever
> }
>
> public class FooFactory {
> public static final String FOO_TYPE = "whatever";
>
> public static IFooContainer makeFooContainer() {
> ISharedObjectContainer cont =
> SharedObjectContainerFactory.makeSharedObjectContainer(FOO_T YPE);
> IFooContainer fooContainer = cont.getAdapter(IFooContainer.class);
> if (fooContainer == null) throw new Exception("doesn't have IFoo
> nature"); <-- this is not the right exception type but anyway
> return fooContainer;
> }
>
> I would expect that some ECF plugins would have their own factories
> and to the work of the construction/casting for the programmer. In
> fact, it would be really good (I think) if when constructing a plugin
> developers could choose to create a plugin from an ECF template, and
> the above factory class/methods could/would be created automatically
> for them.
>
Using a template is more than what I am asking for. I think ECF should
provide a common registry for all these IContainer types (We still need
a better name.) and some call on the registry (like the getAdapter call)
that gives me an IContainer of the desired type. I would ensure that
the registry provides a default implementation for getting an
ISharedObjectContainer (which is an extension of IContainer). This will
be there for anyone who wants all the functionality.

>>
>> To obtain one of the venues supported by this IVenueContainer, we
>> call getVenue. Like the adapter pattern, we provide the interface of
>> the venue type we desire and we will cast to that interface when we
>> get the return value, e.g., presenceVenue = (IPresenceVenue)
>> getVenue(IPresenceVenue.class);
>
>
> So I still see this as redundant to Object getAdapter(Class), but I
> guess you are saying that getVenue (because the method name is
> specific) would be more understandable than getAdapter() (as
> getAdapter is more abstract?). Is this right? I don't know how
> comfortable people/programmers would be with the propgation of lots of
> individually named 'interface query' mechanisms that all do
> similar/identical things.
>
You are right about why I prefer getVenue over getAdapter and you raise
a good point. getAdapter might not say what it is used for, but that is
both a curse and a blessing. It has the virtue that it can be used for
any new purpose regardless of whether venue seems like the right name
for it. We don't want to keep adding new methods for each new type of
purpose.

Having said that, remember "venue" is really just a stand in for
"service", which is itself fairly generic. I do not imagine a need for
much more than the getService call for added specificity.

The other less obvious reason for wanting a getVenue method is that it
defines a different factory convention as I describe later. getAdapter
can only be used when the factory is essentially a no argument
constructor. For getVenue, I imagine a factory convention that involves
passing the ISharedObjectContainer to the factory. This difference in
the factory pattern may be the most important reason for creating a new
method.

>>
>> The fact that the return value from getVenue is IVenue is not and
>> should not be very constraining. It simply imposes on authors that
>> they provide certain basic functionality. For example, we might have:
>> public interface IVenue {
>> public ID getID();
>> public void activate();
>> public boolean isActive();
>> public void deactivate();
>> }
>> The purpose of IVenue is to define a polymorphic type, not to impose
>> very many requirements for that type. The requirements should be the
>> lowest common denominator of whatever we think any IVenue will need
>> to provide. If we do not want to impose any requirements, I would
>> propose that we create an interface with no methods.
>
>
> As you've defined IVenue above, it bares a very strong resemblance to
> an ISharedObject. Here are the methods on ISharedObject:
>
> public void init(ISharedObjectConfig config);
> public void handleEvent(Event evt);
> public void dispose();
> public Object getAdapter(Class);
>
I have been beginning to wonder whether IVenues will be defined around
ISharedObjects more than ISharedObjectContainers. I am currently
thinking of an ISharedObject as a multicast communication channel. Is
this right?

I probably would not expose handleEvent or getAdapter. I would leave it
up to the component author to decide whether these are necessary or this
the right way to expose the capabilities. I'm not sure on init.
Without looking again at the config, I'm not sure what about it seems
lowest common denominator and what seems constraining.

Having said all that, your point is probably a good one. We might not
be that far apart.

> With ISharedObjects, activated and deactivated events are delivered
> asynchronously via the handleEvent method. The config has methods for
> determining the ISharedObject's ID, and it's current state (ala your
> getID and isActive()).
>
> So I don't think we're that far off here...as the ISharedObject
> interface is intended to be the minimal contract for objects to 'live'
> in the context of a container.
>
> AHA...so with your getVenue method you are intending to provide a
> lookup of a given 'venue' component...OK, I didn't get that
> initially...SORRY!
>
Yes, the getVenue searches a registry for a specific form of a venue
component.

> But what you are describing could easily be done with ISharedObjects by:
>
> a) Having a container (call it IVenueContainer ;) that has an
> Interface->SharedObject ID map.
> b) Having IVenue extend ISharedObject
> c) Implementing your getVenue(Class) method like this (on
> IVenueContainer implementation class):
>
> public IVenue getVenue(Class clazz) {
> ID objectID = map.get(clazz);
> // No object implementing that class found
> if (objectID == null) return null;
> return (IVenue)
> super.getSharedObjectManager().getSharedObject(objectID);
> }
>
As I describe later on this is not exactly right, since I want to pass
something into the factory that gives the new component the "keys to the
city", i.e., the ISharedObjectContainer. Also, I am asking whether we
want each new component to roll its own registry or whether we might
prefer to manage these extensions through a common mechanism. Obviously
we cannot prevent a new component author from doing whatever he or she
thinks is best, but we can provide an example of what we would recommend
as the preferred way to do things.

> Is this what you were thinking with getVenue(Class clazz)? Sorry if I
> misunderstood earlier. If this is what you intend though (a lookup to
> find a service), then perhaps we should have a container that registers
> it's sharedobjects via the OSGI BundleContext.registerService(), so
> that any other plugin can use its own BundleContext.getService() call
> rather than have to have a special additional registry? So, maybe we
> just have an IVenueContainer register it's IVenue implementers with
> the OSGI registry, and have plugins that are interested in getting a
> specific service (IVenue) off of a given container use the OSGI
> service lookup mechanisms?
>
I think you are asking the right questions now about how to support
registration of new capabilities. I can't help with respect to OSGi.

>>
>> Now comes the interesting part. We are asking IVenueContainer for an
>> IVenue that must build on its capabilities, but not be required to
>> expose any more than the component builder deems appropriate. Put
>> another way, our new IVenue must have access to something about the
>> innards of IVenueContainer, but without exposing it.
>>
>> Do do this, I assume a registry of IVenueFactories. Presumably this
>> is a singleton that IVenueContainer kinows about. Moreover, I assume
>> that the registry records an association of an IVenueFactory with an
>> IVenue interface. When the IVenueContainer recieves the IVenue
>> request, it finds the registry and gets the appropriate factory.
>> Now, let's imagine that the factory looks like this:
>> public interface IVenueFactory {
>> public Class getKey();
>> public IVenue createVenue(ISharedObjectContainer soc) throws
>> VenueFactoryException;
>> }
>> The factory is prepared to create something of type IVenue, but first
>> it wants access to the implementation platform upon which it will
>> build. In a sense, it "wraps" itself around the implementation core
>> and provides access to it according to its own rules (e.g., its own
>> interface). The implementation upon which it builds remains opaque
>> to the application developer who chooses to use this venue. The
>> platform can be a "black box" and the component that extends it can
>> be a "black box". If the factory requires a specialization of
>> ISharedObjectContainer (such as to do something implementation
>> specific, it will call the getAdapter on ISharedObjectContainer and
>> throw an exception if it cannot get what it needs.
>>
>> This is the component approach that I would advocate for the ECF. It
>> means that every time we see an opportunity for extension, call it
>> XXX, we do the following:
>> 1) Create a highly generic IXXX to represent the lowest common
>> denominator of capability.
>> 2) Add a getXXX method to whatever interface will be the logical
>> container for the new XXX capability.
>> 3) Provide a generic factory interface of type IXXXFactory that
>> supports creating the new capability around a provided infrastructure
>> object.
>> 4) Provide a registry for factories of type IXXXFactory.
>> I think the infrastructure object that is wrapped by the new object
>> will vary depending on the purpose of XXX. Sometimes it will be
>> ISharedObjectContainer, sometimes ISharedObject, and other times a
>> derived object from some higher level API.
>
>
> I'm with you here (really). But I think that we might want to use the
> existing OSGI/Eclipse mechanisms for some of these things, however, in
> preference to providing an alternative implementation in ECF. So:
>
> 1) Create a highly generic IXXX to represent the lowest common
> denominator of capability
> Scott: yes, I buy this. I think the key thing is whether it's a) new
> capability for the *container itself*, or whether it's new capability
> for the components/applications that 'exist' within that container
> (i.e. are contained by the given container).

I don't understand this distinction. I would continue to argue that we
want something like IContainer as the root for anyone providing ECF
capability. The instantiator will recruit an ISharedObjectContainer and
expose the functionality as seems best in an IContainer extension. The
primary thing that distinguishes IContainer from other such extensions
is that, being the root object, it can go get ISharedObjectContainer
directly. It does not need anything to pass in the infrastructure that
it will build on. For other extensions, however, we already have a root
and the factory pattern must shift slightly so that the
ISharedObjectContainer (or some infrastructure object) is passed in to
the factory as part of the instantiation request. We can't go get a new
one, we have to be told the one we are working on.

Other than that, I think of the IContainer and the IVenue in a similar
way; they are base interfaces describing a minimal lowest common
denominator capability that can be used as a common root type for
extensions of that type.

> 2) Add a getXXX method to whatever interface will be the logical
> container for the new XXX capability
> Scott: I'm a little hesitant on this, primarily because I think that
> we might be able to a) get away with use of getAdapter and the use of
> the Eclipse AdapterFactory and AdapterManager mechanisms (which
> provide some of the facilities you are looking for I believe...I'll
> look into further); OR b) use the OSGI registerService/getService
> methods (since there is already a generic service registry there).

I understand and appreciate your argument here, but how do we deal with
the different factory patterns, i.e., the arguments passed into the
factory. Could we characterize the factory argument requirements in an
attribute on the factory? That might permit us to keep using getAdapter
for factories of certain standard types.

>
> c) Provide a generic factory interface of type IXXXFactory that
> supports...
> Scott: I think that we may be able to get this by using the
> AdapterFactory and AdapterManager platform extension points. Let me
> look at this closer

That's fine with me. I don't want to invent anything new, but I also
don't want to leave it to component authors to roll their own. The more
we can have a common way to do things; the more familiarity with one
component will provide training on the others. Component developers
will usually follow our lead, provided we give them one.

> 4) Provide a registry for factories of type IXXXFactory...
> Scott: Again, I think this may be redundant with either/both the
> Eclipse AdapterFactory, AdapterManager and/or the OSGI
> registerService/getService registry. I'll try to sort this out upon
> closer inspection.
>
Excellent!

>>
>> Notice that I am treating getAdapter as a way to get alternate forms
>> of an object, not as a way to obtain the extensions.
>
>
> But getAdapter (as defined in IAdaptable) does already behave this
> way...it's completely up to the getAdapter implementer to determine
> what class/instance is used to return the Object...it can be an
> extension of the class implementing getAdapter(), or it can be a new
> instance of some completely different class...i.e. an alternate
> form...as long as it implements the interface specified as the
> getAdapter parameter.
>
I think this has been my misunderstanding. Nevertheless, I still think
there is an issue about different factory types and the arguments that
they want to receive.

>>
>> Now, if this conforms to the eclipse plug-in model and we are willing
>> to rely on it, then I can get what I want. Otherwise, I would
>> advocate that we start creating the mechanisms to support this kind
>> of plug-in.
>
>
> I think that with a judicious combination of the Eclipse
> AdapterFactory/AdapterManager registry AND the OSGI
> registerService/getService we can expose via ECF an API that provides
> you what you want, and requires minimal additional implementation
> (i.e. I don't think it's necessary or desireable for ECF to implement
> a bunch of additional registries that are queried/provide 'services'
> to other plugins at runtime). I'll verify this by looking at both the
> OSGI registerService/getService mechanisms on BundleContext, and the
> Eclipse AdapaterFactory/AdapterManager extension points.
>
If there is not a common way to do this, I think we could remake
SharedObjectContainerFactory into an ECFFactoryRegistry that associates
factories of all types with an appropriate interface. For root classes,
this would have the following method: IContainer getContainer(Class
containerClass,String providerType) throws ECFException; For all other
types, it would be necessary to get the factory and call the appropriate
create method while passing the appropriate internal objects, e.g.,
ISharedObjectContainer. We would not expose a simple way to build these
types because we don't really want application developers to do it.
Constructing these objects is the business of the infrastructure and
component authors.

In eclipse implementations, the registry would be prepared to look for
plug-ins. In non-eclipse implementations, this would be handled
externally and something would need to add all components to the registry.

>> In either case, we should start demonstrating the pattern in the way
>> we build components on top of the ECF.
>
>
> I agree! (whew)
>
> I would say, however, that what we are talking about is really sets of
> components (i.e. like presence api, or discovery api, etc). These are
> really whole subsystems...which, naturally consist of 1 or more
> components/services. But in any event I think we're on the same page
> here.
>
Yes. You are right. I am thinking of whole subsystems and not just one
interface extensions.

>>
>> Let me know what you think.
>
>
> Hopefully this is enough for now. I will look at the OSGI and Eclipse
> mechanisms described above and do a cursory assessement. If others
> would like to contribute to this please feel free to do so...I've only
> just begun to use the OSGI service registration/lookup facilities and
> although they look perfect for the job I may be missing something.
> Please let me know if so.
>
Good luck. Sorry I cannot help with this.

> Thanks,
>
> Scott
Re: A Componentry Pattern [message #586880 is a reply to message #586867] Mon, 27 June 2005 04:12 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

John F. Patterson wrote:
<stuff deleted>
>
>
> The reason that I keep coming back to login and logoff is that the name
> joinGroup is not very compelling for most people. What is the group?
> Even if login is tied to connecting, I think we are better off going
> with a familiar sounding name and explaining its extra functionality.
> For me, connect/disconnect or login/logoff would be preferable to
> joinGroup/leaveGroup.

The use of joinGroup/leaveGroup was driven by the fact that ECF has a
'group communication' model...i.e. the communication is not limited to
client-server (although 2 does constitute a group), but is rather more
generally a group of communicating processes. Further the
java.net.MulticastSocket uses joinGroup/leaveGroup for it's equivalent
to connect/disconnect, and so this is at least precedent (in the java
base API) for using these semantics for multipoint communications.

I am, however, quite willing to bend to the will of the community on
this question: what should the join/connect/login
leave/disconnect/logff methods be called? Depending upon people's
feedback, I would be willing to use either:

1) joinGroup/leaveGroup (as is)
1a) join/leave
2) connect/disconnect

I'm very uncomfortable with login/logoff, as it doesn't capture the
primary semantics of the operation (which really is about connection
rather than authentication).

So, please us all know what your preferences are about this aspect of
the API, as it is hard for me to tell just from John's posting what
other people are thinking. He makes a great case, but it's not
necessarily representative. Thanks.

<stuff deleted>

>> public IVenue getVenue(Class mustbeivenuesubclass) {
>> return (IVenue) super.getAdapter(mustbeivenuesubclass);
>> }
>>
> I understand this, but getAdapter says nothing about the nature of the
> contract. There is no implied type to pass in or clear objective for
> the method. Moreover, it can only work for a "no argument"
> constructor. As I mention later, I am interested in some factories that
> are given an argument to build their implementation around. Yes,
> getAdapter is very abstract and powerful, but it doesn't say much about
> how it will be used.

I agree that getAdapter doesn't say anything about how it will be used
for any particular case...but it seems to me that's the role of less
abstract parts of the API (i.e. any extension API can define more
specific adapter methods if they wish to).

Given this and the ubiquity of getAdapter as a mechanism for runtime
extension in Eclipse (i.e. it's everywhere already), I would like to
keep getAdapter().

<stuff deleted>

>> public interface ISharedObjectContainer extends IContainer {
>> // All current ISharedObject methods *except* the three defined above
>> }
>>
> I hate the name IContainer, but I like the point. This provides the
> lowest common denominator type from which all ECF container types may
> grow. This is what I have been arguing for.

I'm willing to create such a super interface for ISharedObjectContainer
and will do so. Like I've said before I have a little bit of concern
that it will soon be necessary to 'move up' several of the other methods
in ISharedObjectContainer (i.e. the membership methods), and that this
will require making the super interface as complex as
ISharedObjectContainer (which I don't think of as particularly complex
for the core interface), but in any case....

But, I would like to find a name for this interface that does not
introduce a new concept into the mix (like IContainer). I *really*
don't want to introduce a totally new concept/name (like Venue, or Bag,
or ...) just ends up being a synonmym for 'container'.

So, even though John seems to hate it, if people want it I'm inclined to
have an IContainer interface:

public interface IContainer {
/* or whatever we end up changing these two methods to */
public void joinGroup(ID,IJoinContext);
public void leaveGroup();
public Object getAdapter(Class intf);
}

because a) ISharedObjectContainer is an obvious extension/specialization
of IContainer...and the relationship is obviously hierarchical; b) this
does not introduce any new concepts.

And, of course, there will be a ContainerFactory to make instances of
IContainers, and a new extension point that will allow provider plugins
to provide their own implementations of IContainer that can be accessed
via the ContainerFactory.

<stuff deleted>

> I want to allow those who create new container types to decide how much
> of this and in what way they care to expose the functionality. Your
> IContainer type allows me a way to use ContainerFactory to create an
> IContainer of my type with only the limited requirements. In the
> instantiator for this IContainer, I will recruit an
> ISharedObjectContainer for my internal use, but only expose its
> capabilities as needed and in the manner that suits my IContainer
> extension. If you make people first get an ISharedObjectContainer and
> then use getAdapter to get my alternate container type, then what's the
> point of hiding any capability. They are all hanging out there on the
> ISharedObjectConatiner for anyone to misuse.

I'm into information hiding, but I really question that programmers will
be able to create a functional container with an API so limited as that
provided by IContainer (even given getAdapter()).
ISharedObjectContainer, even though it looks like a lot of methods
really has a) container lifecycle: getConfig() and dispose(); b) the
join/leave, getAdapter core; c) the container listeners add/remove; d)
the group membership (reliability) methods: getGroupID, etc; e)
getSharedObjectManager();

ISharedObjectContainer isn't nearly as complicated as
org.osgi.framework.Bundle or BundleContext...

But, again if it seems so important to everyone that a minimal super
interface be provided and a factory for same I will do it.

Please others do speak up, though, one way or the other: do you see
ISharedObjectContainer as needing to be simplified with a super
interface, or do you see it as a reasonable root interface by itself?

<stuff deleted>
> If you think of IContainer (still a bad name) as *the* type that
> application developers will use, then you are probably right. Don't
> think of it that way. It is more like an abstract type that we expect
> component developers to extend.

But that is what I expect *container implementers* to do with
ISharedObjectContainer (but not application developers). Note that
container implementers (those that build containers that expose SIP, or
XMPP, or Skype, or other protocols) will need to do more than those that
want to create components or build applications out of those components
using shared objects (which are only available through the shared object
container anyway).

And container implementers can always either use getAdapter() (for
runtime interface access) or just extend ISharedObjectContainer (compile
time) to their hearts content. This is true whether we're talking about
ISharedObjectContainer or IContainer.

Our factories for IContainer will be
> designed to give you the type of container that you ask for. If you
> want all the capabilities, then ask for an ISharedObjectContainer. But,
> I, as a component developer, might want to create a new type of
> container that remaps the capabilities of ISharedObjectContainer. I
> want to fit into the ECF factory story, but I don't want my users
> (application developers) to have access to the innards of the
> container.

Truly, I don't see ISharedObjectContainer as exposing the innards of the
container...it just exposes (via the methods listed above) the ability
to join/leave, get access to shared object components, add/remove event
listeners, get reliability/group membership info, and manage the
lifecycle of the container instance. I don't see this as overkill, but
obviously you do...and I'm willing to consider it overkill if others
feel similarly.

<stuff deleted>

>>
> I have been beginning to wonder whether IVenues will be defined around
> ISharedObjects more than ISharedObjectContainers. I am currently
> thinking of an ISharedObject as a multicast communication channel. Is
> this right?

No, containers represent an abstract multipoint (in general)
communication channel...ISharedObjects represent components that can use
that channel context to communicate and that can 'come and go' in that
context. ISharedObjects are meant to be the 'component' or 'service'
that uses the container-provided communication channel to communicate/be
communicated with. Containers and shared objects have a (typically)
one-to-many relationship, although it's possible to create a
many-to-many relationship between them...i.e. have ISharedObject
instances that are contained by multiple containers.

>
> I probably would not expose handleEvent or getAdapter. I would leave it
> up to the component author to decide whether these are necessary or this
> the right way to expose the capabilities. I'm not sure on init.
> Without looking again at the config, I'm not sure what about it seems
> lowest common denominator and what seems constraining.

The init/dispose are necessary for the component/shared object to have a
'lifecycle' in the context provided by the container...init so that the
shared object can be given access to the ISharedObjectContext (which is
accessed via the ISharedObjectConfig provided via init). The
ISharedObjectContext provides the container-implemented communications
channel to the ISharedObject code (see
ISharedObjectContext.sendMessage(), etc).

handleEvent and handleEvents are the entry point methods for delivering
events asynchronously to the shared object.

ISharedObject was patterned (loosely) after the Servlet API notions
(which also have the notion of a container (the servlet container) and a
components (the servlet). The servlet can be initialized/disposed in
the context of the container (a server-provided entity), and it can be
delivered synchronous http requests (the http servlet anyway).

>
> Having said all that, your point is probably a good one. We might not
> be that far apart.

I don't think so either.

So John, and everyone else please let your views be known on the
following questions:

1) Do we need/want a super interface (IContainer) for
ISharedObjectContainer?
2) Should ISharedObjectContainer 'join', 'joinGroup', or 'connect'?

Thanks,

Scott
On getAdapter [message #586893 is a reply to message #586880] Tue, 28 June 2005 15:31 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

Upon further consideration, I am inclined to relent somewhat regarding
getAdapter.

I have had two reasons for preferring a more specific name.

1) It is more readable and more understandable.

2) I have felt that the getAdapter was not sufficiently versatile to
handle complex construction patterns, such as those requiring that
arguments be passed on construction. I felt that a different getXXX
would be needed to signal the difference in construction pattern. I now
believe that while this is helpful, it is not essential.

Imagine that we have an adaptable object, i.e., it has a getAdapter
method and that its implementation "knows" about a variety of different
factories. When the getAdapter is called and the Class is passed in,
the adaptable object will retrieve the appropriate factory from a
registry. (I'm not saying where or what the registry will be, but I
assume we agree that some sort of registry will exist.) Then, the
adaptable can "reflect" on the returned factory. If it is a type that
it "knows" about. It can call the appropriate creation method and pass
the required information. If it is not one that it knows about, it will
return null.

So, my second argument against getAdapter is erased and I am left with
the improved understandability.

I still think that when creating an extensible component that is an
essential feature of how some offering works, we might still want to use
the getXXX method even if it looks a lot like getAdapter. I'm not sure
getVenue in the example we have been discussing is a very compelling
example.

In any case, I am down to only an aesthetic argument against getAdapter.

jfp
Re: On getAdapter [message #586904 is a reply to message #586893] Tue, 28 June 2005 16:03 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

Thanks for your note...I'm a 'big fan' of getAdapter (as you already
know). I'm glad it doesn't offend as much as it did before.

RE: registry for adapters...Eclipse 3.1 already has this...it's and
extension point called "org.eclipse.core.runtime.adapters". Plugins can
simply declare an adapter factory class in their plugin.xml declaration
like this:

<extension point="org.eclipse.core.runtime.adapters">
<factory
class="com.xyz.MyFileAdapterFactory"
adaptableType="org.eclipse.core.resources.IFile">
<adapter type="com.xyz.MyFile"/>
</factory>
</extension>


And this will result in a custom Adapterfactory (in this case
MyFileAdapterFactory, which must implement
org.eclipse.core.runtime.IAdapterFactory). Then if the getAdapter
implementation uses this idiom:

public Object getAdapter(Class type) {
Platform.getAdapterManager().getAdapter(this,type);
}

This will then (at runtime) consult the eclipse IAdapterFactory registry
and call the appropriate adapter.

So, this mechanism suits well for allowing plugins to declare new
adapaters for existing object (e.g. ECF instances).

The one issue remaining is the IAdaptable is an Eclipse-specific
mechanism, and in the ECF code I've specifically limited the number of
Eclipse dependencies so as to make it useable on servers (not running
the eclipse platform, but rather running some other OSGI runtime). But
it may be time to rethink that, and just put in a dependency on
IAdaptable (and include that one interface in any server
implementation...it doesn't pull in any more Eclipse code).

Scott


John F. Patterson wrote:
> Scott:
>
> Upon further consideration, I am inclined to relent somewhat regarding
> getAdapter.
>
> I have had two reasons for preferring a more specific name.
>
> 1) It is more readable and more understandable.
>
> 2) I have felt that the getAdapter was not sufficiently versatile to
> handle complex construction patterns, such as those requiring that
> arguments be passed on construction. I felt that a different getXXX
> would be needed to signal the difference in construction pattern. I now
> believe that while this is helpful, it is not essential.
>
> Imagine that we have an adaptable object, i.e., it has a getAdapter
> method and that its implementation "knows" about a variety of different
> factories. When the getAdapter is called and the Class is passed in,
> the adaptable object will retrieve the appropriate factory from a
> registry. (I'm not saying where or what the registry will be, but I
> assume we agree that some sort of registry will exist.) Then, the
> adaptable can "reflect" on the returned factory. If it is a type that
> it "knows" about. It can call the appropriate creation method and pass
> the required information. If it is not one that it knows about, it will
> return null.
>
> So, my second argument against getAdapter is erased and I am left with
> the improved understandability.
>
> I still think that when creating an extensible component that is an
> essential feature of how some offering works, we might still want to use
> the getXXX method even if it looks a lot like getAdapter. I'm not sure
> getVenue in the example we have been discussing is a very compelling
> example.
>
> In any case, I am down to only an aesthetic argument against getAdapter.
>
> jfp
Using IAdaptable, but avoiding eclipse dependencies [message #586917 is a reply to message #586904] Tue, 28 June 2005 18:53 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

I might not have thought this through thoroughly enough, but I think you
can probably still use the Adaptable pattern and avoid the eclipse
dependency.

There seem to be three relevant interfaces:

IAdaptable,
IAdapterManager, and
IAdapterFactory.

Let's take them one at a time.

IAdapatble -- I am not sure what the value is to using this interface.
It has only one method -- getAdapter. I assume you could use the method
without designating that an ECF interface extends IAdaptable. (This is
what you have been doing.) I have not yet seen the functionality exposed
to you that makes it useful to treat all adaptable objects as a common type.

IAdapterManager -- I think you could define your own registry --
IEcfRegistry -- that does most of what IAdapterManager does. ECF
entities would be expected to check this registry for adaptables.
Eclipse-based implementations could extend the registry to call
Platform.getAdapterManager(), which would extend the registration
functionality to embrace eclipse when it is available.

IAdapterFactory -- I think you could define an IEcfAdapterFactory that
looks a lot like IAdapterFactory. (You might want to change the method
names to avoid confusion -- or not.) A factory registering directly
with IEcfRegistry would need to be an IEcfAdapterFactory. If the same
factory needed to register with the platform, it would implement the
IAdapterFactory as well.

This hides the IAdaptable functionality under IEcfRegistry, so that it
is only seen by implementations of that registry that can rely on eclipse.

Thus, I think you can avoid the eclipse dependency in the API. The
question is whether it is worth the interface duplication and extra
redirection to avoid that dependency.

jfp
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586925 is a reply to message #586917] Tue, 28 June 2005 20:47 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

John F. Patterson wrote:
> Scott:
>
> I might not have thought this through thoroughly enough, but I think you
> can probably still use the Adaptable pattern and avoid the eclipse
> dependency.
>
> There seem to be three relevant interfaces:
>
> IAdaptable,
> IAdapterManager, and
> IAdapterFactory.
>
> Let's take them one at a time.
>
> IAdapatble -- I am not sure what the value is to using this interface.
> It has only one method -- getAdapter. I assume you could use the method
> without designating that an ECF interface extends IAdaptable. (This is
> what you have been doing.)

Right.
I have not yet seen the functionality exposed
> to you that makes it useful to treat all adaptable objects as a common
> type.

Well, the potential for having all the existing code (within Eclipse)
that knows how to talk to IAdaptables would be of great use for
integration with the existing eclipse plugins and UI. For example,
IResource...the core interface for all eclipse resources (e.g.
IProjects, IFiles, directories, workspace, etc) extends IAdaptable.
This means containers could be passed around in the existing UI where
those entities are passed around now...


>
> IAdapterManager -- I think you could define your own registry --
> IEcfRegistry -- that does most of what IAdapterManager does. ECF
> entities would be expected to check this registry for adaptables.
> Eclipse-based implementations could extend the registry to call
> Platform.getAdapterManager(), which would extend the registration
> functionality to embrace eclipse when it is available.
>
> IAdapterFactory -- I think you could define an IEcfAdapterFactory that
> looks a lot like IAdapterFactory. (You might want to change the method
> names to avoid confusion -- or not.) A factory registering directly
> with IEcfRegistry would need to be an IEcfAdapterFactory. If the same
> factory needed to register with the platform, it would implement the
> IAdapterFactory as well.
>
> This hides the IAdaptable functionality under IEcfRegistry, so that it
> is only seen by implementations of that registry that can rely on eclipse.

We can/could very well do this (have a registry and adapter manager that
is unique to ECF), but is it worth doing? That is, wouldn't we
basically be duplicating all the mechanism in eclipse? Perhaps we
should just include these interfaces/classes in non-eclipse (RCP)
applications? Actually, I see by checking that all these classes are in
cluded in the org.eclipse.core.runtime plugin which is part of RCP...

>
> Thus, I think you can avoid the eclipse dependency in the API. The
> question is whether it is worth the interface duplication and extra
> redirection to avoid that dependency.

Right. So we've already got a dependency on OSGi. We could very well
add a dependency on Eclipse RCP (which is OSGi++) and get all this
mechanism. I'm beginning to think that adding Eclipse RCP as a
dependency might not be such a terrible thing...even for servers...as
RCP has done some really useful things that make life for ECF
client/servers *much* nicer...e.g. the buddy loader mechanisms (for
classloading of serialized classes), the IAdaptable, etc. interfaces and
registry implementations, the plugin id, the Platform.* services...all
of which are significant enhancements to OSGi v3 useful for ECF.

Please comment if you have views on this (whether to add a dependency on
ECF to the RCP core plugins).

Scott
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586933 is a reply to message #586925] Wed, 29 June 2005 14:38 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

My comments are embedded.

jfp

Scott Lewis wrote:

> Hi John,
>
> John F. Patterson wrote:
>
>> Scott:
>>
>> I might not have thought this through thoroughly enough, but I think
>> you can probably still use the Adaptable pattern and avoid the
>> eclipse dependency.
>>
>> There seem to be three relevant interfaces:
>>
>> IAdaptable,
>> IAdapterManager, and
>> IAdapterFactory.
>>
>> Let's take them one at a time.
>>
>> IAdapatble -- I am not sure what the value is to using this
>> interface. It has only one method -- getAdapter. I assume you could
>> use the method without designating that an ECF interface extends
>> IAdaptable. (This is what you have been doing.)
>
>
> Right.
> I have not yet seen the functionality exposed
>
>> to you that makes it useful to treat all adaptable objects as a
>> common type.
>
>
> Well, the potential for having all the existing code (within Eclipse)
> that knows how to talk to IAdaptables would be of great use for
> integration with the existing eclipse plugins and UI. For example,
> IResource...the core interface for all eclipse resources (e.g.
> IProjects, IFiles, directories, workspace, etc) extends IAdaptable.
> This means containers could be passed around in the existing UI where
> those entities are passed around now...
>
This seems more like an argument for IResource than an argument for
IAdaptable. The question is not whether eclipse-based implementations
of ECF entities might want to also implement eclipse-specific
interfaces. The question is whether the ECF needs to be "aware" of
these interfaces. We got dragged into IAdapterFactory because we are
leaning towards either using or duplicating its functionality as a
fundamental part of ECF, not because we simply want to ensure
compatibility with it.

>
>>
>> IAdapterManager -- I think you could define your own registry --
>> IEcfRegistry -- that does most of what IAdapterManager does. ECF
>> entities would be expected to check this registry for adaptables.
>> Eclipse-based implementations could extend the registry to call
>> Platform.getAdapterManager(), which would extend the registration
>> functionality to embrace eclipse when it is available.
>>
>> IAdapterFactory -- I think you could define an IEcfAdapterFactory
>> that looks a lot like IAdapterFactory. (You might want to change the
>> method names to avoid confusion -- or not.) A factory registering
>> directly with IEcfRegistry would need to be an IEcfAdapterFactory.
>> If the same factory needed to register with the platform, it would
>> implement the IAdapterFactory as well.
>>
>> This hides the IAdaptable functionality under IEcfRegistry, so that
>> it is only seen by implementations of that registry that can rely on
>> eclipse.
>
>
> We can/could very well do this (have a registry and adapter manager
> that is unique to ECF), but is it worth doing? That is, wouldn't we
> basically be duplicating all the mechanism in eclipse? Perhaps we
> should just include these interfaces/classes in non-eclipse (RCP)
> applications? Actually, I see by checking that all these classes are
> in cluded in the org.eclipse.core.runtime plugin which is part of RCP...
>
>>
>> Thus, I think you can avoid the eclipse dependency in the API. The
>> question is whether it is worth the interface duplication and extra
>> redirection to avoid that dependency.
>
>
> Right. So we've already got a dependency on OSGi. We could very well
> add a dependency on Eclipse RCP (which is OSGi++) and get all this
> mechanism. I'm beginning to think that adding Eclipse RCP as a
> dependency might not be such a terrible thing...even for servers...as
> RCP has done some really useful things that make life for ECF
> client/servers *much* nicer...e.g. the buddy loader mechanisms (for
> classloading of serialized classes), the IAdaptable, etc. interfaces
> and registry implementations, the plugin id, the Platform.*
> services...all of which are significant enhancements to OSGi v3 useful
> for ECF.
>
I guess I lean toward avoiding the dependency on eclipse packages, if we
can do it and still have some implementations be first-class eclipse
citizens. I think the technique I mentioned above for avoiding
adaptable dependencies is acceptable, if duplicative. I looked over
IResource and I would not be inclined to duplicate this interface, if we
really need it.

> Please comment if you have views on this (whether to add a dependency
> on ECF to the RCP core plugins).

I am inclined to avoid a dependency on the RCP, but this is low on my
list of virtues for ECF. To a first approximation, most of the
applications I envision will probably operate in an RCP environment.

It would be nice to hear from others about this issue.

>
> Scott
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586940 is a reply to message #586933] Wed, 29 June 2005 21:16 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

John F. Patterson wrote:
<stuff deleted>

>>
> This seems more like an argument for IResource than an argument for
> IAdaptable. The question is not whether eclipse-based implementations
> of ECF entities might want to also implement eclipse-specific
> interfaces. The question is whether the ECF needs to be "aware" of
> these interfaces. We got dragged into IAdapterFactory because we are
> leaning towards either using or duplicating its functionality as a
> fundamental part of ECF, not because we simply want to ensure
> compatibility with it.

True enough. But I think the main point is that whether we introduce a
dependency on IAdaptable, IAdapterFactory or IResource, it's all a
dependency. It may be that given the need for OSGi, already, however,
and the strength of RCP as an OSGi implementation, however, that RCP
might be the best way to go. Getting ECF to run properly on (e.g.)
another OSGi server implementation probably wouldn't be easy...precisely
because the other non-RCP implementations are missing things like doing
classloading across bundles/plugins, having a symbolic identifier for a
bundle/plugin (that is guaranteed unique), etc. So currently I don't
see much positive for ECF in making our libraries OSGi dependent only.
That is, making them dependent upon RCP (which includes IResource,
IAdaptable, IAdapterFactory, etc) isn't increasing the requirements on
server runtimes very much, and it affects (in a positive way) ECF
applications drastically (because of the RCP additions for classloading,
symbolic names for plugins, etc) that have not yet been added to the
OSGi specification.

<stuff deleted>

>>
> I guess I lean toward avoiding the dependency on eclipse packages, if we
> can do it and still have some implementations be first-class eclipse
> citizens. I think the technique I mentioned above for avoiding
> adaptable dependencies is acceptable, if duplicative. I looked over
> IResource and I would not be inclined to duplicate this interface, if we
> really need it.
>
>> Please comment if you have views on this (whether to add a dependency
>> on ECF to the RCP core plugins).
>
>
> I am inclined to avoid a dependency on the RCP, but this is low on my
> list of virtues for ECF. To a first approximation, most of the
> applications I envision will probably operate in an RCP environment.

Right. And with all the emphasis on RCP with 3.1+, the new PDE support
for building RCP apps (which is going to be played up dramatically over
the next few months I believe), and even some people on this mailing
list who have already used ECF to build RCP clients, having/using RCP
might very well be worth the dependency (all the interfaces/classes
we've been discussing plus a lot more).

>
> It would be nice to hear from others about this issue.

Yes. I'll stop talking now and listen further.

(other) ECFers: What say you?

Scott
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586948 is a reply to message #586940] Thu, 30 June 2005 06:24 Go to previous messageGo to next message
Eike Stepper is currently offline Eike StepperFriend
Messages: 6682
Registered: July 2009
Senior Member
scott, john,

sorry that i interfere your discussion, but as a system architect i'm quite interested in the general dependency discussion.

am i right that most of this deeper thread is about using IAdaptable (and consorts), which all reside in org.eclipse.core.runtime?
am i further right that this is one of the lowest, non-osgi package available in the eclipse stack?
is there any way to make an eclipse based application (IDE, RCP or whatsoever) without org.eclipse.core.runtime?

and given that the *E* in ECF is for *Eclipse*, i really don't understand, why ECF shouldn't be using the core of it. am i missing something?

cheers
/eike


p.s. on the other hand i just realized that the EMF team chose to implement an own Notifier/Adapter pattern (not IAdaptable based). i can't remember that there was a big discussion about that, but i know that they want to support standalone apps as a top level requirement. in addition EMF uses an optional dependency to get rid of core.resources in standalone or RCP apps.


Re: Using IAdaptable, but avoiding eclipse dependencies [message #586960 is a reply to message #586948] Thu, 30 June 2005 06:34 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi Eike,

Eike Stepper wrote:
> scott, john,
>
> sorry that i interfere your discussion, but as a system architect i'm
> quite interested in the general dependency discussion.
>
> am i right that most of this deeper thread is about using IAdaptable
> (and consorts), which all reside in org.eclipse.core.runtime?

Yes.

> am i further right that this is one of the lowest, non-osgi package
> available in the eclipse stack?

Yes, I would say so.

> is there any way to make an eclipse based application (IDE, RCP or
> whatsoever) without org.eclipse.core.runtime?

Not really.

>
> and given that the *E* in ECF is for *Eclipse*, i really don't
> understand, why ECF shouldn't be using the core of it. am i missing
> something?

I think the only thing at issue is the development of servers. There's
no particular reason why any Eclipse classes are needed for servers.
It's completely conceivable (even likely), however, that ECF could be
used to develop servers...so adding a dependency for server development
could be a negative.

It's not a very large negative, however, IMHO...especially since now
some folks on the equinox mailing list are discussing running the RCP
for server runtime.

And actually, I run the ecftcp://composent.com:3282/server test server
using the org.eclipse.core.runtime classes anyway. It's not really a
problem at all.

Scott
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586972 is a reply to message #586960] Thu, 30 June 2005 12:46 Go to previous messageGo to next message
Li-Te Cheng is currently offline Li-Te ChengFriend
Messages: 16
Registered: July 2009
Junior Member
Here's my take on this:

Since this is the ECF project - i.e. "Eclipse" communications framework
- I don't see a problem with having Eclipse dependencies like IResource,
etc. (as pointed out by Eike earlier)

Given the interest in using IAdaptable, I think it's worth reusing the
Eclipse code rather than duplicate it whenever possible. Otherwise, the
ECF project, being small as it is, will be burdened with maintaining a
separate version every time Eclipse decides to revise itself (which
appears to be occurring on an annual basis :) ).

I don't see a IResource dependency problem. getAdapter - the sole
method of IAdaptable - returns Object, not IResource. So the only taint
in the ECF API will show up in whatever interfaces extend IAdaptable.
It is entirely up to the implementor of ISharedObjectContainer etc to
become dependent on IResource and its ilk or not.

Also, because the IResource, IFile, etc bindings are done at runtime via
extension point declarations then IResource will only show up if anyone
decides to pass in IResource.class or its ilk in the parameter for
getAdapter, and only if there is an extension set up for IResource-like
things. This is guaranteed to work if you are running ECF in the
Eclipse IDE, since the IDE does have IResource, etc registered to the
IAdapter extension point.

In a RCP application, this is not guaranteed to be the case (the RCP,
being, a stripped down Eclipse platform has IAdaptable via
org.eclipse.core.runtime, but not org.eclipse.core.resources, which is
the home of IResource et al).

To sum up:

1. I think it's ok to have some/minimal/manageable Eclipse
dependencies. After all, this is the ECLIPSE communications framework,
not a JCP/JSR/IETF thing. I'd worry about grand generalizations later
when the ECF is fully fleshed out and has strong traction in the
community. From Eclipse's POV, this is good, because ECF will benefit
from leveraging and working with other Eclipse efforts.

2. The IResource dependency - if any - is really a loose runtime
dependency, not a tight compile-time dependency - thanks to the Eclipse
extension point mechanism. You will not see any sign of IResource in
the API - only IAdaptable. IAdaptable and its ilk are defined in a
plugin that shows up in the RCP and Eclipse IDE - so RCP and Eclipse IDE
developers can rejoice.

3. You may see IResource in implementations of the API and calls to
getAdapter (i.e. non-java Interface code). So, I don't see the API
(the set of interface classes) being threatened by IResource. If
something like ISharedObjectContainer extended IResource, or mentions
IResource in its method signatures - then there is a problem. I think
it is much more appropriate to use IAdaptable instead, because then you
can easily convert it into an IResource via getAdapter if the
implementation supports it.

4. The only "taint" will be a dependency on IAdaptable. If you really
wanted to provide ECF as a non-Eclipse set of jars, then you would need
to ship the ECF jars, the OSGI stuff, and one other "pure Eclipse" jar =
org.eclipse.runtime.jar (where IAdaptable is defined). If you consider
the API to be just the set of interfaces, then that should compile fine
and dandy - you just have to be aware that if you are using an ECF
implementation that relies on the Eclipse platform, then it will not
work in ECF-standalone-mode.

You can still use the getAdapter pattern outside of Eclipse, you just
can't rely on the nice registry stuff that Eclipse offers. So, you
cannot do

public Object getAdapter(Class type) {
Platform.getAdapterManager().getAdapter(this,type);
}

unless you want to manually start up the Eclipse platform stuff in
org.eclipse.runtime.jar. However, you can do manual getAdapter()
stuff, if the implementor desires that, e.g.

public Object getAdapter(Class type) {
if ( type == FooClass )
return new FooClass();
}

or you can make up your own registry, etc. That will work fine outside
of Eclipse, and works fine inside of Eclipse too. But that's entirely
an implementation decision, not a problem of the API.

5. So, the real problem is if people are willing to accept Eclipse-only
implementations of the ECF API, or if people prefer having ECF
implementations that are guaranteed to work 100% both inside and outside
of Eclipse. Implementing an Eclipse-only solution is easier, since you
can do Platform.getAdapterManager().getAdapter(...). In other words:

Does it matter when I write a piece of code using ECF, am I guaranteed
that it will not break whether I'm inside or outside of Eclipse
regardless of what ECF implementation I use?

Since we can't control implementations, I would say it's ok that code
using ECF will break outside of Eclipse when the selected ECF
implementation works only in Eclipse - pick another
provider/implementation that works outside. I suppose it would be nice
if the ECF API indicated this (e.g.
ISharedObjectContainer.requiresEclipsePlatform() ), although it's
probably better if the registry of available ISharedObjectContainers
would not have "Eclipse-only" containers if you're running outside of
Eclipse.

6. At the end of the day, extending IAdaptable means adding one jar to
get the definition of IAdaptable and nothing else. As Scott mentioned
in the case of writing a server with this stuff - it's not a big deal.

If you really want to be a purist, it's probably ok to define getAdapter
methods without extending IAdaptable, and let the implementation worry
about extending IAdaptable or not. Now we no longer have to include
org.eclipse.core.runtime.

And I suppose the ECF could define something like IECFAdaptable and then
use some kind of trickery with the Eclipse fragment mechanism to somehow
bind IECFAdaptable to IAdaptable (so if you were in RCP or Eclipse-land,
IECFAdaptable and IAdaptable were exchangeable, but in standalone mode,
they're not). You could also pull off this trick easier with AOP. But
either approach sounds like more work than it's worth...

Li-Te

Scott Lewis wrote:
> Hi Eike,
>
> Eike Stepper wrote:
>
>> scott, john,
>>
>> sorry that i interfere your discussion, but as a system architect i'm
>> quite interested in the general dependency discussion.
>>
>> am i right that most of this deeper thread is about using IAdaptable
>> (and consorts), which all reside in org.eclipse.core.runtime?
>
>
> Yes.
>
>> am i further right that this is one of the lowest, non-osgi package
>> available in the eclipse stack?
>
>
> Yes, I would say so.
>
>> is there any way to make an eclipse based application (IDE, RCP or
>> whatsoever) without org.eclipse.core.runtime?
>
>
> Not really.
>
>>
>> and given that the *E* in ECF is for *Eclipse*, i really don't
>> understand, why ECF shouldn't be using the core of it. am i missing
>> something?
>
>
> I think the only thing at issue is the development of servers. There's
> no particular reason why any Eclipse classes are needed for servers.
> It's completely conceivable (even likely), however, that ECF could be
> used to develop servers...so adding a dependency for server development
> could be a negative.
>
> It's not a very large negative, however, IMHO...especially since now
> some folks on the equinox mailing list are discussing running the RCP
> for server runtime.
>
> And actually, I run the ecftcp://composent.com:3282/server test server
> using the org.eclipse.core.runtime classes anyway. It's not really a
> problem at all.
>
> Scott
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586986 is a reply to message #586972] Thu, 30 June 2005 13:28 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott, Elke, Li-Te:

I think it is important to distinguish between an API dependency and an
implementation dependency. I assume we are only talking about an API
dependency. I fully expect the reference implementation of the ECF to
be thoroughly integrated into eclipse and taking advantage of as many
eclipse-specific capabilities as it can. The issue is whether the API
should have an eclipse dependency.

While I am not inclined to go far out of my way to avoid an eclipse
dependency, keeping the API independent has some virtues.

On the specific issue of IAdaptable we could simply put getAdapter
methods on our interfaces and leave the issue of a registry
unspecified. It would be up to each implementation to provide a
registry (and eclipse already has one.)

If we adopted this approach, the only issue that we are currently facing
is whether our adaptable interfaces should extend IAdaptable or simply
use the getAdapter method. To preserve independence, I would vote for
simply using the getAdapter method and not extending IAdaptable.
Frankly, I have not seen what the API gains by extending the IAdaptable
interface. Eclipse-based implementations that can gain some value from
having their ECF objects extend IAdaptable can always do that.

So ... if I follow my own reasoning, this issue of IAdaptable is turning
into a question of whether the ECF needs to specify an approach to a
registry or should just leave that unspecified. I was arguing earlier
for specifying the ecf approach to a registry, but perhaps it would be
better to leave this unspecified in the API. The eclipse-based
implementation would use the adaptable capability provided by eclipse.
Other implementations would need to provide their own approach, but the
API won't tell them how to do it.

How does this sound?

jfp
Re: Using IAdaptable, but avoiding eclipse dependencies [message #586993 is a reply to message #586986] Thu, 30 June 2005 19:23 Go to previous messageGo to next message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John and Li-Te,

First, thanks Li-Te (and John) for your very thorough analysis of the
dependency situation...you are quite right that there's no issue for
IResource...I brought it up spuriously in a fit of fatigue.

As John says below the important issue is API dependency and the only
interface at issue for the time being is IAdaptable. For API design
reasons it would be convenient to have IContainer and/or
ISharedObjectContainer actually extend IAdaptable, which puts an API
dependency on IAdaptable.

Given everyone's comments and analysis I think the thing to do is to put
in an API dependency on IAdaptable only, and leave the implementers to
create the registry. For Eclipse clients this is trivial (using
existing IAdapterFactory and etc), and for servers they can do this or
not depending upon the needs. Servers will have to provide runtime
access to just one non-ECF interface class:
org.eclipse.core.runtime.IAdaptable . The server implementer can choose
to use RCP to get this, or just provide access to IAdaptable on the the
classpath for the server (as I do now with the 'generic server'
implementation). Until further review/approval IAdaptable will be the
only such dependency in the ECF API.

Of course, ECF clients (and servers) built on Eclipse/Eclipse RCP will
have absolutely no problem with any of this, because both will always
have access to IAdaptable, IAdapterFactory, Platform, etc. It will only
be an issue for server/provider implementers, and they can choose to
implement their own registries, etc. if they wish.

So, to sum up...I'm inclined to add/leave an API dependency to
IAdaptable, nothing else, and introduce this to the API within the next
couple of days (along with other things).

Thanks to all for their consideration on this.

Scott



John F. Patterson wrote:
> Scott, Elke, Li-Te:
>
> I think it is important to distinguish between an API dependency and an
> implementation dependency. I assume we are only talking about an API
> dependency. I fully expect the reference implementation of the ECF to
> be thoroughly integrated into eclipse and taking advantage of as many
> eclipse-specific capabilities as it can. The issue is whether the API
> should have an eclipse dependency.
>
> While I am not inclined to go far out of my way to avoid an eclipse
> dependency, keeping the API independent has some virtues.
>
> On the specific issue of IAdaptable we could simply put getAdapter
> methods on our interfaces and leave the issue of a registry
> unspecified. It would be up to each implementation to provide a
> registry (and eclipse already has one.)
>
> If we adopted this approach, the only issue that we are currently facing
> is whether our adaptable interfaces should extend IAdaptable or simply
> use the getAdapter method. To preserve independence, I would vote for
> simply using the getAdapter method and not extending IAdaptable.
> Frankly, I have not seen what the API gains by extending the IAdaptable
> interface. Eclipse-based implementations that can gain some value from
> having their ECF objects extend IAdaptable can always do that.
>
> So ... if I follow my own reasoning, this issue of IAdaptable is turning
> into a question of whether the ECF needs to specify an approach to a
> registry or should just leave that unspecified. I was arguing earlier
> for specifying the ecf approach to a registry, but perhaps it would be
> better to leave this unspecified in the API. The eclipse-based
> implementation would use the adaptable capability provided by eclipse.
> Other implementations would need to provide their own approach, but the
> API won't tell them how to do it.
>
> How does this sound?
>
> jfp
On EcfService [message #587010 is a reply to message #586833] Fri, 01 July 2005 13:57 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Scott:

I want to return to this issue of naming of ECF Services. I am getting
pretty tired of trying to use the term venue and I tend to agree with
Peter that we should call them what they are. I do not like your
proposal of a "ContainerService" or a "SharedObjectService" because that
focuses too much on the mechanism, not on the objective. Frankly, many
of these services (like presence) will largely hide the underlying
mechanics; many application developers might not understand why they are
being called SharedObjectServices.

Of the available proposals, I prefer the term EcfService, but there are
a couple of issues about usage.

1) If we use the term EcfService, is it "EcfService" or "ECFService".

2) For specific instances of an EcfService, must they include the "Ecf"
prefix. For example, must it be "IEcfPresenceService" or could it
simply be "IPresenceService". I would prefer the latter.

jfp

BTW: I am still working on a Presence Service proposal, which is why
these things are on my mind.

Scott Lewis wrote:

> Hi John/Peter,
>
> Peter Nehrer wrote:
>
>> John, Scott,
>>
>> I have a couple of thoughts that are not directly related to Presence
>> API, but I think they're relevant to this discussion nevertheless.
>>
>> First, I'd like to try to rescue the term "service". It is such a
>> generic term that it would be a shame to give it away to OSGi. As far
>> as I understand, services in OSGi are basically public interfaces
>> that a bundle exposes. Bundles publish services in a common registry,
>> which can then be queried by other bundles (and service references
>> obtained). Services are what bundles do.
>>
>> An ECF container, on the other hand, is not a bundle. The services it
>> may or may not provide are not in the same category as OSGi bundle
>> services. Typically, a conversation is carried in one of the two
>> contexts, so it shold not cause much confusion, and in the cases that
>> it might, we could say "ECF service" vs. "OSGi service". I don't
>> really have strong feelings toward one term or another and I'll get
>> used to any one we settle on, but I thought I'd bring this up.
>
>
> I also don't have particularly strong feelings about what ECF
> 'services' are called. I just don't want to cause confusion by
> overloading an already overloaded term. I do think that it would be
> nice to somehow use the concepts of a 'containerservice' or 'shared
> object service' somehow.

[stuff deleted]
Re: On EcfService [message #587023 is a reply to message #587010] Fri, 01 July 2005 14:31 Go to previous messageGo to next message
Eike Stepper is currently offline Eike StepperFriend
Messages: 6682
Registered: July 2009
Senior Member
hi,

my general, personal naming preferences (without much explanation) are:

- if a name contains an abbreviation (ECF), it is treated as a complete normal syllable => IEcfService
- a derived class always (or as often as reasonable) reapeats the whole super class name => IEcfPresenceService
- i don't like these general prefixes at all. java has hierarchical package names to unflatten the namespace => IService (IPresenceService)

i think that names are important, but it's not worth *long* discussions.
it's more important that someone settles a consistent rule set (with no obvious misleadings) and then dictates that ;-)

cheers
/eike



John F. Patterson schrieb:
> Scott:
>
> I want to return to this issue of naming of ECF Services. I am getting
> pretty tired of trying to use the term venue and I tend to agree with
> Peter that we should call them what they are. I do not like your
> proposal of a "ContainerService" or a "SharedObjectService" because that
> focuses too much on the mechanism, not on the objective. Frankly, many
> of these services (like presence) will largely hide the underlying
> mechanics; many application developers might not understand why they are
> being called SharedObjectServices.
>
> Of the available proposals, I prefer the term EcfService, but there are
> a couple of issues about usage.
>
> 1) If we use the term EcfService, is it "EcfService" or "ECFService".
>
> 2) For specific instances of an EcfService, must they include the "Ecf"
> prefix. For example, must it be "IEcfPresenceService" or could it
> simply be "IPresenceService". I would prefer the latter.
>
> jfp
>
> BTW: I am still working on a Presence Service proposal, which is why
> these things are on my mind.
>
> Scott Lewis wrote:
>
>> Hi John/Peter,
>>
>> Peter Nehrer wrote:
>>
>>> John, Scott,
>>>
>>> I have a couple of thoughts that are not directly related to Presence
>>> API, but I think they're relevant to this discussion nevertheless.
>>>
>>> First, I'd like to try to rescue the term "service". It is such a
>>> generic term that it would be a shame to give it away to OSGi. As far
>>> as I understand, services in OSGi are basically public interfaces
>>> that a bundle exposes. Bundles publish services in a common registry,
>>> which can then be queried by other bundles (and service references
>>> obtained). Services are what bundles do.
>>>
>>> An ECF container, on the other hand, is not a bundle. The services it
>>> may or may not provide are not in the same category as OSGi bundle
>>> services. Typically, a conversation is carried in one of the two
>>> contexts, so it shold not cause much confusion, and in the cases that
>>> it might, we could say "ECF service" vs. "OSGi service". I don't
>>> really have strong feelings toward one term or another and I'll get
>>> used to any one we settle on, but I thought I'd bring this up.
>>
>>
>>
>> I also don't have particularly strong feelings about what ECF
>> 'services' are called. I just don't want to cause confusion by
>> overloading an already overloaded term. I do think that it would be
>> nice to somehow use the concepts of a 'containerservice' or 'shared
>> object service' somehow.
>
>
> [stuff deleted]


A Presence Venue Proposal [message #587034 is a reply to message #582093] Fri, 08 July 2005 21:22 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: john_patterson.us.ibm.com

Presence Venue API

The outline for this document is as follows:

1 -- Introduction
2 -- PresenceVenue
2.1 -- Overview
2.2 -- PresenceVenue API
2.3 -- PresenceVenue Example code
3 -- MyPresence and BuddyPresence
3.1 -- Overview
3.2 -- MyPresence and BuddyPresence API
3.3 -- MyPresence and BuddyPresence Example code

As in earlier postings, Li-Te Cheng has helped me prepare this.

1 -- Introduction
=============

This presentation of a proposed Presence Venue API (I use the term venue
to avoid confusion with OSGi services.) is both more complicated and
less complete than I had hoped for. It became more complicated because
I wanted to capture a rather general notion of presence that could be
applied to infomation other than simply one's online status. This has
made the PresenceVenue objects rather abstract. If you have the
patience, I think you will find that it is an interesting Pub/Sub
(Publish and Subscribe) service that could be used to provide awareness
of a lot more than just online status. If you don't have the patience,
skip down to the Section 3. This section describes specializations that
are tailored to the buddy list case.

These APIs are also less complete than I had originally planned for two
reasons. The first and most important is that I have split out or
simply not described several services that the PresenceVenue will
interact with. The most obvious of these is that I have not
incorporated chat into the PresenceVenue. I think of this as a
logically distinct service. Presumably, I will be able to activate a
chat from a different venue (MeetingVenue?) using the ID representing a
person. A directory service is also logically distinct and is
presumably where I discover the IDs that permit me to identify others.
Finally, the most problematic service is a storage service. Frankly, I
am not entirely certain how to deal with this. There is a tendency for
presence services to bundle in a storage service, but I should be free
to save my buddy list anywhere I want. I'm just skipping the storage
service for the time being.

The second reason that the API is somewhat incomplete is that it builds
on an abstract notion of property-value pairs, but it does not say much
about any standardized names or values. Any specific use of the
PresenceAPI will require conventional (if not standardized) names and
values if any consequence other than simply presenting them to the user
is desired. I think of these a little like Mime types. Some will be
standardized, some will be defacto conventions, and some will be novel
and idiosyncratic uses. I have provided the conventions for the
BuddyListVenue, but we might want to define other properties and values
as well.

2 -- PresenceVenue
=================

2.1 -- PresenceVenue Overview
-----------------------------------------------
I consider this a simple service, though it is somewhat abstract. It is
simpler than one might expect because I am not trying to handle IM,
account management, directory, or storage services. Chat is logically a
different service (venue) and I think it is best to keep it out of the
story for the time being. I'll try to work up something on chat later.
I am also not trying to deal with Smack's notion of account management.
I haven't decided where is the right place to put that.

A general pattern that you will find in this API is that objects
listening for remote events begin as inert with most of their
interesting capabilities not yet available. This is so that listeners
may be added before any notifications start arriving. The general
pattern is:

public interface INotifiable {
public void activate();
public boolean isActive();
public void deactivate();

public void addNotifiableListener();
public void removeNotifiableListener();
}

The actual methods to activate or deactivate might have different names
(e.g., subscribe and dispose), but the basic idea is the same. The
lifecycle is to go from inactive to active and then to inactive, though
this last step might be implicit due to other events. A question that
usually differs from one situation to another is whether an object can
be reactivated. I have not taken a stand on this. If forced to adopt a
uniform policy, I would argue that objects cannot be reactivated and
that one must obtain a new version of the INotifiable befrore
reactivating it.

In the PresenceVenue API, there are five event-receiving objects:
IPresenceVenue, ISubscribable, ICoreSubscribable, IPublishable, and
ICorePublishable. I have adopted the attitude that a presence object is
simply a bag of property-value pairs. I like this because it is easily
serializable, provides a modicum of structure, but imposes very few
constraints on what may be represented. I have defined a new
PresenceProperty class, so that the values may be byte arrays, which
makes the whole thing even more general. ISubscribable and IPublishable
handle only the lifecycle methods for the presence objects, not the
property access methods. They are expected to be extended by
specializations that are "aware" of the ways that the properties are
being used. ICoreSubscribable and ICorePublishable are basic
implementations that handle properties in a very generic fashion. They
are intended to be used by ISubscribable and IPublishable extensions
that provide less generic methods for manipulating the properties.

In some sense, this API is not complete because it will be necessary to
establish some standard properties and their values. For presence types
that are well known and well standardized (like person presence) we will
probably want to define an extension of ISubscribable (e.g.,
IBuddyPresence) that provides simple getters for the standard
properties. The pattern I expect is that IBuddyPresence will extend
ISubscribable. When the IPresenceVenue is asked to get an
IBuddyPresence, it will first create an ICoreSubscribable and then call
its getAdapter to "wrap" the IBuddyPresence around it. The
IBuddyPresence will expose these generic capabilities via its own
specialized interfaces. Section 3 illustrates this pattern.

Clients may subscribe to or publish presence objects. To subscribe to a
presence object, you first get an ISubscribable object. This only
provides a local representation of the subscribable. It contains the
address of the subscribable (its ID), but nothing else. To get the
Subscribable working, you must either fetch it or subscribe to it.
Fetching is a one-time subscription that simply collects the current
state of the object. Subscribing allows one to track changes to the
object. Fetching and subscribing activate the object (once the return
event occurs). Once the object is activated, you can get the properties
and it will return the most recently available values. Getting a
property does not cause a network event.

To publish an object, you must first create an IPublishable object. If
a client publishes a presence object, then it is the only one who may
change the state of that object. The separation of the IPublishable
from the ISubscribable helps avoid the impression that applications and
components may set the properties of presence objects that they do not
manage. The ICorePublishable interface provides the methods needed to
set properties, publish the changes, and handle requests to subscribe to
the IPublishable. It also extends ICoreSubscribable, which means you
may subscribe to your published objects. While this is mostly not
necessary, it can be useful in server-managed presence to ensure that
others are seeing what you think they should see. .Setting properties
does not cause a network event. To ensure that others become aware of
the changes, the application must publish the object. The object is not
really active until after the first publish event.

The most complicated aspect of this API is the subscription process. To
begin with, the API does not deal with how one discovers a subscribable
worth subscribing to. Presumably, the IDs (URLs?) for interesting
presence objects are simply well-known, found in a directory, passed via
email, or available as properties on other well-known presence
objects. I imagine subscriptions happening in one of two ways. The
first, and simplest, assumes that a server will act as an intermediary
and permit or deny subscriptions on its own. This API does not model
how the server makes its decisions, so we only consider the behavior of
the subscribing client. It calls the subscribe method on the
ICoreSubscribable and receives back a subscribed or subscribeFailed
event on the listener.

The other approach assumes that the client managing the published object
will be involved in the decision about whether to permit or deny a
subscription attempt. The subscribing client calls subscribe as before,
which causes a subscriptionRequested event on the listener for the
corresponding ICorePublishable of the managing client. The application
on the managing client must either permit or deny the subscription
request. In this case, denial can only really be based on the ID of the
requesting client, since little else is available. When denying a
subscription, I have provided two ways to flag the denial: an int code
that should be enumerated from a set of, as yet, undefined reasons and a
String to provide a human-readable reason. When the reason for denial
is that I don't want to share this because of who you are, I assume the
application will want to mimic a reason that might otherwise be
automatically available.

There is one final wrinkle in this API. The presentation so far assumes
that the subscriber already knows the type of the objects being
subscribed to. This tends to be true in the buddy list case and most
other cases, but one could subscribe to a presence object without
knowing the appropriate interface type to ask for. Since the
ISubscribable objects are created before they are activated, one might
ask for an ISubscribable type that does not match the corresponding
IPublishable type. If we assume that the IPublishable always sets a
property (PresenceConstants.TYPE_PROPERTY) indicating the appropriate
subscribing type, then we could detect this problem. If one knows in
advance that the type is unknown, my recommendation is to get an
ICoreSubscribable and then use getAdapter once the preferred type is
known. If one mistakenly gets a subscribable of the wrong type, then I
would use getAdapter if it is exposed or deactivate the object and start
over with the correct type.

2.2 -- PresenceVenue API
---------------------------------------
The interfaces are provided below. I have not incorporated many
comments because I find it easier to get a sense of the whole interface
without them. I don't think there is too much that isn't either obvious
or described above. Please do not get too excited by my use of arrays
to represent a set of objects. I am happy to use Iterator, Enumeration,
or Vector, if people think that is better. Also, don't get too excited
by my use of the PresenceProperty object. I am happy to create a
different way to do this. I have tried to follow the Java Beans
convention in my use of listeners and listener events.

Some Helper Classes
--------------------------------
public class PresenceConstants {
public static final String NAME_PROPERTY = "Name";
public static final String TYPE_PROPERTY = "SubscribingType";
public static final String ONLINE_PROPERTY = "Online";
public static final String STATUS_PROPERTY = "Status";

public static final byte[] ONLINE_VALUE = "Online".getBytes();
public static final byte[] OFFLINE_VALUE = "Offline".getBytes();
public static final byte[] DO_NOT_DISTURB_VALUE = "Do not
disturb".getBytes();
}
public class PresenceProperty {
public PresenceProperty(String key, byte[] val);
public PresenceProperty(String key, String val);
public String getKey();
public byte[] getValue();
}
public class PresenceException extends Exception {
public PresenceException(int code, String reason);
public int getCode();
}

INotifiable
---------------
// INotifiable is a base interface for all the event receiving
objects. The objects start as inert (not connected) and must be activated
// to become useful. Listeners are not included, since they will
differ from specialization to specialization.

public interface INotifiable {
public ID getID();

public void activate();
public boolean isActive();
public void deactivate();
}

IPresenceVenue
-------------------------
public interface IPresenceVenue extends INotifiable {
// Available prior to activation
public void addPresenceVenueListener(IPresenceVenueListener pvl);
public void removePresenceVenueListener(IPresenceVenueListener pvl);

// Only available once the object is activated
// Logically, getSubscribable and createPublishable do not require that
the venue be active to be invoked;
// the results just cannot be activated until the venue is
activated, Therefore, it might be wise to treat them as
// unavailable so that the objects will not be created until they
can themselves be activated.

public ISubscribable getSubscribable(Class subscribableType,ID id)
throws PresenceException;
public IPublishable createPublishable(Class publishableType,ID id)
throws PresenceException;
}
public interface IPresenceVenueListener {
public void activated(PresenceVenueEvent pve);
public void lost(PresenceVenueEvent pve);
public void deactivated(PresenceVenueEvent pve);
}
public class PresenceVenueEvent extends java.util.EventObject {
public IPresenceVenue getPresenceVenue();
}

ISubscribable
---------------------
// ISubscribable is simply a common type for all subscribable objects.
It contains the methods that manage the object lifecycle, but not the
property access.
// ICoreSubscribable is the internal subscribable that must be
supported. Usually it will be contained within some other
// ISubscribable that exposes the ICoreSubscribable capabilities
in a specialized way.

public interface ISubscribable extends INotifiable {
// Available prior to activation
pulbic void fetch(); // Causes activation
public void subscribe(); // Causes activation
public boolean isSubscribed(); // Notice: Fetch makes it
possible to be active, but not subscribed.

public void addSubscribableListener(ISubscribableListener sl);
public void removeSubscribableListener(ISubscribableListener sl);

// Only available once the object is activated

public void unsubscribe() throws PresenceException;
// Unsubscribing does not necessarily deactive the object, since we
might continue to do fetches
}
public interface ISubscribableListener {
public void fetched(SubscribableEvent se);
public void fetchFailed(SubscribableEvent se);
public void subscribed(SubscribableEvent se);
public void subscribeFailed(SubscribableEvent se);
public void unsubscribed(SubscribableEvent se);
public void unsubscribeFailed(SubscribableEvent se);
public void deactivated(SubscribableEvent se);
}
public class SubscribableEvent extends java.util.EventObject {
public ISubscribable getSubscribable();
}

ICoreSubscribable
----------------------------
// ICoreSubscribable is the basic internal implementation of a
subscribable.
// It works directly with the subscribable's properties.
// Usually, it will be contained within a more specialized type that
provides less
// generic methods for accessing the properties.
// ICoreSubscribable is an ISubscribable so that it may be a
legitimate return
// value of IPresenceVenue.getSubscribable.

public interface ICoreSubscribable extends ISubscribable {
// Available prior to activation
public void addPropertyListener(IPropertyListener pl);
public void removePropertyListener(IPropertyListener pl);

public Object getAdapter(Class clazz) throws PresenceException;

// Only available once the object is activated
public byte[] getProperty(String key) throws PresenceException;
public PresenceProperty[] getProperties() throws PresenceException;
}
// The IPropertyListener and its event are not particular to the
ICoreSubscribable and could be used by
// specializations of ISubscribable as a way to expose property
changes.
public interface IPropertyListener {
public void changed(PropertyEvent pe);
}
public class PropertyEvent extends java.util.EventObject {
public ISubscribable getSubscribable();
public PresenceProperty[] getChangedProperties();
public PresenceProperty[] getNewProperties();
public String[] getDeletedProperties();
}

IPublishable
-------------------
// IPublishable is simply a common type for all publishable objects.
It contains only the methods for managing the lifecycle,
// but not property access or subscription acceptance.
// ICorePublishable is the internal publishable that must be
supported. Usually it will be contained within some other
// IPublishable that exposes the ICorePublishable capabilities in
a specialized way.

public interface IPublishable extends INotifiable {
// Available prior to activation
public boolean hasUnpublishedSettings();
public void publish(); // The first publish activates the object

public void addPublishableListener(IPublishableListener pl);
public void removePublishableListener(IPublishableListener pl);
}
public interface IPublishableListener {
public void published(PublishableEvent pe);
public void publishFailed(PublishableEvent pe);
public void deactivated(PublishableEvent pe);
}
public class CorePublishableEvent extends java.util.EventObject {
public IPublishable getPublishable();
}

ICorePublishable
--------------------------
// ICorePublishable is the basic internal implementation of a publishable.
// It works directly with the publishable's properties and it
manages requests to subscribe.
// Usually, it will be contained within a more specialized type that
provides less
// generic methods for manipulating the properties.
// ICorePublishable is an IPublishable so that it may be a
legitimate return
// value of IPresenceVenue.createPublishable.
// ICorePublishable extends ICoreSubscribable so that one may
subscribe to what is being
// published.

public interface ICorePublishable extends IPublishable, ICoreSubscribable {
// Available prior to activation
public void setProperty(String key,byte[] val); // Setting and
getting properties is available prior to activation
public void setProperties(PresenceProperty[] props);

public void addSubscriptionListener(ISubscriptionListener sl);
public void removeSubscriptionListener(ISubscriptionListener sl);

// Only available once the object is activated

public void permitSubscription(ID id) throws PresenceException;
public void denySubscription(ID id,int code,String reason) throws
PresenceException;
public void revokeSubscription(ID id,int code,String reason) throws
PresenceException;
}
// The ISubscriptionListener and its event are not particular to the
ICorePublishable and could be used by
// specializations of IPublishable as a way to expose subscription
requests.
public interface ISubscriptionListener {
public void subscriptionRequested(SubscriptionEvent pse);
public void unsubscribeNotification(SubscriptionEvent pse);
}
public class SubscriptionEvent extends java.util.EventObject {
public IPublishable getPublishable();
public ID getSubscriberID();
}

2.3 -- PresenceVenue Example code
-------------------------------------------------------

This just illustrates how the PresenceVenue API would be used to set up
your buddy list.

1. Create an IPresenceVenue and start it,

IPresenceVenue presenceVenue = (IPresenceVenue)
sharedContainer.getAdapter( IPresenceVenue.class );
presenceVenue.activate();

2. Set my online status

ICorePublishable cp = (ICorePublishable)
presenceVenue.createPublishable(ICorePublishable.class,myUse rID);
cp.setProperty( PresenceConstants.ONLINE_PROPERTY,
PresenceConstants.ONLINE_VALUE );
cp.setProperty( PresenceConstants.STATUS_PROPERTY, "I am ready to
work" );
cp.publish();

3. Create a buddy list,

ID[] myBuddyIds = getBuddyListFromSomewhere();
List<ISubscribable> myBuddies = new ArrayList<ISubscribable>();

for (ID id : myBuddyIds)
{
ICoreSubscribable myBuddy = (ICoreSubscribable)
presenceVenue.getSubscribable(ICoreSubscribable.class, id );
myBuddy.addSubscribableListener( ... );
myBuddy.addPropertyListener( ... );
myBuddy.subscribe();
myBuddies.add( myBuddy);
}


3 --MyPresence and BuddyPresence
================================

3.1 -- MyPresence and BuddyPresence Overview
------------------------------------------------------------ -------------
The presence venue provides the machinery to create specializations of
subscribable and publishable objects. To illustrate this we will define
the MyPresence and BuddyPresence types for handling familiar buddy
lists. These are very simple and provide only a little "syntactic
sugaring" over the core capabilities. They illustrate a point,
however. A presence specialization might use a property in extremely
complicated ways by coding information into the value. Or, it might use
several properties to encode something that is best exposed via a single
method. To ensure that these properties are used as intended, the
component developer will want to create a subscribable and a publishable
that expose the capabilities through appropriate methods.

We get the specializations through the same methods on IPresenceVenue
that we used earlier to get the ICoreSubscribable or ICorePublishable.
These methods are similar to getAdapter in form, but work a little
differently. It is assumed that the implementation of IPresenceVenue
has privileged access to building an ICoreSubscribable or
ICorePublishable. These are not component extensions that are plugged
in, but core elements of the API implementation. As such, the
implementation of IPresenceVenue can access these core implementations
in ways that are not exposed through the interfaces.

So here's the expected implementation:
1) A request for a specialized type is received at the presence venue
via getSubscribable or createPublishable.
2) A corresponding core object is created and assigned the ID (probably
through its constructor.)
3) The getAdapter method of the core object is called to obtain the
specialized type.
4) The appropriate factory for the specialized type is found in the
adapter registry and following the IAdaptable pattern an object of the
new type is created around the core object.
5) This specialization is passed as the return value of the
getSubscribable or createPublishable method.

This approach allows us to use the getAdapter pattern after first
configuring the core object with some specialized informaton that we
would not otherwise be able to pass to the IAdaptable constructor, i.e.,
the ID.

I have configured this so that ICoreSubscribable and ICorePublishable
can be proper return values of the getSubscribable and createPublishable
methods. I have also assumed that they will be the default types.
This is not necessary. An implementation that wanted to be especially
guarded about ensuring that properties are not abused could refuse to
pass the core types on request. This would ensure that presence objects
are always managed via their appropriate specializations.

3.2 -- MyPresence and BuddyPresence API
------------------------------------------------------------ -----

IBuddyPresence
-------------------------
public interface IBuddyPresence extends ISubscribable {
// Available prior to activation
public void addBuddyPresenceListener(IBuddyPresenceListener bpl);
public void removeBuddyPresenceListener(IBuddyPresenceListener bpl);

// Only available once the object is activated
public boolean isOnline() throws PresenceException;
public String getStatusMessage() PresenceException;
}
public interface IBuddyPresenceListener extends ISubscribableListener {
public void onlineChanged(BuddyPresenceEvent bpe);
public void statusMessageChanged(BuddyPresenceEvent bpe);
}
public class BuddyPresenceEvent extends java.util.EventObject {
public IBuddyPresence getBuddyPresence();
}

IMyPresence
--------------------
public interface IMyPresence extends IPublishable, IBuddyPresence {
// Available prior to activation
public void setOnline(boolean isOnline);
public void setStatusMessage(String msg);

public void addSubscriptionListener(ISubscriptionListener sl);
public void removeSubscriptionListener(ISubscriptionListener sl);

// Only available once the object is activated
public void permitSubscription(ID id) throws PresenceException;
public void denySubscription(ID id,int code,String reason) throws
PresenceException;
public void revokeSubscription(ID id,int code,String reason) throws
PresenceException;
}

3.3 -- MyPresence and BuddyPresence Example code
-------------------------------------------------------

This just illustrates how the BuddyListVenue API would be used to set up
your buddy list.

1. Create an IPresenceVenue and start it,

IPresenceVenue presenceVenue = (IPresenceVenue)
sharedContainer.getAdapter( IPresenceVenue.class );
presenceVenue.activate();

2. Set my online status

IMyPresence mp = (IMyPresence)
presenceVenue.createPublishable(IMyPresence.class,myUserID );
mp.setOnline(true );
mp.setStatusMessage("I am ready to work" );
mp.publish();

3. Create a buddy list,

ID[] myBuddyIds = getBuddyListFromSomewhere();
List<IBuddyPresence> myBuddies = new ArrayList<IBuddyPresence>();

for (ID id : myBuddyIds)
{
IBuddyPresence myBuddy = (IBuddyPresence)
presenceVenue.getSubscribable(IBuddyPresence.class, id );
myBuddy.addSubscribableListener( ... );
myBuddy.addBuddyPresenceListener( ... );
myBuddy.subscribe();
myBuddies.add( myBuddy );
}
Re: A Presence Venue Proposal [message #587244 is a reply to message #587034] Tue, 12 July 2005 19:29 Go to previous message
Scott Lewis is currently offline Scott LewisFriend
Messages: 1038
Registered: July 2009
Senior Member
Hi John,

Thanks for the proposal. The ECF team is just getting out a new release
(0.3.3) as well as deploying some new servers for testing/providing
service so that's why I haven't responded to your proposal as yet.
Sorry for the delay.

I'll attempt to respond with detailed comments very soon (within a day
or so). In general, I like the proposal very much. Actually, at first
look I think it's probably fairly easily implemented via the existing
org.eclipse.ecf.presence API, so it could make a nice addition/compliment.

I have some API design nits about the use of static Strings (in
PresenceConstants) to represent presence state (e.g. "active, etc"), as
I think that these should probably be based upon 'extensible,
serializable type-safe enums' as per Josh Bloch's Book 'Effective Java'.
LMK if you want more detail on what I mean here. But, in general I
don't have any major problems with the approach you've described.

Thanks kindly to both you and Li-Te for the ideas and work...and more soon.

Scott


John F. Patterson wrote:
> Presence Venue API
>
> The outline for this document is as follows:
>
> 1 -- Introduction
> 2 -- PresenceVenue
> 2.1 -- Overview
> 2.2 -- PresenceVenue API
> 2.3 -- PresenceVenue Example code
> 3 -- MyPresence and BuddyPresence
> 3.1 -- Overview
> 3.2 -- MyPresence and BuddyPresence API
> 3.3 -- MyPresence and BuddyPresence Example code
>
> As in earlier postings, Li-Te Cheng has helped me prepare this.
>
> 1 -- Introduction
> =============
>
> This presentation of a proposed Presence Venue API (I use the term venue
> to avoid confusion with OSGi services.) is both more complicated and
> less complete than I had hoped for. It became more complicated because
> I wanted to capture a rather general notion of presence that could be
> applied to infomation other than simply one's online status. This has
> made the PresenceVenue objects rather abstract. If you have the
> patience, I think you will find that it is an interesting Pub/Sub
> (Publish and Subscribe) service that could be used to provide awareness
> of a lot more than just online status. If you don't have the patience,
> skip down to the Section 3. This section describes specializations that
> are tailored to the buddy list case.
>
> These APIs are also less complete than I had originally planned for two
> reasons. The first and most important is that I have split out or
> simply not described several services that the PresenceVenue will
> interact with. The most obvious of these is that I have not
> incorporated chat into the PresenceVenue. I think of this as a
> logically distinct service. Presumably, I will be able to activate a
> chat from a different venue (MeetingVenue?) using the ID representing a
> person. A directory service is also logically distinct and is
> presumably where I discover the IDs that permit me to identify others.
> Finally, the most problematic service is a storage service. Frankly, I
> am not entirely certain how to deal with this. There is a tendency for
> presence services to bundle in a storage service, but I should be free
> to save my buddy list anywhere I want. I'm just skipping the storage
> service for the time being.
>
> The second reason that the API is somewhat incomplete is that it builds
> on an abstract notion of property-value pairs, but it does not say much
> about any standardized names or values. Any specific use of the
> PresenceAPI will require conventional (if not standardized) names and
> values if any consequence other than simply presenting them to the user
> is desired. I think of these a little like Mime types. Some will be
> standardized, some will be defacto conventions, and some will be novel
> and idiosyncratic uses. I have provided the conventions for the
> BuddyListVenue, but we might want to define other properties and values
> as well.
>
> 2 -- PresenceVenue
> =================
>
> 2.1 -- PresenceVenue Overview
> -----------------------------------------------
> I consider this a simple service, though it is somewhat abstract. It is
> simpler than one might expect because I am not trying to handle IM,
> account management, directory, or storage services. Chat is logically a
> different service (venue) and I think it is best to keep it out of the
> story for the time being. I'll try to work up something on chat later.
> I am also not trying to deal with Smack's notion of account management.
> I haven't decided where is the right place to put that.
>
> A general pattern that you will find in this API is that objects
> listening for remote events begin as inert with most of their
> interesting capabilities not yet available. This is so that listeners
> may be added before any notifications start arriving. The general
> pattern is:
>
> public interface INotifiable {
> public void activate();
> public boolean isActive();
> public void deactivate();
>
> public void addNotifiableListener();
> public void removeNotifiableListener();
> }
>
> The actual methods to activate or deactivate might have different names
> (e.g., subscribe and dispose), but the basic idea is the same. The
> lifecycle is to go from inactive to active and then to inactive, though
> this last step might be implicit due to other events. A question that
> usually differs from one situation to another is whether an object can
> be reactivated. I have not taken a stand on this. If forced to adopt a
> uniform policy, I would argue that objects cannot be reactivated and
> that one must obtain a new version of the INotifiable befrore
> reactivating it.
>
> In the PresenceVenue API, there are five event-receiving objects:
> IPresenceVenue, ISubscribable, ICoreSubscribable, IPublishable, and
> ICorePublishable. I have adopted the attitude that a presence object is
> simply a bag of property-value pairs. I like this because it is easily
> serializable, provides a modicum of structure, but imposes very few
> constraints on what may be represented. I have defined a new
> PresenceProperty class, so that the values may be byte arrays, which
> makes the whole thing even more general. ISubscribable and IPublishable
> handle only the lifecycle methods for the presence objects, not the
> property access methods. They are expected to be extended by
> specializations that are "aware" of the ways that the properties are
> being used. ICoreSubscribable and ICorePublishable are basic
> implementations that handle properties in a very generic fashion. They
> are intended to be used by ISubscribable and IPublishable extensions
> that provide less generic methods for manipulating the properties.
>
> In some sense, this API is not complete because it will be necessary to
> establish some standard properties and their values. For presence types
> that are well known and well standardized (like person presence) we will
> probably want to define an extension of ISubscribable (e.g.,
> IBuddyPresence) that provides simple getters for the standard
> properties. The pattern I expect is that IBuddyPresence will extend
> ISubscribable. When the IPresenceVenue is asked to get an
> IBuddyPresence, it will first create an ICoreSubscribable and then call
> its getAdapter to "wrap" the IBuddyPresence around it. The
> IBuddyPresence will expose these generic capabilities via its own
> specialized interfaces. Section 3 illustrates this pattern.
>
> Clients may subscribe to or publish presence objects. To subscribe to a
> presence object, you first get an ISubscribable object. This only
> provides a local representation of the subscribable. It contains the
> address of the subscribable (its ID), but nothing else. To get the
> Subscribable working, you must either fetch it or subscribe to it.
> Fetching is a one-time subscription that simply collects the current
> state of the object. Subscribing allows one to track changes to the
> object. Fetching and subscribing activate the object (once the return
> event occurs). Once the object is activated, you can get the properties
> and it will return the most recently available values. Getting a
> property does not cause a network event.
>
> To publish an object, you must first create an IPublishable object. If
> a client publishes a presence object, then it is the only one who may
> change the state of that object. The separation of the IPublishable
> from the ISubscribable helps avoid the impression that applications and
> components may set the properties of presence objects that they do not
> manage. The ICorePublishable interface provides the methods needed to
> set properties, publish the changes, and handle requests to subscribe to
> the IPublishable. It also extends ICoreSubscribable, which means you
> may subscribe to your published objects. While this is mostly not
> necessary, it can be useful in server-managed presence to ensure that
> others are seeing what you think they should see. .Setting properties
> does not cause a network event. To ensure that others become aware of
> the changes, the application must publish the object. The object is not
> really active until after the first publish event.
> The most complicated aspect of this API is the subscription process. To
> begin with, the API does not deal with how one discovers a subscribable
> worth subscribing to. Presumably, the IDs (URLs?) for interesting
> presence objects are simply well-known, found in a directory, passed via
> email, or available as properties on other well-known presence
> objects. I imagine subscriptions happening in one of two ways. The
> first, and simplest, assumes that a server will act as an intermediary
> and permit or deny subscriptions on its own. This API does not model
> how the server makes its decisions, so we only consider the behavior of
> the subscribing client. It calls the subscribe method on the
> ICoreSubscribable and receives back a subscribed or subscribeFailed
> event on the listener.
>
> The other approach assumes that the client managing the published object
> will be involved in the decision about whether to permit or deny a
> subscription attempt. The subscribing client calls subscribe as before,
> which causes a subscriptionRequested event on the listener for the
> corresponding ICorePublishable of the managing client. The application
> on the managing client must either permit or deny the subscription
> request. In this case, denial can only really be based on the ID of the
> requesting client, since little else is available. When denying a
> subscription, I have provided two ways to flag the denial: an int code
> that should be enumerated from a set of, as yet, undefined reasons and a
> String to provide a human-readable reason. When the reason for denial
> is that I don't want to share this because of who you are, I assume the
> application will want to mimic a reason that might otherwise be
> automatically available.
>
> There is one final wrinkle in this API. The presentation so far assumes
> that the subscriber already knows the type of the objects being
> subscribed to. This tends to be true in the buddy list case and most
> other cases, but one could subscribe to a presence object without
> knowing the appropriate interface type to ask for. Since the
> ISubscribable objects are created before they are activated, one might
> ask for an ISubscribable type that does not match the corresponding
> IPublishable type. If we assume that the IPublishable always sets a
> property (PresenceConstants.TYPE_PROPERTY) indicating the appropriate
> subscribing type, then we could detect this problem. If one knows in
> advance that the type is unknown, my recommendation is to get an
> ICoreSubscribable and then use getAdapter once the preferred type is
> known. If one mistakenly gets a subscribable of the wrong type, then I
> would use getAdapter if it is exposed or deactivate the object and start
> over with the correct type.
>
> 2.2 -- PresenceVenue API
> ---------------------------------------
> The interfaces are provided below. I have not incorporated many
> comments because I find it easier to get a sense of the whole interface
> without them. I don't think there is too much that isn't either obvious
> or described above. Please do not get too excited by my use of arrays
> to represent a set of objects. I am happy to use Iterator, Enumeration,
> or Vector, if people think that is better. Also, don't get too excited
> by my use of the PresenceProperty object. I am happy to create a
> different way to do this. I have tried to follow the Java Beans
> convention in my use of listeners and listener events.
>
> Some Helper Classes
> --------------------------------
> public class PresenceConstants {
> public static final String NAME_PROPERTY = "Name";
> public static final String TYPE_PROPERTY = "SubscribingType";
> public static final String ONLINE_PROPERTY = "Online";
> public static final String STATUS_PROPERTY = "Status";
>
> public static final byte[] ONLINE_VALUE = "Online".getBytes();
> public static final byte[] OFFLINE_VALUE = "Offline".getBytes();
> public static final byte[] DO_NOT_DISTURB_VALUE = "Do not
> disturb".getBytes();
> }
> public class PresenceProperty {
> public PresenceProperty(String key, byte[] val);
> public PresenceProperty(String key, String val);
> public String getKey();
> public byte[] getValue();
> }
> public class PresenceException extends Exception {
> public PresenceException(int code, String reason);
> public int getCode();
> }
>
> INotifiable
> ---------------
> // INotifiable is a base interface for all the event receiving
> objects. The objects start as inert (not connected) and must be activated
> // to become useful. Listeners are not included, since they will
> differ from specialization to specialization.
>
> public interface INotifiable {
> public ID getID();
>
> public void activate();
> public boolean isActive();
> public void deactivate();
> }
>
> IPresenceVenue
> -------------------------
> public interface IPresenceVenue extends INotifiable {
> // Available prior to activation
> public void addPresenceVenueListener(IPresenceVenueListener pvl);
> public void removePresenceVenueListener(IPresenceVenueListener pvl);
>
> // Only available once the object is activated
> // Logically, getSubscribable and createPublishable do not require that
> the venue be active to be invoked;
> // the results just cannot be activated until the venue is
> activated, Therefore, it might be wise to treat them as
> // unavailable so that the objects will not be created until they
> can themselves be activated.
>
> public ISubscribable getSubscribable(Class subscribableType,ID id)
> throws PresenceException;
> public IPublishable createPublishable(Class publishableType,ID id)
> throws PresenceException;
> }
> public interface IPresenceVenueListener {
> public void activated(PresenceVenueEvent pve);
> public void lost(PresenceVenueEvent pve);
> public void deactivated(PresenceVenueEvent pve);
> }
> public class PresenceVenueEvent extends java.util.EventObject {
> public IPresenceVenue getPresenceVenue();
> }
>
> ISubscribable
> ---------------------
> // ISubscribable is simply a common type for all subscribable objects.
> It contains the methods that manage the object lifecycle, but not the
> property access.
> // ICoreSubscribable is the internal subscribable that must be
> supported. Usually it will be contained within some other
> // ISubscribable that exposes the ICoreSubscribable capabilities
> in a specialized way.
>
> public interface ISubscribable extends INotifiable {
> // Available prior to activation
> pulbic void fetch(); // Causes activation
> public void subscribe(); // Causes activation
> public boolean isSubscribed(); // Notice: Fetch makes it
> possible to be active, but not subscribed.
>
> public void addSubscribableListener(ISubscribableListener sl);
> public void removeSubscribableListener(ISubscribableListener sl);
>
> // Only available once the object is activated
>
> public void unsubscribe() throws PresenceException; //
> Unsubscribing does not necessarily deactive the object, since we might
> continue to do fetches
> }
> public interface ISubscribableListener {
> public void fetched(SubscribableEvent se);
> public void fetchFailed(SubscribableEvent se);
> public void subscribed(SubscribableEvent se);
> public void subscribeFailed(SubscribableEvent se);
> public void unsubscribed(SubscribableEvent se);
> public void unsubscribeFailed(SubscribableEvent se);
> public void deactivated(SubscribableEvent se);
> }
> public class SubscribableEvent extends java.util.EventObject {
> public ISubscribable getSubscribable();
> }
>
> ICoreSubscribable
> ----------------------------
> // ICoreSubscribable is the basic internal implementation of a
> subscribable.
> // It works directly with the subscribable's properties.
> // Usually, it will be contained within a more specialized type that
> provides less
> // generic methods for accessing the properties.
> // ICoreSubscribable is an ISubscribable so that it may be a
> legitimate return
> // value of IPresenceVenue.getSubscribable.
> public interface ICoreSubscribable extends ISubscribable {
> // Available prior to activation
> public void addPropertyListener(IPropertyListener pl);
> public void removePropertyListener(IPropertyListener pl);
>
> public Object getAdapter(Class clazz) throws PresenceException;
>
> // Only available once the object is activated
> public byte[] getProperty(String key) throws PresenceException;
> public PresenceProperty[] getProperties() throws PresenceException;
> }
> // The IPropertyListener and its event are not particular to the
> ICoreSubscribable and could be used by
> // specializations of ISubscribable as a way to expose property
> changes.
> public interface IPropertyListener {
> public void changed(PropertyEvent pe);
> }
> public class PropertyEvent extends java.util.EventObject {
> public ISubscribable getSubscribable();
> public PresenceProperty[] getChangedProperties();
> public PresenceProperty[] getNewProperties();
> public String[] getDeletedProperties();
> }
>
> IPublishable
> -------------------
> // IPublishable is simply a common type for all publishable objects.
> It contains only the methods for managing the lifecycle,
> // but not property access or subscription acceptance.
> // ICorePublishable is the internal publishable that must be
> supported. Usually it will be contained within some other
> // IPublishable that exposes the ICorePublishable capabilities in
> a specialized way.
>
> public interface IPublishable extends INotifiable {
> // Available prior to activation
> public boolean hasUnpublishedSettings();
> public void publish(); // The first publish activates the object
>
> public void addPublishableListener(IPublishableListener pl);
> public void removePublishableListener(IPublishableListener pl);
> }
> public interface IPublishableListener {
> public void published(PublishableEvent pe);
> public void publishFailed(PublishableEvent pe);
> public void deactivated(PublishableEvent pe);
> }
> public class CorePublishableEvent extends java.util.EventObject {
> public IPublishable getPublishable();
> }
>
> ICorePublishable
> --------------------------
> // ICorePublishable is the basic internal implementation of a publishable.
> // It works directly with the publishable's properties and it
> manages requests to subscribe.
> // Usually, it will be contained within a more specialized type that
> provides less
> // generic methods for manipulating the properties.
> // ICorePublishable is an IPublishable so that it may be a
> legitimate return
> // value of IPresenceVenue.createPublishable.
> // ICorePublishable extends ICoreSubscribable so that one may
> subscribe to what is being
> // published.
>
> public interface ICorePublishable extends IPublishable, ICoreSubscribable {
> // Available prior to activation
> public void setProperty(String key,byte[] val); // Setting and
> getting properties is available prior to activation
> public void setProperties(PresenceProperty[] props);
>
> public void addSubscriptionListener(ISubscriptionListener sl);
> public void removeSubscriptionListener(ISubscriptionListener sl);
>
> // Only available once the object is activated
>
> public void permitSubscription(ID id) throws PresenceException;
> public void denySubscription(ID id,int code,String reason) throws
> PresenceException;
> public void revokeSubscription(ID id,int code,String reason) throws
> PresenceException;
> }
> // The ISubscriptionListener and its event are not particular to the
> ICorePublishable and could be used by
> // specializations of IPublishable as a way to expose subscription
> requests.
> public interface ISubscriptionListener {
> public void subscriptionRequested(SubscriptionEvent pse);
> public void unsubscribeNotification(SubscriptionEvent pse);
> }
> public class SubscriptionEvent extends java.util.EventObject {
> public IPublishable getPublishable();
> public ID getSubscriberID();
> }
>
> 2.3 -- PresenceVenue Example code
> -------------------------------------------------------
>
> This just illustrates how the PresenceVenue API would be used to set up
> your buddy list.
>
> 1. Create an IPresenceVenue and start it,
>
> IPresenceVenue presenceVenue = (IPresenceVenue)
> sharedContainer.getAdapter( IPresenceVenue.class );
> presenceVenue.activate();
>
> 2. Set my online status
>
> ICorePublishable cp = (ICorePublishable)
> presenceVenue.createPublishable(ICorePublishable.class,myUse rID);
> cp.setProperty( PresenceConstants.ONLINE_PROPERTY,
> PresenceConstants.ONLINE_VALUE );
> cp.setProperty( PresenceConstants.STATUS_PROPERTY, "I am ready to
> work" );
> cp.publish();
>
> 3. Create a buddy list,
>
> ID[] myBuddyIds = getBuddyListFromSomewhere();
> List<ISubscribable> myBuddies = new ArrayList<ISubscribable>();
>
> for (ID id : myBuddyIds)
> {
> ICoreSubscribable myBuddy = (ICoreSubscribable)
> presenceVenue.getSubscribable(ICoreSubscribable.class, id );
> myBuddy.addSubscribableListener( ... );
> myBuddy.addPropertyListener( ... );
> myBuddy.subscribe();
> myBuddies.add( myBuddy);
> }
>
>
> 3 --MyPresence and BuddyPresence
> ================================
>
> 3.1 -- MyPresence and BuddyPresence Overview
> ------------------------------------------------------------ -------------
> The presence venue provides the machinery to create specializations of
> subscribable and publishable objects. To illustrate this we will define
> the MyPresence and BuddyPresence types for handling familiar buddy
> lists. These are very simple and provide only a little "syntactic
> sugaring" over the core capabilities. They illustrate a point,
> however. A presence specialization might use a property in extremely
> complicated ways by coding information into the value. Or, it might use
> several properties to encode something that is best exposed via a single
> method. To ensure that these properties are used as intended, the
> component developer will want to create a subscribable and a publishable
> that expose the capabilities through appropriate methods.
>
> We get the specializations through the same methods on IPresenceVenue
> that we used earlier to get the ICoreSubscribable or ICorePublishable.
> These methods are similar to getAdapter in form, but work a little
> differently. It is assumed that the implementation of IPresenceVenue
> has privileged access to building an ICoreSubscribable or
> ICorePublishable. These are not component extensions that are plugged
> in, but core elements of the API implementation. As such, the
> implementation of IPresenceVenue can access these core implementations
> in ways that are not exposed through the interfaces.
>
> So here's the expected implementation:
> 1) A request for a specialized type is received at the presence venue
> via getSubscribable or createPublishable.
> 2) A corresponding core object is created and assigned the ID (probably
> through its constructor.)
> 3) The getAdapter method of the core object is called to obtain the
> specialized type.
> 4) The appropriate factory for the specialized type is found in the
> adapter registry and following the IAdaptable pattern an object of the
> new type is created around the core object.
> 5) This specialization is passed as the return value of the
> getSubscribable or createPublishable method.
>
> This approach allows us to use the getAdapter pattern after first
> configuring the core object with some specialized informaton that we
> would not otherwise be able to pass to the IAdaptable constructor, i.e.,
> the ID.
>
> I have configured this so that ICoreSubscribable and ICorePublishable
> can be proper return values of the getSubscribable and createPublishable
> methods. I have also assumed that they will be the default types.
> This is not necessary. An implementation that wanted to be especially
> guarded about ensuring that properties are not abused could refuse to
> pass the core types on request. This would ensure that presence objects
> are always managed via their appropriate specializations.
>
> 3.2 -- MyPresence and BuddyPresence API
> ------------------------------------------------------------ -----
>
> IBuddyPresence
> -------------------------
> public interface IBuddyPresence extends ISubscribable {
> // Available prior to activation
> public void addBuddyPresenceListener(IBuddyPresenceListener bpl);
> public void removeBuddyPresenceListener(IBuddyPresenceListener bpl);
>
> // Only available once the object is activated
> public boolean isOnline() throws PresenceException;
> public String getStatusMessage() PresenceException;
> }
> public interface IBuddyPresenceListener extends ISubscribableListener {
> public void onlineChanged(BuddyPresenceEvent bpe);
> public void statusMessageChanged(BuddyPresenceEvent bpe);
> }
> public class BuddyPresenceEvent extends java.util.EventObject {
> public IBuddyPresence getBuddyPresence();
> }
>
> IMyPresence
> --------------------
> public interface IMyPresence extends IPublishable, IBuddyPresence {
> // Available prior to activation
> public void setOnline(boolean isOnline);
> public void setStatusMessage(String msg);
>
> public void addSubscriptionListener(ISubscriptionListener sl);
> public void removeSubscriptionListener(ISubscriptionListener sl);
>
> // Only available once the object is activated
> public void permitSubscription(ID id) throws PresenceException;
> public void denySubscription(ID id,int code,String reason) throws
> PresenceException;
> public void revokeSubscription(ID id,int code,String reason) throws
> PresenceException;
> }
>
> 3.3 -- MyPresence and BuddyPresence Example code
> -------------------------------------------------------
>
> This just illustrates how the BuddyListVenue API would be used to set up
> your buddy list.
>
> 1. Create an IPresenceVenue and start it,
>
> IPresenceVenue presenceVenue = (IPresenceVenue)
> sharedContainer.getAdapter( IPresenceVenue.class );
> presenceVenue.activate();
>
> 2. Set my online status
>
> IMyPresence mp = (IMyPresence)
> presenceVenue.createPublishable(IMyPresence.class,myUserID );
> mp.setOnline(true );
> mp.setStatusMessage("I am ready to work" );
> mp.publish();
>
> 3. Create a buddy list,
>
> ID[] myBuddyIds = getBuddyListFromSomewhere();
> List<IBuddyPresence> myBuddies = new ArrayList<IBuddyPresence>();
>
> for (ID id : myBuddyIds)
> {
> IBuddyPresence myBuddy = (IBuddyPresence)
> presenceVenue.getSubscribable(IBuddyPresence.class, id );
> myBuddy.addSubscribableListener( ... );
> myBuddy.addBuddyPresenceListener( ... );
> myBuddy.subscribe();
> myBuddies.add( myBuddy );
> }
Previous Topic:Collaboration Server Maturity
Next Topic:message lost?
Goto Forum:
  


Current Time: Thu Mar 28 13:21:43 GMT 2024

Powered by FUDForum. Page generated in 0.08656 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top