Hey guys,

Thanks to help of Sylvain :) I'd like to present this horrible hack which shows how VPC can be implemented, in particular example of Virtual Pipeline Generator. VPC generator is defined in the sitemap as:

  <!--
    - Virtual Pipeline Generator: Composed of other pipeline components.
    -->
  <map:generator name="virtual"
                 src="org.apache.cocoon.generation.VirtualPipelineGenerator">
    <map:generate src="welcome.xml"/>
    <map:transform src="welcome.xslt">
      <map:parameter name="contextPath" value="{request:contextPath}"/>
    </map:transform>
  </map:generator>

Later on, VPC generator can be used from any sub sitemap (as is the case with regular sitemap components) like this:

  <map:match pattern="x">
    <map:generate type="virtual"/>
    <map:serialize type="xhtml"/>
  </map:match>

All sources used in the definition of VPC generator will be resolved relative to the parent sitemap. All sources mentioned in the src attribute will be resolved relative to caller sitemap (example: <map:generate type="virtual" src="relative.xml"/>).

To see VPC generator in action, apply attached patch to the trunk and go to:
  http://localhost:8888/
  http://localhost:8888/samples/x


Now, there are several things which should be resolved to have proper implementaion:

  1. Figure out proper way of switching environment.
  2. Figure out how ComponentsNodeBuilder can access SitemapLanguage's
     manager's avalon sub-context.
  3. Implement caching
  4. Implement parameter passing & resolving
  5. Refactor classes implementing ProcessingPipeline


Help is welcome :)

Vadim
Index: 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/SitemapLanguage.java
===================================================================
--- 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/SitemapLanguage.java
    (revision 76031)
+++ 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/SitemapLanguage.java
    (working copy)
@@ -15,20 +15,12 @@
  */
 package org.apache.cocoon.components.treeprocessor.sitemap;
 
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-
 import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.avalon.framework.configuration.ConfigurationException;
 import org.apache.avalon.framework.configuration.DefaultConfiguration;
+import org.apache.avalon.framework.context.DefaultContext;
 import org.apache.avalon.framework.service.ServiceManager;
+
 import org.apache.cocoon.acting.Action;
 import org.apache.cocoon.components.container.CocoonServiceManager;
 import org.apache.cocoon.components.pipeline.ProcessingPipeline;
@@ -37,6 +29,8 @@
 import org.apache.cocoon.components.treeprocessor.DefaultTreeBuilder;
 import org.apache.cocoon.components.treeprocessor.ProcessorComponentInfo;
 import 
org.apache.cocoon.components.treeprocessor.variables.VariableResolverFactory;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.environment.internal.EnvironmentHelper;
 import org.apache.cocoon.generation.Generator;
 import org.apache.cocoon.matching.Matcher;
 import org.apache.cocoon.reading.Reader;
@@ -45,8 +39,19 @@
 import org.apache.cocoon.sitemap.PatternException;
 import org.apache.cocoon.transformation.Transformer;
 import org.apache.cocoon.util.StringUtils;
+
 import org.apache.regexp.RE;
 
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
 /**
  * The tree builder for the sitemap language.
  *
@@ -78,9 +83,16 @@
 
         CocoonServiceManager newManager = new 
CocoonServiceManager(this.parentProcessorManager, null);
 
+        // Create sub-context for this sitemap
+        DefaultContext newContext = new DefaultContext(this.context);
+        Environment env = EnvironmentHelper.getCurrentEnvironment();
+        newContext.put("ENVURI", env.getURI());
+        newContext.put("ENVPREFIX", env.getURIPrefix());
+        newContext.put("ENVHELPER", 
getProcessor().getWrappingProcessor().getEnvironmentHelper());
+
         // Go through the component lifecycle
         newManager.enableLogging(getLogger());
-        newManager.contextualize(this.context);
+        newManager.contextualize(newContext);
         newManager.configure(config);
         newManager.initialize();
 
Index: 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/ComponentsNodeBuilder.java
===================================================================
--- 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/ComponentsNodeBuilder.java
      (revision 76030)
+++ 
src/java/org/apache/cocoon/components/treeprocessor/sitemap/ComponentsNodeBuilder.java
      (working copy)
@@ -24,10 +24,11 @@
  * Handles &lt;map:components&gt;. It doesn't actually create a 
<code>ProcessingNode</code>.
  *
  * @author <a href="mailto:[EMAIL PROTECTED]">Sylvain Wallez</a>
- * @version CVS $Id: ComponentsNodeBuilder.java,v 1.2 2004/03/05 13:02:51 
bdelacretaz Exp $
+ * @version CVS $Id$
  */
