/* ======================================================================
The Bodington System Software License, Version 1.0

Copyright (c) 2001 The University of Leeds.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
=========================================================

This software was originally created by the University of Leeds and may contain voluntary
contributions from others.  For more information on the Bodington Open Source Project, please
see http://bodington.org/

====================================================================== */

package org.bodington.servlet.facilities;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.util.Hashtable;

import javax.xml.transform.Templates;

import org.apache.log4j.Logger;
import org.bodington.server.BuildingContext;
import org.bodington.server.BuildingServerException;
import org.bodington.server.realm.PassPhrase;
import org.bodington.server.realm.Permission;
import org.bodington.server.realm.User;
import org.bodington.server.resources.FeedCache;
import org.bodington.server.resources.FeedResource;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceTreeManager;
import org.bodington.servlet.FacilityList;
import org.bodington.servlet.Request;
import org.bodington.servlet.template.Template;
import org.bodington.util.TextUtils;
import org.jafer.exception.JaferException;
import org.jafer.util.xml.DOMFactory;
import org.jafer.util.xml.XMLSerializer;
import org.jafer.util.xml.XMLTransformer;
import org.w3c.dom.Document;

/**
 * @author buckett
 */
public abstract class FeedFacility extends Facility
{
    private static Logger log = Logger.getLogger(FeedFacility.class);
    private static String TRANSFORM_STYLESHEET = "/org/bodington/xsl/feedTransform.xsl";
    private static String PRIVATE_STEM =  "http://hcdt1.oucs.ox.ac.uk/pebble/";
    private Templates stylesheetTemplate;
    private String statusMessage;
    
    public boolean canCopy( Resource resource )
    {
        return true;
    }
    
