TILES-538 - Pluggable debugging around rendering


git-svn-id: https://svn.apache.org/repos/asf/tiles/framework/trunk@1199201 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java
new file mode 100644
index 0000000..5ef0f87
--- /dev/null
+++ b/tiles-request/tiles-request-api/src/main/java/org/apache/tiles/request/render/PublisherRenderer.java
@@ -0,0 +1,102 @@
+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.tiles.request.render;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tiles.request.Request;
+
+/**
+ * Provides a Publisher-Subscriber implementation around the provided renderer to delegate to.
+ *
+ * @version $Rev: 1035784 $ $Date: 2010-11-16 20:24:12 +0000 (Tue, 16 Nov 2010) $
+ * @since 3.0.0
+ */
+public class PublisherRenderer implements TypeDetectingRenderer {
+
+    public interface RendererListener{
+        /** Called before the delegate's render method is called. */
+        void start(String template, Request request) throws IOException;
+        /** Called after the delegate's render method is called. */
+        void end(String template, Request request) throws IOException;
+        /** If the delegate render method throws an IOException it is passed through this. */
+        void handleIOException(IOException ex, Request request) throws IOException;
+    }
+
+    private final TypeDetectingRenderer renderer;
+    private final List<RendererListener> listeners = new ArrayList<RendererListener>();
+    private final List<RendererListener> listenersReversed = new ArrayList<RendererListener>();
+
+    public PublisherRenderer(TypeDetectingRenderer renderer){
+        this.renderer = renderer;
+    }
+
+    @Override
+    public void render(String path, Request request) throws IOException {
+        if (path == null) {
+            throw new CannotRenderException("Cannot dispatch a null path");
+        }
+        try{
+            for(RendererListener listener : listeners){
+                listener.start(path, request);
+            }
+            renderer.render(path, request);
+        }catch(IOException ex){
+            handleIOException(ex, request);
+        }finally{
+            for(RendererListener wrapper : listenersReversed){
+                wrapper.end(path, request);
+            }
+        }
+    }
+
+    @Override
+    public boolean isRenderable(String path, Request request) {
+        return renderer.isRenderable(path, request);
+    }
+
+    public void addListener(RendererListener listener){
+        listeners.add(listener);
+        listenersReversed.clear();
+        listenersReversed.addAll(listeners);
+        Collections.reverse(listenersReversed);
+    }
+
+    private void handleIOException(IOException exception, Request request) throws IOException{
+        IOException ex = exception;
+        boolean throwIt = listeners.isEmpty();
+        for(RendererListener listener : listenersReversed){
+            try{
+                listener.handleIOException(ex, request);
+                throwIt = false;
+            }catch(IOException newEx){
+                ex = newEx;
+                throwIt = true;
+            }
+        }
+        if(throwIt){
+            throw ex;
+        }
+    }
+}