You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by "Howard M. Lewis Ship" <hl...@comcast.net> on 2003/09/04 21:25:32 UTC

RE: [HiveMind] Proposal for adding Interceptors to a set of Services

This is an interesting idea; I'll take a peek when I get the chance.

Some other ideas I've considered:

a) Defining interceptor "sets" and identify one or more sets for a service.  This is more of a "drag
in" approach than a "push in" approach you described.
b) Having a "hivemind.InterceptorConsultant" service that would be hooked into the code that
constructs the interceptor stack.  The consultant acts as a delegate to the ServiceExtensionPoint,
and can provide additional interceptors to the normal list.  The InterceptorConsultant could be
driven off of an extension point and could achieve something very similar to what you've described.

I've also through about:
1) A mechanism to allow you to enable/disable a contributed interceptor at runtime, perhaps driven
off of system properties.
2) Passing parameters to interceptor factories in the same way that we do to
ImplementationFactories.

--
Howard M. Lewis Ship
Creator, Tapestry: Java Web Components
http://jakarta.apache.org/tapestry
http://jakarta.apache.org/commons/sandbox/hivemind/
http://javatapestry.blogspot.com

> -----Original Message-----
> From: Christian Essl [mailto:christianessl@yahoo.de] 
> Sent: Thursday, August 21, 2003 1:58 PM
> To: commons-dev@jakarta.apache.org
> Subject: [HiveMind] Proposal for adding Interceptors to a set 
> of Services
> 
> 
> 
> 
> I'd like to propose a module level tag, with which it is possible to 
> register Interceptors with all in the Registry Services or 
> all Services 
> which implement a (some) certain interface(s). I've 
> implemented a proposal 
> for this feature and tested it - the diff is attached.
> 
> These 'global-interceptors' have some advantages. Ie it would 
> be possible 
> to register (unregister) a Logger for all Services with just a little 
> change to just one module. Also services implementing a 
> certain interface 
> could be given automatically parameters through such an Interceptor  
> (Avalon like) without the need to specify the interceptor in 
> each service. 
> This therefore also reduces the amount of failure because of 
> forgetting the 
> interceptor. The same way a stop-service could be implemented, which 
> informs all loaded Services that implement iE StopInterface when the 
> program exits.
> 
> For the code of my proposal I have changed the module xml 
> format to include 
> under <module> the following tag:
> 
> <global-interceptor service-id="id of ServiceInterceptorFactory" 
> order="interceptor-order">
>       <class name="full.interface.name"/>
>       <class name="full.interface.name2"/> </global-interceptor>
> 
> The global-interceptor tag takes the same arguments as the 
> <interceptor> 
> tag. If no <class> tag is specified the interceptor will be 
> registered with 
> all Services in all modules. If there are one ore more 
> <class> tags the 
> Services which are intercepted must implement all the given 
> interface(s) 
> (AND). If there are more <global-interceptor>s with the same 
> ServiceInterceptorFactory-Id than the Interceptor is wrapped 
> if at least 
> one fits (OR)
> 
> The ServiceInterceptorFactories specified and the Services 
> requested of the 
> Factory at creation time are never wrapped by global 
> Interceptors (because 
> of circularity - actually the implementation loads all the 
> ServiceInterceptorFactories in 
> RegistryBuilder.constructRegistry() as non 
> deferred).
> 
> My implementation works following: First the DescriptorParser 
> builds up 
> GlobalInterceptorDescriptors which are added to the ModuleDescriptor.
> 
> In the RegistryBuilder one instance of 
> org....hivemind.impl.GlobalInterceptorRegistry is build up from the 
> Descriptors. There is only one instance - held in an field - per 
> RegistryBuilder (and so per Registry). This instance is used 
> to hold all 
> the mappings of interfaces to 
> ServiceInterceptorContributions. The instance 
> is given to each ServiceExtensionPointImpl when it is 
> constructed. The 
> point will use the instance in addInterceptors() to request 
> the global- 
> mapped Interceptors for itself and add it to it's own 
> interceptors list.
> 
> When the Modules are processed in the RegistryBuilder the 
> GlobalInterceptorRegistry is populated but not yet ready to 
> use. As the 
> last step in the RegistryBuilder.constructRegistry() the 
> GlobalInterceptorRegistry is build up. Here some 
> optimizations are done (or 
> better could be done) and all the needed 
> ServiceInterceptorFactories are 
> loaded - non deffered. Till this time the 
> ServiceExtensionPointImpls will 
> not use the GlobalInterceptorRegistry. This way Services (the 
> ServiceInterceptorFactories and there requested Services) 
> loaded by the 
> GlobalInterceptorRegistry itself are not intercepted (except 
> if defined in 
> a Service-Extension), which is important for circularity. 
> After this the 
> GlobalInterceptorRegister is ready to be used by the 
> ServiceExtesionPointImpls and all Service loaded are wrapped 
> if they match.
> 
> Finally there are also two Tests which you can see from the diff.
> 
> -- 
> Using M2, Opera's revolutionary e-mail client: 
> http://www.opera.com/m2/
> 