-
 public class ComponentsNodeBuilder extends AbstractProcessingNodeBuilder {
+    // TODO: See TODO below
+    public static ProcessingNode HORRIBLE_HACK;
 
     /** This builder has no parameters -- return <code>false</code> */
     protected boolean hasParameters() {
@@ -35,8 +36,25 @@
     }
 
     public ProcessingNode buildNode(Configuration config) throws Exception {
+        // check for component configurations
+        Configuration child = config.getChild("generators", false);
+        if (child != null) {
+            Configuration[] generators = child.getChildren("generator");
+            for (int i = 0; i < generators.length; i++) {
+                Configuration generator = generators[i];
+                if ("virtual".equals(generator.getAttribute("name"))) {
+                    // Got it
 
-        // Nothing more here. To be removed ;)
+                    PipelineNodeBuilder builder = new PipelineNodeBuilder();
+                    builder.setBuilder(this.treeBuilder);
+                    ProcessingNode node = builder.buildNode(generator);
+                    // TODO Stuff this node somehow into the context of 
SitemapLanguage's
+                    //      component manager so that VirtualPipelineComponent 
can find it
+                    HORRIBLE_HACK = node;
+                }
+            }
+        }
+
         return null;
     }
 }
Index: src/java/org/apache/cocoon/generation/VirtualPipelineGenerator.java
===================================================================
--- src/java/org/apache/cocoon/generation/VirtualPipelineGenerator.java 
(revision 0)
+++ src/java/org/apache/cocoon/generation/VirtualPipelineGenerator.java 
(revision 0)
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cocoon.generation;
+
+import org.apache.avalon.framework.context.Context;
+import org.apache.avalon.framework.context.ContextException;
+import org.apache.avalon.framework.context.Contextualizable;
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.components.pipeline.VirtualProcessingPipeline;
+import org.apache.cocoon.components.pipeline.ProcessingPipeline;
+import org.apache.cocoon.components.treeprocessor.InvokeContext;
+import org.apache.cocoon.components.treeprocessor.ProcessingNode;
+import 
org.apache.cocoon.components.treeprocessor.sitemap.ComponentsNodeBuilder;
+import org.apache.cocoon.environment.Environment;
+import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.environment.internal.EnvironmentHelper;
+import org.apache.cocoon.xml.XMLConsumer;
+
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ *
+ */
+public class VirtualPipelineGenerator implements Generator, Serviceable, 
Contextualizable {
+    private Context context;
+    private ServiceManager manager;
+    private XMLConsumer consumer;
+    private ProcessingNode node;
+    private ProcessingPipeline pipeline;
+
+    private class MyInvokeContext extends InvokeContext {
+        public MyInvokeContext() throws Exception {
+            super(true);
+            // TODO: Refactor ProcessingPipelines implementations
+            super.processingPipeline = new 
VirtualProcessingPipeline(VirtualPipelineGenerator.this.context);
+        }
+    }
+
+    public void contextualize(Context context) throws ContextException {
+        this.context = context;
+        // TODO: Get created node from somewhere
+        this.node = ComponentsNodeBuilder.HORRIBLE_HACK;
+    }
+
+    public void service(ServiceManager manager) throws ServiceException {
+        this.manager = manager;
+    }
+
+    public void setConsumer(XMLConsumer consumer) {
+        this.consumer = consumer;
+    }
+
+    // TODO: Implement caching
+
+    public void setup(SourceResolver resolver, Map objectModel, String src, 
Parameters par)
+    throws ProcessingException, SAXException, IOException {
+
+        Environment env = EnvironmentHelper.getCurrentEnvironment();
+        String oldPrefix = env.getURIPrefix();
+        String oldURI    = env.getURI();
+        try {
+            String prefix = (String) context.get("ENVPREFIX");
+            String uri = (String) context.get("ENVURI");
+            env.setURI(prefix, uri);
+
+            MyInvokeContext invoker = new MyInvokeContext();
+            invoker.service(this.manager);
+            this.node.invoke(env, invoker);
+            this.pipeline = invoker.getProcessingPipeline();
+        } catch (Exception e) {
+            throw new ProcessingException("Oops", e);
+        } finally {
+            // Restore context
+            env.setURI(oldPrefix, oldURI);
+        }
+    }
+
+    public void generate()
+    throws IOException, SAXException, ProcessingException {
+
+        // Should use SourceResolver of the this components' sitemap, not 
caller sitemap
+        // Have to switch to another environment...
+        Environment env = EnvironmentHelper.getCurrentEnvironment();
+        String oldPrefix = env.getURIPrefix();
+        String oldURI    = env.getURI();
+        try {
+            String prefix = (String) context.get("ENVPREFIX");
+            String uri = (String) context.get("ENVURI");
+            env.setURI(prefix, uri);
+
+            this.pipeline.prepareInternal(env);
+        } catch (Exception e) {
+            throw new ProcessingException("Oops", e);
+        } finally {
+            // Restore context
+            env.setURI(oldPrefix, oldURI);
+        }
+
+        this.pipeline.process(env, this.consumer);
+    }
+}
Index: src/webapp/samples/sitemap.xmap
===================================================================
--- src/webapp/samples/sitemap.xmap     (revision 76030)
+++ src/webapp/samples/sitemap.xmap     (working copy)
@@ -59,6 +59,11 @@
     </map:component-configurations>
 
     <map:pipeline>