    protected boolean displayCurrentSettings(String command, FeedResource resource, PrintWriter out)
    {
        // returns boolean if command was acted upon.
        if (command.equalsIgnoreCase("inline"))
        {
            if (resource.isDisplayInline()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("image"))
        {
            if (resource.isDisplayImage()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("title"))
        {
            if (resource.isDisplayTitle()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("description"))
        {
            if (resource.isDisplayDescription()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("author"))
        {
            if (resource.isDisplayAuthor()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("allowlinks"))
        {
            if (resource.isAllowTitleLinks()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("date"))
        {
            if (resource.isDisplayDate()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("maxnumber"))
        {
            out.print(resource.getMaxNumberOfItems());
            return true;
        }
    
        if (command.equalsIgnoreCase("itemtitles"))
        {
            if (resource.isDisplayItemTitles()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("itemauthors"))
        {
            if (resource.isDisplayItemAuthors()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("content"))
        {
            if (resource.isDisplayItemContent()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("excerpt"))
        {
            if (resource.isDisplayExcerpt()) out.print("checked");
            return true;
        }
    
        if (command.equalsIgnoreCase("itemdate"))
        {
            if (resource.isDisplayItemDate()) out.print("checked");
            return true;
        }
        
        if (command.equalsIgnoreCase("itemseparator"))
        {
            out.print(resource.getItemSeparationTag());
            return true;
        }
    
        if (command.equalsIgnoreCase(getURLParameter()))
        {
            out.print(resource.getURL());
            return true;
        }
    
        return false;
    }

    protected void updateDisplayInline(Request req, FeedResource resource)
    {
        if (req.getParameter("inline") != null && !resource.isDisplayInline())
        {
            resource.setDisplayInline(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("inline") == null
            && resource.isDisplayInline())
        {
            resource.setDisplayInline(false);
            resource.setUnsaved();
        }
    }

    protected void updateMaxNumberOfItems(Request req, FeedResource resource)
    {
        if (req.getParameter("maxnumber") != null)
        {
            String maxNumber = req.getParameter("maxnumber");
            if (!resource.getMaxNumberOfItems().equals(maxNumber)) try
            {
                int newMax = Integer.parseInt(maxNumber); // check that it's a
                // number.
                resource.setMaxNumberOfItems(maxNumber);
                resource.setUnsaved();
            }
            catch (NumberFormatException ex)
            {
                //        do nothing... // TODO display error message?
            }
        }
    }

    protected void updateDisplayImage(Request req, FeedResource resource)
    {
        if (req.getParameter("image") != null && !resource.isDisplayImage())
        {
            resource.setDisplayImage(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("image") == null && resource.isDisplayImage())
        {
            resource.setDisplayImage(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayTitle(Request req, FeedResource resource)
    {
        if (req.getParameter("title") != null && !resource.isDisplayTitle())
        {
            resource.setDisplayTitle(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("title") == null && resource.isDisplayTitle())
        {
            resource.setDisplayTitle(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayDescription(Request req, FeedResource resource)
    {
        if (req.getParameter("description") != null
            && !resource.isDisplayDescription())
        {
            resource.setDisplayDescription(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("description") == null
            && resource.isDisplayDescription())
        {
            resource.setDisplayDescription(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayAuthor(Request req, FeedResource resource)
    {
        if (req.getParameter("author") != null && !resource.isDisplayAuthor())
        {
            resource.setDisplayAuthor(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("author") == null
            && resource.isDisplayAuthor())
        {
            resource.setDisplayAuthor(false);
            resource.setUnsaved();
        }
    }

    protected void updateAllowTitleLinks(Request req, FeedResource resource)
    {
        if (req.getParameter("allowlinks") != null
            && !resource.isAllowTitleLinks())
        {
            resource.setAllowTitleLinks(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("allowlinks") == null
            && resource.isAllowTitleLinks())
        {
            resource.setAllowTitleLinks(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayDate(Request req, FeedResource resource)
    {
        if (req.getParameter("date") != null && !resource.isDisplayDate())
        {
            resource.setDisplayDate(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("date") == null && resource.isDisplayDate())
        {
            resource.setDisplayDate(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayItemTitles(Request req, FeedResource resource)
    {
        if (req.getParameter("itemtitles") != null
            && !resource.isDisplayItemTitles())
        {
            resource.setDisplayItemTitles(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("itemtitles") == null
            && resource.isDisplayItemTitles())
        {
            resource.setDisplayItemTitles(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayItemAuthors(Request req, FeedResource resource)
    {
        if (req.getParameter("itemauthors") != null
            && !resource.isDisplayItemAuthors())
        {
            resource.setDisplayItemAuthors(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("itemauthors") == null
            && resource.isDisplayItemAuthors())
        {
            resource.setDisplayItemAuthors(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayItemContent(Request req, FeedResource resource)
    {
        if (req.getParameter("content") != null
            && !resource.isDisplayItemContent())
        {
            resource.setDisplayItemContent(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("content") == null
            && resource.isDisplayItemContent())
        {
            resource.setDisplayItemContent(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayExcerpt(Request req, FeedResource resource)
    {
        if (req.getParameter("excerpt") != null && !resource.isDisplayExcerpt())
        {
            resource.setDisplayExcerpt(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("excerpt") == null
            && resource.isDisplayExcerpt())
        {
            resource.setDisplayExcerpt(false);
            resource.setUnsaved();
        }
    }

    protected void updateDisplayItemDate(Request req, FeedResource resource)
    {
        if (req.getParameter("date") != null && !resource.isDisplayItemDate())
        {
            resource.setDisplayItemDate(true);
            resource.setUnsaved();
        }
        else if (req.getParameter("date") == null
            && resource.isDisplayItemDate())
        {
            resource.setDisplayItemDate(false);
            resource.setUnsaved();
        }
    }

    protected void updateItemSeparationTag(Request req, FeedResource resource)
    {
        if (req.getParameter("itemseparator") != null)
        {
            String separationTag = req.getParameter("itemseparator");
            if (!separationTag.equals(resource.getItemSeparationTag()))
            {
                resource.setItemSeparationTag(separationTag);
                resource.setUnsaved();
            }
        }
    }

    protected void updateURL(Request req, FeedResource resource)
    throws BuildingServerException
    {
        if (req.getParameter(getURLParameter()) != null)
        {
            String url = req.getParameter(getURLParameter());
            try 
            {
                if (denyAccess(new URL(url)))
                {
                    throw new BuildingServerException("You are not allowed access to this URL");
                }
            } 
            catch (MalformedURLException mue)
            {
                throw new BuildingServerException("The URL you supplied does not apear to be valid");
            }
            
            if (!url.equals(resource.getURL()))
            {
                resource.setURL(url);
                resource.setUnsaved();
            }
        }
    }

    protected void resourceMenuItem(Resource resource, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight) throws IOException, BuildingServerException
    {
        FeedResource newsfeed = (FeedResource) resource;
        if (newsfeed.isDisplayInline())
            displayInline(newsfeed, breq, out, state, depth, highlight);
        else
            displayLinked(newsfeed, breq, out, state, depth, highlight);
    }

    private void displayInline(FeedResource resource, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight) throws IOException, BuildingServerException
    {
        boolean manage;
        int level;
        String href, target, content;
    
        content = transformFeed(resource);
    
        manage = resource.checkPermission(Permission.MANAGE);
        href = (!state.rootless && depth == 1) ? null : (breq.getContextPath()
            + breq.getServletPath() + resource.getFullName());
        target = "_top";
    
        //    if ( !resource.checkPermission( Permission.VIEW ) ) // else : just
        // don't see it at all!
        //    {
    
        level = ((depth - 1) % 10) + 1;
    
        // span is used to get everything on one line in browsers that
        // lack CSS but CSS redfines as block.
        out.print("<span class=\"" + state.css_class_prefix + "_node_content");
        if (depth == 1) out.print("_without_stalk");
        out.print("_lev");
        out.print(level);
        out.print("_hl");
        out.print(highlight);
        out.print("\">");
    
        if (href != null && manage)
        {
            out.print("<a");
            if (target != null)
            {
                out.print(" target=\"");
                out.print(target);
                out.print("\"");
            }
            out.print(" href=\"");
            out.print(href);
            out.print("\" title=\"Manage "+ getFacilityName()+ ".\"><span style=\"font-size: x-small\"><sup>Manage</sup></span></a>");
        }
    
        out.print(content);
        // TODO Do we want to have timed release?
        //String markup = timedResourceText(resource);
        //if (markup != null)
        //    out.print("<br>" + markup);
    
        out.print("</span>");
    
    }

    private void displayLinked(Resource resource, Request breq, PrintWriter out, ResourceMenuOutputState state, int depth, int highlight) throws IOException, BuildingServerException
    {
        Facility facility;
        Template template;
        String href = null, manage_href, icon_src = null, title = null, target = null, manage_target, content;
        boolean italic = false; // should really use a redefinable style
    
        boolean manage = false;
        boolean deny = false;
    
        manage = resource.checkPermission(Permission.MANAGE);
        href = (!state.rootless && depth == 1) ? null : (breq.getContextPath()
            + breq.getServletPath() + resource.getFullName());
        target = "_top";
    
        manage_href = href + "bs_template_manage.html";
        manage_target = "menu";
    
        // using getTemplateGifUrl doesn't work for descendant resources - need
        // to
        // reference proper resource.
        facility = state.fl.get(new Integer(resource.getHttpFacilityNo()));
        if (facility != null)
        {
            template = Template.get(facility.facilityname, resource
                .getImplicitHttpUIStyle(), resource.getResourceId(),
                state.big_icons ? "icon.gif" : "iconsmall.gif");
            if (template != null)
                icon_src = breq.getContextPath() + state.gif_url
                    + template.getUrl() + state.colour_code;
        }
        title = resource.getTitle();
        if (!resource.checkPermission(Permission.VIEW))
        {
            if (state.anon)
                // anonymous users are allowed to see link because
                // they get a message about logging in if they enter
                italic = true;
            else
            {
                // logged in but no access - don't give link
                href = null;
                deny = true;
            }
        }
    
        // heading level for title line matches depth but there are only
        // six heading levels in HTML so stop there.
        int hlevel = (depth > 6) ? 6 : depth;
        int level = ((depth - 1) % 10) + 1;
    
        // title in heading with appropriate class attribute
        out.print("          <h");
        out.print(hlevel);
    
        out.print(" class=\"" + state.css_class_prefix + "_node_title_lev");
        out.print(level);
        out.print("\">");
    
        // optional link around whole title (inc. icon)
        // changed to 2 separate links for icon and text
        // (and a third for the manage link)
        if (href != null)
        {
            out.print("<a"); // link for icon
            if (target != null)
            {
                out.print(" target=\"");
                out.print(target);
                out.print("\"");
            }
            out.print(" href=\"");
            out.print(href);
            out.print("\">");
        }
        // <altAndTitle> --->>>
        out.print("<img alt=\"Icon for " 
            + (TextUtils.beginsWithVowel(resource.getResourceTypeName()) ? "an" : "a") 
            + " " + resource.getResourceTypeName() + ".\" src=\"");
        // <<<--- <altAndTitle>
        out.print(icon_src);
        out.print("\" ");
    
        if (depth == 1)
            out.print("class=\"" + state.css_class_prefix
                + "_node_icon_without_stalk\" />");
        else
            out.print("class=\"" + state.css_class_prefix
                + "_node_icon_no_expander\" />");
    
        if (href != null) out.print("</a>"); // closing tag for icon link
    
        // span is used to get everything on one line in browsers that
        // lack CSS but CSS redfines as block.
        out.print("<span class=\"" + state.css_class_prefix + "_node_content");
        if (depth == 1) out.print("_without_stalk");
        out.print("_lev");
        out.print(level);
        out.print("_hl");
        out.print(highlight);
        out.print("\">");
    
        if (href != null)
        {
            out.print("<a"); // tag for text link
            if (target != null)
            {
                out.print(" target=\"");
                out.print(target);
                out.print("\"");
            }
            out.print(" href=\"");
            out.print(href);
            out.print("\">");
        }
    
        if (italic) out.print("<i>");
    
        if (title != null)
        {
            out.print(title);
        }
    
        else
        {
            if (href != null)
                out.print(href);
            else
                out.print("{untitled item}");
        }
    
        if (italic) out.print("</i>");
    
        if (href != null) out.print("</a>"); // closing tag for text link
    
        if (href != null && manage)
        {
            out.print("&nbsp;<a"); // tag for manage link
            if (target != null)
            {
                out.print(" target=\"");
                out.print(manage_target);
                out.print("\"");
            }
            out.print(" href=\"");
            out.print(manage_href);
            out.print("\" title=\"Manage "+ getFacilityName()+ 
            		".\"><span style=\"font-size: x-small\"><sup>Manage</sup></span></a>");// closing tag for manage link
        }
    
        out.print("</span>");
    
        out.print("</h"); // end of node_title
        out.print(hlevel);
        out.println(">");
    
        if (!state.rootless && depth == 1)
            content = resource.getIntroduction();
        else
            content = resource.getDescription();
        if (deny)
        {
            content = "<em>"
                + "You are not included on the access list for this item.</em>"
                + " <br />" + content;
        }
    
        content = (content == null) ? "" : content;
    
        out.print("<div class=\"" + state.css_class_prefix + "_node_content");
        if (depth == 1) out.print("_without_stalk");
        out.print("_lev");
        out.print(level);
        out.print("_hl");
        out.print(highlight);
        out.print("\">");
    
        out.print(content);
        
        String markup = timedResourceText(resource);
        if (markup != null)
            out.print("<div>" + markup + "</div>");
    
        out.print("</div>");
        out.print("<div class=\"clearer\">&nbsp;</div>\n");
    }

    protected String getFrameDisplay(FeedResource resource)
    {
        if (resource.isDisplayInline())
        { // TODO keep this? (loads manage page instead of displaying blank
            // page...)
            String redirect = "<SCRIPT LANGUAGE=JavaScript>"
                + "top.buildmain.feed.location.href=\"./bs_template_manage.html\""
                + "</SCRIPT>";
            return redirect;
        }
    
        return transformFeed(resource);
    }

    /**
     * Fomat the supplied feed with HTML.
     * @param resource A FeedResource object to format.
     * @return A String HTML rendering of the Feed.
     */
    public String transformFeed(FeedResource resource)
    {
        // Displays XML feed transformed to HTML.
        // If a problem occurs, and display is set to:
        // inline - nothing displayed at all.
        // not inline - an error message is displayed.
    
        try
        {
            FeedCache feedCache = resource.getFeedCache();
            feedCache.update();
            return transformFeed(new ByteArrayInputStream(feedCache
                .getData().getBytes()), resource.loadTransformParams());
        }
        catch (JaferException ex)
        {
            setStatusMessage("Error in parsing/transforming XML to HTML."
                + ex.getMessage());
        }
    
        log.error(getStatusMessage());
    
        if (!resource.isDisplayInline()) return getStatusMessage();
    
        return "";
    }
    
    protected String transformFeed(InputStream data, Hashtable paramMap) throws JaferException
    {
        Document doc = DOMFactory.parse(data);
        StringWriter writer = new StringWriter();
        
        // TODO Should be able to use a re-useable template
        // instead of loading the stylesheet every time, but it doesn't
        // work...
        
        //            root = XMLTransformer.transform(paramMap, root,
        // stylesheetTemplate);
        //            XMLSerializer.out(root, "html", writer);
        InputStream stylesheet = getClass().getResourceAsStream(
            BuildingContext.getProperty("blog.xslt", TRANSFORM_STYLESHEET));
        //            URL stylesheet = getClass().getResource(TRANSFORM_STYLESHEET);
        XMLSerializer.transformOutput(doc, stylesheet, paramMap, writer);
        
        return writer.toString();
    }

    private String getStatusMessage()
    {
        if (statusMessage != null) return statusMessage;
    
        return "No status message available.";
    }

    private void setStatusMessage(String message)
    {
        if (message != null) statusMessage = message;
    }

    /**
     * Check if the current use should have access to the URL.
     * It currently checks against the user in the current context. 
     * The check is to see if the beginning of the URL matches the restricted
     * URL if it does then check that the initials of the user match the URL path.
     * @param url The URL against which are performing the check.
     * @return True if the user should be denyed access.
     */
    protected boolean denyAccess(URL url)
    {
        String username;
        try
        {
            PassPhrase passPhrase = PassPhrase.findPassPhrase(
                (User) BuildingContext.getContext().getUser()
            );
            if (passPhrase == null || passPhrase.getUserName() == null)
            {
                return false;
            }
            username = passPhrase.getUserName();
        }
        catch (BuildingServerException e)
        {
            return false;
        }
        URL privateStem;
        try
        {
            privateStem = new URL(BuildingContext.getProperty("blog.private",
                PRIVATE_STEM));
        }
        catch (MalformedURLException mue)
        {
            return false;
        }
    
        if (privateStem.getProtocol().equals(url.getProtocol()) && 
                        privateStem.getHost().equals(url.getHost()) &&
                        privateStem.getPort() == url.getPort())
        {
            if (url.getPath().startsWith(privateStem.getPath()) && 
                (url.getPath().substring(privateStem.getPath().length())).startsWith(username+"/"))
                return false;
            else
                return true;
        }
        return false;
    }

    private void createStylesheetTemplate(String stylesheetPath) throws JaferException
    {
        InputStream stylesheet = getClass().getResourceAsStream(stylesheetPath);
        stylesheetTemplate = XMLTransformer.createTemplate(stylesheet);
    }

    /**
     * Used to define what the string is that should be used as the URL paramter in subclasses.
     * @return Returns the URL parameter that is used.
     */
    abstract protected  String getURLParameter();

    /**
     * Used to define what the string is that should be used as the Display Name in subclasses.
     * @return Returns the Facility name that should be displayed to the user.
     */
    abstract protected String getFacilityName();

    
    /**
     * Check that it is ok to create this resource.
     * The problem with this method is that it doesn't display a nice error message.
     * @return True if it is ok to create this resource. 
     */
    public boolean create( Request breq, Connection con, Resource newResource )
	throws Exception
	{
        return (!denyAccess(new URL(((FeedResource)newResource).getURL())));
	}
    
    /**
     * This attempts to render a feed. Can be called in any template 
     * to render a feed on that page. Development of this was done
     * to allow RSS in the login page.
     * The output of this call should be valid XML and so shouldn't break
     * page rendering but may contain evil code from the RSS feed.
     * @param out The PrintWriter to write the rendered feed to.
     * @param resourceUrl The bodington resource to display
     */
    public static void displayFeed(PrintWriter out, String resourceUrl)
    {
        Resource resource = null;
        try
        {
            resource = ResourceTreeManager.getInstance().findResource(resourceUrl);
        }
        catch (BuildingServerException e)
        {
            log.error("Problem find resource: "+ resourceUrl, e);
        }
        if (resource instanceof FeedResource)
        {
            FeedResource feedResource = (FeedResource)resource;
            FeedFacility  feedFacility = (FeedFacility)FacilityList.getFacilities().get(new Integer(feedResource.getHttpFacilityNo()));
            out.write(feedFacility.transformFeed(feedResource));
        }
        else
        {
            out.println("<h3>No News</h3>");
            out.println(resourceUrl+ "  does not appear to be a FeedResource.");
        }
    }
}