Re: [HiveMind] Proposal for adding Interceptors to a set of Services

Posted by Christian Essl <ch...@yahoo.de>.
>> I'd like to propose a module level tag, with which it is possible to 
>> register Interceptors with all in the Registry Services or all Services 
>> which implement a (some) certain interface(s).
>>

> This is an interesting idea; I'll take a peek when I get the chance.
>
> Some other ideas I've considered:
>
> a) Defining interceptor "sets" and identify one or more sets for a 
> service.  This is more of a "drag in" approach than a "push in" approach 
> you described.

Drag in is always good for the decorator pattern. However as I see push in 
is as important to Interceptors. In my opinion interceptors like defined in 
HiveMind
(they have to take any interface) are often like aspects in (a weak form 
of) Aspect Orientated Programming and 'push in' is there more or less a 
must.

Actually I think the different service-modells are also (hard coded) 
interceptors. If we had a push-in interceptor model new 'service-modells' 
could be implemented as 'normal
interceptors' without changing the core. May be with the use of meta-inf 
service. (I'll describe how I think this could be possible later).

> b) Having a "hivemind.InterceptorConsultant" service that would be hooked 
> into the code.  The InterceptorConsultant could be
> driven off of an extension point and could achieve something very similar 
> to what you've described.

Yes that is certainly cleaner and easier to mantain.

> I've also through about:
> 1) A mechanism to allow you to enable/disable a contributed interceptor 
> at runtime, perhaps driven
> off of system properties.

More later