+      <map:match pattern="x">
+        <map:generate type="virtual"/>
+        <map:serialize type="xhtml"/>
+      </map:match>
+
       <map:match pattern="">
         <map:generate src="samples.xml"/>
         <map:transform 
src="context://samples/common/style/xsl/html/simple-samples2html.xsl">
Index: src/webapp/sitemap.xmap
===================================================================
--- src/webapp/sitemap.xmap     (revision 76030)
+++ src/webapp/sitemap.xmap     (working copy)
@@ -77,6 +77,16 @@
     <!-- The notifying generator can only be used in a <handle-errors> section 
: it produces an XML
          representation of the exception that caused the error handler to be 
executed -->
     <map:generator name="notifying" 
src="org.apache.cocoon.sitemap.NotifyingGenerator"/>
+
+    <!--
+      - Virtual Pipeline Generator: Composed of other pipeline components.
+      -->
+    <map:generator name="virtual" 
src="org.apache.cocoon.generation.VirtualPipelineGenerator">
+      <map:generate src="welcome.xml"/>
+      <map:transform src="welcome.xslt">
+        <map:parameter name="contextPath" value="{request:contextPath}"/>
+      </map:transform>
+    </map:generator>
   </map:generators>
 
 
@@ -532,7 +542,7 @@
           | sends the events produced by the parser down the pipeline to
           | be processed by the next stage.
           +-->
-      <map:generate src="welcome.xml"/>
+      <map:generate type="virtual"/>
 
       <!--+
           | This transformer gets the input SAX events and transforms them
@@ -540,10 +550,10 @@
           | applying the XSLT stylesheet indicated in the "src" attribute
           | and sending the output down the pipeline to be processed by the
           | next stage.
-          +-->
       <map:transform src="welcome.xslt">
         <map:parameter name="contextPath" value="{request:contextPath}"/>
       </map:transform>
+      +-->
 
       <!--+
           | The serializer concludes the SAX events journey into the pipeline

Reply via email to