Grzegorz Kossakowski pisze:
Hello,
Joerg Heinicke asked[1] me to provide summary of the issue that Daniel
raised[2] and outline
possible solutions so we can discuss them.
I think we should do the same for Object Model. I proposed[5] to create
new Spring scope (or reuse
sitemap scope that must be implemented). Actually, such scope does,
behind-the-scene, what I
described one paragraph above. It creates new instance (that may inherit
properties from old one) of
Object Model when entering pipeline components. Then component is safe
to modify Object Model
according to its needs or even pass it to subsequent, internal requets
(like calling service) and
can be sure that Object Model contains only data belongs only to the
current component. What's very
convenient, Spring scope would be completely transparent to the
components and even almost
transparent to the pipeline's implementation. Pipeline's code would have
to only inform when scope
is entered and leaved.
On the other hand, there is a subtle difference between threads ran
simultaneously and pipeline's
components ran simultaneously. In latter case, we are in charge of
component's management and
execution. That means we know exactly when one or another component is
executed so we can modify the
same Object Model instance the way that changes to Object Model
performed in one component will be
not visible in another one. Since switching between components occurs on
every SAX event we would
have to adjust Object Model on-the-fly for every SAX event and every
component in a pipeline. This
solution has been proposed[6] by Daniel that gave more detailed
description.
I've done more research and came to conclusion that we will need to combine ideas of custom scope
and environment changer. I'm going discuss technical details here.
Implementing custom scope has advantages that were outlined earlier. What I missed before was how
class implementing org.springframework.beans.factory.config.Scope interface knows which scope is the
active one? How it knows if new bean should be created or there is already one in this scope?
In order to keep this information accurate we need to switch scope every time component is switched,
which means effectively for every SAX event that comes along the pipeline. We already expressed
concerns about such solution because it may be too heavy but if we only change _scope_ it's really
lightweight and I expect it to have almost zero impact on performance. The code of such scope
changer would look like:
public class PipelineComponentScopeChanger implements ContentHandler {
//This map will contain only one attribute "beans", this map is shared
between all
//PipelineScopeChanger instances and PipelineScope class
private Map scopeAttributesHolder;
//This map holds beans in this scope
private Map beansInScope;
private ContentHandler nextPipelineComponent;
public startElement(String namespaceURI, String localName, String qName,
Attributes atts) {
private Map currentBeansInScope = (Map)scopeAttributesHolder.get("beans");
scopeAttributesHolder.put(beansInScope);
nextPipelineComponent.startElement(namespaceURI, localName, qName, atts);
scopeAttributesHolder.put(currentBeansInScope);
}
}
Same goes for all other SAX events. It's actually one extra get and two puts calls on Map. Rather
lightweight, yes?
Then code of PipelineComponentScope would look like:
public class PipelineComponentScope implements Scope {
private Map scopeAttributesHolder;
public Object get(String name, ObjectFactory objectFactory) {
Map scopedBeans = (Map)scopeAttributesHolder.get("beans");
Object scopedObject = scopedBeans.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
scopedBeans.put(name, scopedObject);
}
return scopedObject;
}
}
You may wonder how this scopedAttributesHolder would be injected into these classes. Before
discussing this issue I would like to give you another remark.
I showed to you that PipelineComponentScopeChanger would implement ContentHandler so you could
thought that I'm going to follow Daniel suggestion[1] to put this changer between pipeline
components. Next idea that comes to mind is that we should create this changers in pipeline's code
because it's a place where we are in control of all components and can insert these changer.
Actually, I'm not going this path, or not literally.
I want to create dynamic proxies around pipeline components. Actual wrapping would be performed by
class implementing BeanPostProcessor interface. Taking one perspective one could say that this it's
almost the way as discussed one paragraph above. However, going with this path makes whole pipeline
scope completely *orthogonal* to the pipeline and its components' code. No existing class will be
touched and there is no requirement on pipeline components' configuration files. No need for custom
namespace in XML config, etc.
I feel proud of this solution as it solves non-trivial problem just in few lines of code and few
config files in transparent way. 8-)
Of course I did not mention some corner cases like pipeline component scope behaviour where we are
_not_ in pipeline component or how to properly initialize Object Model in pipeline scope. I'm going
to start implementing this right know so I'll figure out this issues shortly.
[1] http://article.gmane.org/gmane.text.xml.cocoon.devel/74435
--
Grzegorz Kossakowski
http://reflectingonthevicissitudes.wordpress.com/
*** My Internet Service Provider breaks my internet connection
***
*** incessantly so I'll not be able to respond to e-mails
***
*** regularly and my work will be somehow irregular.
***
*** I'm already trying to switch ISP but it will take handful amount of time.
***