> 2) Passing parameters to interceptor factories in the same way that we do 
> to ImplementationFactories.
(Haven't really thought about this.)

My proposal for building a 'push-in interceptor service' 
(hivemind.InterceptorConsultant). A real change from
the code I send last time (just throw it away):

The whole idea turns around having a linked chain of 
ServiceImplementationConstructors, which is alone responsible of producing 
the Interceptors and the final CoreServiceImplemenation. The last element 
of the chain would be either a CreateClassServiceConstructor or a 
InvokeFactoryServiceConstructor. The (optional) chain-elements before would 
be (new to implement) CreateInterceptorServiceConstructors. The chain holds 
all the dragged in and pushed in interceptors in the correct order. It is 
build up at start-up for each ServiceExtensionPointImpl and given to it. 
The ServiceExtensionPointImpl just holds a reference to the chain. The 
ServiceExtensionPointImpl does not create a service, it's interceptors or a 
service-models. It just delegates to the first element in the chain when 
the Service is the first time asked.

Creating the Service in the chain:

If the first element is also the last it just creates the core service 
implementation and returns it.
Otherwise the first element is a CreateInterceptorServiceConstructor. This 
calls on the ServiceInterceptorFactory the method 
createInterceptor(ServiceImplementationConstructor 
nextInChain,AdditionalInfo moduleIdEtc). The ServiceInterceptorFactory may 
now use the nextInChain to construct the next ServiceImplementaion (- 
interceptor). Than the same process happends with the nextChainElement. 
Note the InterceptorFactory or its interceptor my construct the next 
immideatly, deffered, per thread, in any other way or not at all. The 
interceptor is only responsible to fullfill the call to one of the Service 
Interface methods. Note also the interceptor is also free to release the 
following CoreService (or interceptor) whenever it wants. This way the 
interceptor is also sure that it is the only one who has a reference to the 
next element (as long as it does not give it out). I think this is 
important when it comes to dynamic unloading etc. For dynmics (see next) 
I'd also suggest to add  the methods setMemento(Object memento) and 
getMemento() to the ServiceImplementationConstructor.

Dynamics (changing of interceptors or core service, (temporaraly) shutting- 
down services):

Because presumably dynamics is not so often needed, I think the most strait 
way would be to destroy the old interceptors and the core implementation, 
modify the chain and build everything from scratch (of course there is a 
syncrhonization issue here). That the whole process is invisible to the 
outside the SericeExtensionPoint would always return a proxy which just 
forwards plain all calles to the first interceptor gotten from the chain. 
When the chain is destroyed and build up the proxy blocks until the object 
refernced by the proxy is changed. (I think this way you also don't need 
dependency analyses when a service is shut-down. It will just be started 
again when needed).

Regarding the shut-down process I am not so sure. (Generally this is realy 
the thing I miss most in HiveMind. I can imagine there are Resource- 
Services - ConnectionPools etc - , which all need a way to be destroyed). 
However I would say the (old) ServiceImplementationConstructor chain is 
gone through from
start to end and calls on each ServiceInterceptorFactory which was 
activated in the build-up the new method destroyInterceptor(Object 
mementoOfSerImplConst,ExtraInfo moduleExtIdEtc). It is the responsibility 
of the ServiceInterceptorFactory and/or its interceptors to store whatever 
is need for destroy in the memento (when the interceptor was created). The 
last element in the chain calls on the CoreServiceImplementaton 
destroy(ExtraInfo) if it implements the (new) Interface Destroyable.

Circulartiy:
I've thought about this (It's confusing ;-O) and I did not realy come to a 
satisfactory solution however I'd say:

All referes only to ServiceInterceptorFactories used for push-in
1.) no ServiceInterceptorFactory is itself intercepted by a push-in 
interceptor
2.) all ServiceInterceptorFactories (for push-in) are loaded (non-deffered) 
at startup as the first services from the reigstry. This way later they 
will always be present in memory with no need to build with possible 
circularity.
3.) No ServiceInterceptorFactory (for push-in) may be unloaded or changed 
(Ensures 2. point).
4.) All services used in the instantiation or setup of a 
ServiceInterceptorFactory (for push-in) are also not push-in intercepted. 
They will also be loaded at start-up when the ServiceInterceptorFactories 
are loaded (as deffered loading would be a push-in interception, also not 
deffered). However they are alowed to be changed unloaded etc.

The alogorithmus ensuring this would be:
0.) (do extension point setup)
1.) read the extension points and build up the chains (fully with push-in). 
Disable all push-in Interceptor elements in all the chains (They will just 
forward to the next ServiceImplementationConstructor) (1.a. build up the 
ServiceExtnesionPoints with the chains).
2.) Mark the ServiceExtensionPointImpl for these 
ServiceIntercepotrFactories which are used for push-in as not dynamic. 
(They will not be build up again or changed or destroyed)
3.) create the Registry
4.) load all the ServiceInterceptorFactories which are used as push-in 
normally through the registry
5.) Enable all the chain (push-in) elements
6.) return the registry.

(Maybe an alternative approach would be to move the whole push-in 
interceptor-factories out of the service mechanism and keep them as plain 
objects in memory. Of course than they could not use any of the HiveMind 
functionality.)
   Please realy see this just as a proposal from - as I can judge from your 
source - a worse programmer than you. Of course following this proposal 
would mean a considerable change for the core, for new Services, for tests, 
maybe of the module-xml syntax. Because it is a lot of work and as I see 
the current code is realy nice and save, I would also suggest to leave the 
change - if you want to consider it at all - for a later release.

As said the most disturbing thing about HiveMind to me is - apart that it 
has no destroy functionality for services - the fact that it is not 
realesed. As I see from the cvs-logs you work very hard and fast so I don't 
want and can't pressure you and I certainly don't want to load you with 
extra-whishes.



-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/