You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by hl...@apache.org on 2003/09/10 00:09:21 UTC

cvs commit: jakarta-commons-sandbox/hivemind/xdocs override.xml navigation.xml

hlship      2003/09/09 15:09:21

  Modified:    hivemind/xdocs navigation.xml
  Added:       hivemind/xdocs override.xml
  Log:
  Add doc on how to override services.
  
  Revision  Changes    Path
  1.17      +2 -1      jakarta-commons-sandbox/hivemind/xdocs/navigation.xml
  
  Index: navigation.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/hivemind/xdocs/navigation.xml,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- navigation.xml	8 Sep 2003 23:07:22 -0000	1.16
  +++ navigation.xml	9 Sep 2003 22:09:21 -0000	1.17
  @@ -23,6 +23,7 @@
   			<item name="Localization" href="/localization.html"/>
   			<item name="Inversion of Control" href="/ioc.html"/>
   			<item name="Multi-Threading" href="/multithreading.html"/>
  +			<item name="Overriding Services" href="/override.html"/>
   			<item name="Case Study #1: Vista Startup/Shutdown"
   				href="case1.html"/>
   		</menu>
  
  
  
  1.1                  jakarta-commons-sandbox/hivemind/xdocs/override.xml
  
  Index: override.xml
  ===================================================================
  <?xml version="1.0"?>
  <!-- $Id: override.xml,v 1.1 2003/09/09 22:09:21 hlship Exp $ -->
  <!DOCTYPE document [
  	<!ENTITY % common-links SYSTEM "../common/links.xml">
  	%common-links;
  	]>
  <document>
  	<properties>
  		<title>Overriding a Service</title>
  		<author email="hlship@apache.org">Howard M. Lewis Ship</author>
  	</properties>
  	<body>
  
  <section name="Introduction">
  
  <p>
  It is not uncommon to want to override an existing service and replace it with a new implementation. This
  goes beyond simply intercepting the service ... the goal is to replace the original implementation with
  a new implementation. This occurs frequently in <a href="http://jakarta.apache.org/tapestry/">Tapestry</a>
  where frequently an existing service is replaced with a new implementation that handles application-specific cases (and
  delegates most cases to the default implementation).
  </p>
  
  <blockquote>
  Note: Tapestry 3.0 is in beta at the time of this writing, and doesn't use HiveMind. Plans are afoot to 
  refactor Tapestry 3.1 to make considerable use of HiveMind. Many of the ideas represented in HiveMind
  germinated in earlier Tapestry releases.	
  </blockquote>
  
  <p>
  HiveMind doesn't have an explicit mechanism for accomplishing this ... that's because its reasonable to
  replace and wrap existing services just with the mechanisms already available.	
  </p>
  
  </section>
  
  
  <section name="Step One: A non-overridable service">
  	
  <p>
  To describe this technique, we'll start with a ordinary, every day service.  In fact, for
  discussion purposes, there will be two services: Consumer and Provider.  Ultimately, we'll show how to
  override Provider.	  Also for discussion purposes, we'll do all of this in a single module, though
  (of course) you can as easily split it up across many modules.
  </p>	
  
  <p>
  To begin, we'll define the two services, and set Provider as a property of Consumer:
  <source><![CDATA[
  <?xml version="1.0"?>
  <module id="ex.override" version="1.0.0">
    <service id="Provider" interface="ex.override.Provider">
      <create-instance class="ex.override.impl.ProviderImpl"/>
    </service>
    
    <service id="Consumer" interace="ex.override.Consumer">
      <invoke-factory service-id="hivemind.BuilderFactory">
        <construct class="ex.override.impl.Consumer">
          <set-service property="provider" service-id="Provider"/>
        </construct>
      </invoke-factory>
    </service>
  </module>
  ]]>	
  </source>	
  </p>
  
  </section>
  
  <section name="Step Two: Add some indirection">
  
  <p>
  In this step, we still have just the two services ... Consumer and Provider, but they are
  linked together less explicitly, by using substitution symbols.
  <source><![CDATA[
  <?xml version="1.0"?>
  <module id="ex.override" version="1.0.0">
    <service id="Provider" interface="ex.override.Provider">
      <create-instance class="ex.override.impl.ProviderImpl"/>
    </service>
    
    <extension point-id="hivemind.FactoryDefaults">
      <default symbol="ex.override.Provider" value="ex.override.Provider"/>
    </extension>
    
    <service id="Consumer" interace="ex.override.Consumer">
      <invoke-factory service-id="hivemind.BuilderFactory">
        <construct class="ex.override.impl.Consumer">
          <set-service property="provider" service-id="${ex.override.Provider}"/>
        </construct>
      </invoke-factory>
    </service>
  </module>
  ]]>	
  </source>	
  </p>	
  
  <p>
  The end result is the same ... the symbol <code>ex.override.Provider</code>	 evaluates
  to the service id <code>ex.override.Provider</code> and the end result is the same as step one.
  We needed to use a fully qualified service id because, ultimately, we don't know in which modules
  the symbol will be referenced.
  </p>
  
  	
  </section>
  
  <section name="Step Three: Override!">
  	
  
  <p>
  The final step is to define a second service and slip it into place. For kicks, the OverrideProvider
  service will get a reference to the original Provider service.
  <source><![CDATA[
  <?xml version="1.0"?>
  <module id="ex.override" version="1.0.0">
    <service id="Provider" interface="ex.override.Provider">
      <create-instance class="ex.override.impl.ProviderImpl"/>
    </service>
    
    <extension point-id="hivemind.FactoryDefaults">
      <default symbol="ex.override.Provider" value="ex.override.Provider"/>
    </extension>
    
    <service id="OverrideProvider" interface="ex.override.Provider">
      <invoke-factory service-id="hivemind.BuilderFactory">
        <construct class="ex.override.impl.OverrideProviderImpl">
          <set-service property="defaultProvider" service-id="Provider"/>
        </construct>
      </invoke-factory>
    </service>
    
    <extension point-id="hivemind.ApplicationDefaults">
      <default symbol="ex.override.Provider" value="ex.override.OverrideProvider"/>
    </extension>  
    
    <service id="Consumer" interace="ex.override.Consumer">
      <invoke-factory service-id="hivemind.BuilderFactory">
        <construct class="ex.override.impl.Consumer">
          <set-service property="provider" service-id="${ex.override.Provider}"/>
        </construct>
      </invoke-factory>
    </service>
  </module>
  ]]>	
  </source>	
  	
  </p>
  
  <p>
  The new service, OverrideProvider, gets a reference to the original service using its
  real id.  It can't use the symbol that the Consumer service uses, because that would end up pointing
  it at itself.	Again, in this example it's all happening in a single module, but it could absolutely be split
  up, with OverrideProvider and the extension to <code>hivemind.ApplicationDefaults</code> in an entirely
  different module.
  </p>
  
  <p>
  <code>hivemind.ApplicationDefaults</code>	overrides <code>hivemind.FactoryDefaults</code>. 
  This means that the Consumer
  will be connected to <code>ex.override.OverrideProvider</code>.
  </p>
  
  <p>
  Note that the &_service; for the Consumer doesn't change between steps two and three.
  </p>
  
  </section>
  
  <section name="Limitations">
  	
  <p>
  The main limitation to this approach is that you can only do it once for a service; there's no
  way to add an EvenMoreOverridenProvider service that wraps around OverrideProvider (that wraps around Provider).
  Making multiple contributions to the <code>hivemind.ApplicationDefaults</code>	 extension point
  with the name symbol name will result in a runtime error ... and unpredictable results.
  </p>
  
  <p>
  This could be addressed by adding another <code>SymbolSource</code> (<code>hivemind.ApplicationDefaults</code> and
  <code>hivemind.FactoryDefaults</code> are <code>SymbolSource</code>s).	
  </p>
  
  <p>
  To be honest, if this kind of indirection becomes extremely frequent, then HiveMind should change
  to accomadate the pattern, perhaps adding an <code>&lt;override&gt;</code> element, similar to a &_interceptor; element.	
  </p>
  
  </section>
  
  </body>
  </document>