ch.entwine.weblounge.workbench.WorkbenchService.java Source code

Java tutorial

Introduction

Here is the source code for ch.entwine.weblounge.workbench.WorkbenchService.java

Source

/*
 *  Weblounge: Web Content Management System
 *  Copyright (c) 2003 - 2011 The Weblounge Team
 *  http://entwinemedia.com/weblounge
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program; if not, write to the Free Software Foundation
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package ch.entwine.weblounge.workbench;

import ch.entwine.weblounge.common.content.ResourceURI;
import ch.entwine.weblounge.common.content.page.Composer;
import ch.entwine.weblounge.common.content.page.Page;
import ch.entwine.weblounge.common.content.page.Pagelet;
import ch.entwine.weblounge.common.content.page.PageletRenderer;
import ch.entwine.weblounge.common.impl.content.page.PageReader;
import ch.entwine.weblounge.common.impl.testing.MockHttpServletRequest;
import ch.entwine.weblounge.common.impl.testing.MockHttpServletResponse;
import ch.entwine.weblounge.common.repository.ContentRepository;
import ch.entwine.weblounge.common.repository.ContentRepositoryException;
import ch.entwine.weblounge.common.request.WebloungeRequest;
import ch.entwine.weblounge.common.site.Environment;
import ch.entwine.weblounge.common.site.Module;
import ch.entwine.weblounge.common.site.Site;
import ch.entwine.weblounge.common.url.UrlUtils;
import ch.entwine.weblounge.workbench.suggest.SimpleSuggestion;

import org.apache.commons.io.IOUtils;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Implementation of a weblounge workbench. The workbench provides support for
 * management applications and the page editor.
 */
public class WorkbenchService {

    /** The logging facility */
    private static Logger logger = LoggerFactory.getLogger(WorkbenchService.class);

    /** The site servlets */
    private static Map<String, Servlet> siteServlets = new HashMap<String, Servlet>();

    /** The cache service tracker */
    private ServiceTracker siteServletTracker = null;

    /** Filter expression used to look up site servlets */
    private static final String serviceFilter = "(&(objectclass=" + Servlet.class.getName() + ")("
            + Site.class.getName().toLowerCase() + "=*))";

    /**
     * Callback from OSGi declarative services on component startup.
     * 
     * @param ctx
     *          the component context
     */
    void activate(ComponentContext ctx) {
        try {
            Filter filter = ctx.getBundleContext().createFilter(serviceFilter);
            siteServletTracker = new SiteServletTracker(ctx.getBundleContext(), filter);
            siteServletTracker.open();
        } catch (InvalidSyntaxException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Callback from OSGi declarative services on component shutdown.
     */
    void deactivate() {
        if (siteServletTracker != null) {
            siteServletTracker.close();
        }
    }

    /**
     * Returns a list of tags from the given site that are suggested based on what
     * is passed in as <code>text</code>. If <code>limit</code> is larger than
     * <code>0</code>, then this is the maximum number of facet values returned.
     * 
     * @param site
     *          the site
     * @param text
     *          the starting test
     * @param limit
     *          the maximum number of tags to return
     * @return the list of suggested tags
     * @throws IllegalStateException
     *           if the content repository is not available
     * @throws ContentRepositoryException
     *           if querying fails
     */
    public List<SimpleSuggestion> suggestTags(Site site, String text, int limit) throws IllegalStateException {
        List<SimpleSuggestion> tags = new ArrayList<SimpleSuggestion>();
        List<String> suggestions;
        try {
            suggestions = site.getContentRepository().suggest("subject", text, limit);
            for (String s : suggestions) {
                tags.add(new SimpleSuggestion("subject", s));
            }
        } catch (ContentRepositoryException e) {
            logger.warn("Error loading subject suggestions for '" + text + "'", e);
            return null;
        }
        return tags;
    }

    /**
     * Returns the pagelet editor or <code>null</code> if either one of the page,
     * the composer or the is not available.
     * 
     * @param site
     *          the site
     * @param pageURI
     *          the page uri
     * @param composerId
     *          the composer id
     * @param pageletIndex
     *          the pagelet index
     * @param language
     *          the environment
     * @param environment
     *          the execution environment
     * @return the pagelet editor
     * @throws IOException
     *           if reading the pagelet fails
     */
    public PageletEditor getEditor(Site site, ResourceURI pageURI, String composerId, int pageletIndex,
            String language, Environment environment) throws IOException {
        if (site == null)
            throw new IllegalArgumentException("Site must not be null");
        if (composerId == null)
            throw new IllegalArgumentException("Composer must not be null");
        if (pageletIndex < 0)
            throw new IllegalArgumentException("Pagelet index must be a positive integer");

        Page page = getPage(site, pageURI);
        if (page == null) {
            logger.warn("Client requested pagelet editor for non existing page {}", pageURI);
            return null;
        }

        // Load the composer
        Composer composer = page.getComposer(composerId);
        if (composer == null) {
            logger.warn("Client requested pagelet editor for non existing composer '{}' on page {}", composerId,
                    pageURI);
            return null;
        }

        // Get the pagelet
        if (composer.getPagelets().length <= pageletIndex || composer.size() <= pageletIndex) {
            logger.warn("Client requested pagelet editor for non existing pagelet on page {}", pageURI);
            return null;
        }

        Pagelet pagelet = composer.getPagelet(pageletIndex);
        pagelet = new TrimpathPageletWrapper(pagelet);
        PageletEditor pageletEditor = new PageletEditor(pagelet, pageURI, composerId, pageletIndex, environment);

        // Load the contents of the editor url
        URL editorURL = pageletEditor.getEditorURL();
        if (editorURL != null) {
            String rendererContent = null;
            try {
                rendererContent = loadContents(editorURL, site, page, composer, pagelet, environment, language);
                pageletEditor.setEditor(rendererContent);
            } catch (ServletException e) {
                logger.warn("Error processing the pagelet renderer at {}: {}", editorURL, e.getMessage());
            }
        }

        return pageletEditor;
    }

    /**
     * Returns the page or <code>null</code> if the page is not found.
     * 
     * @param site
     *          the site
     * @param pageURI
     *          the pareURI
     * @return the page
     */
    private Page getPage(Site site, ResourceURI pageURI) {
        // Get hold of the site's content repository
        ContentRepository contentRepository = site.getContentRepository();
        if (contentRepository == null) {
            logger.warn("No content repository found for site '{}'", site);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }

        // Load the page
        Page page = null;
        try {
            page = (Page) contentRepository.get(pageURI);
        } catch (ContentRepositoryException e) {
            logger.error("Error trying to access content repository {}: {}", contentRepository, e);
            throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
        }
        return page;
    }

    /**
     * Returns the pagelet renderer or <code>null</code> if either one of the
     * page, the composer or the is not available.
     * 
     * @param site
     *          the site
     * @param pageURI
     *          the page uri
     * @param composerId
     *          the composer id
     * @param pageletIndex
     *          the pagelet index
     * @param language
     *          the language
     * @param environment
     *          the environment
     * @return the pagelet renderer
     * @throws IOException
     *           if reading the pagelet fails
     */
    public String getRenderer(Site site, ResourceURI pageURI, String composerId, int pageletIndex, String language,
            Environment environment) throws IOException {

        Page page = getPage(site, pageURI);
        if (page == null) {
            logger.warn("Client requested pagelet renderer for non existing page {}", pageURI);
            return null;
        }

        // Load the composer
        Composer composer = page.getComposer(composerId);
        if (composer == null) {
            logger.warn("Client requested pagelet renderer for non existing composer {} on page {}", composerId,
                    pageURI);
            return null;
        }

        // Get the pagelet
        if (composer.getPagelets().length <= pageletIndex || composer.size() <= pageletIndex) {
            logger.warn("Client requested pagelet renderer for non existing pagelet on page {}", pageURI);
            return null;
        }

        Pagelet pagelet = composer.getPagelet(pageletIndex);
        Module module = site.getModule(pagelet.getModule());
        if (module == null) {
            logger.warn("Client requested pagelet renderer for non existing module {}", pagelet.getModule());
            return null;
        }

        PageletRenderer renderer = module.getRenderer(pagelet.getIdentifier());
        if (renderer == null) {
            logger.warn("Client requested pagelet renderer for non existing renderer on pagelet {}",
                    pagelet.getIdentifier());
            return null;
        }

        // Load the contents of the renderer url
        renderer.setEnvironment(environment);
        URL rendererURL = renderer.getRenderer();
        String rendererContent = null;
        if (rendererURL != null) {
            try {
                rendererContent = loadContents(rendererURL, site, page, composer, pagelet, environment, language);
            } catch (ServletException e) {
                logger.warn("Error processing the pagelet renderer at {}: {}", rendererURL, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }
        }

        return rendererContent;
    }

    public String getRenderer(Site site, ResourceURI pageURI, String composerId, int pageletIndex, String pageXml,
            String language, Environment environment)
            throws IOException, ParserConfigurationException, SAXException {

        InputStream is = null;
        Page page = null;
        try {
            PageReader pageReader = new PageReader();
            is = IOUtils.toInputStream(pageXml, "utf-8");
            page = pageReader.read(is, site);
        } finally {
            IOUtils.closeQuietly(is);
        }

        // Load the composer
        Composer composer = page.getComposer(composerId);
        if (composer == null) {
            logger.warn("Client requested pagelet renderer for non existing composer {} on page {}", composerId,
                    pageURI);
            return null;
        }

        // Get the pagelet
        if (composer.getPagelets().length <= pageletIndex || composer.size() <= pageletIndex) {
            logger.warn("Client requested pagelet renderer for non existing pagelet on page {}", pageURI);
            return null;
        }

        Pagelet pagelet = composer.getPagelet(pageletIndex);
        Module module = site.getModule(pagelet.getModule());
        if (module == null) {
            logger.warn("Client requested pagelet renderer for non existing module {}", pagelet.getModule());
            return null;
        }

        PageletRenderer renderer = module.getRenderer(pagelet.getIdentifier());
        if (renderer == null) {
            logger.warn("Client requested pagelet renderer for non existing renderer on pagelet {}",
                    pagelet.getIdentifier());
            return null;
        }

        // Load the contents of the renderer url
        renderer.setEnvironment(environment);
        URL rendererURL = renderer.getRenderer();
        String rendererContent = null;
        if (rendererURL != null) {
            try {
                rendererContent = loadContents(rendererURL, site, page, composer, pagelet, environment, language);
            } catch (ServletException e) {
                logger.warn("Error processing the pagelet renderer at {}: {}", rendererURL, e.getMessage());
                throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
            }
        }

        return rendererContent;
    }

    /**
     * Asks the site servlet to render the given url using the page, composer and
     * pagelet as the rendering environment. If the no servlet is available for
     * the given site, the contents are loaded from the url directly.
     * 
     * @param rendererURL
     *          the renderer url
     * @param site
     *          the site
     * @param page
     *          the page
     * @param composer
     *          the composer
     * @param pagelet
     *          the pagelet
     * @param environment
     *          the environment
     * @param language
     *          the language
     * @return the servlet response, serialized to a string
     * @throws IOException
     *           if the servlet fails to create the response
     * @throws ServletException
     *           if an exception occurs while processing
     */
    private String loadContents(URL rendererURL, Site site, Page page, Composer composer, Pagelet pagelet,
            Environment environment, String language) throws IOException, ServletException {

        Servlet servlet = siteServlets.get(site.getIdentifier());

        String httpContextURI = UrlUtils.concat("/weblounge-sites", site.getIdentifier());
        int httpContextURILength = httpContextURI.length();
        String url = rendererURL.toExternalForm();
        int uriInPath = url.indexOf(httpContextURI);

        if (uriInPath > 0) {
            String pathInfo = url.substring(uriInPath + httpContextURILength);

            // Prepare the mock request
            MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
            request.setServerName(site.getHostname(environment).getURL().getHost());
            request.setServerPort(site.getHostname(environment).getURL().getPort());
            request.setMethod(site.getHostname(environment).getURL().getProtocol());
            if (language != null)
                request.addPreferredLocale(new Locale(language));
            request.setAttribute(WebloungeRequest.PAGE, page);
            request.setAttribute(WebloungeRequest.COMPOSER, composer);
            request.setAttribute(WebloungeRequest.PAGELET, pagelet);
            request.setPathInfo(pathInfo);
            request.setRequestURI(UrlUtils.concat(httpContextURI, pathInfo));

            MockHttpServletResponse response = new MockHttpServletResponse();
            servlet.service(request, response);
            return response.getContentAsString();
        } else {
            InputStream is = null;
            try {
                is = rendererURL.openStream();
                return IOUtils.toString(is, "utf-8");
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
    }

    /**
     * Adds the site servlet to the list of servlets.
     * 
     * @param id
     *          the site identifier
     * @param servlet
     *          the site servlet
     */
    void addSiteServlet(String id, Servlet servlet) {
        logger.debug("Site servlet attached to {} workbench", id);
        siteServlets.put(id, servlet);
    }

    /**
     * Removes the site servlet from the list of servlets
     * 
     * @param site
     *          the site identifier
     */
    void removeSiteServlet(String id) {
        logger.debug("Site servlet detached from {} workbench", id);
        siteServlets.remove(id);
    }

    /**
     * Implementation of a <code>ServiceTracker</code> that is tracking instances
     * of type {@link Servlet} with an associated <code>site</code> attribute.
     */
    private class SiteServletTracker extends ServiceTracker {

        /**
         * Creates a new servlet tracker that is using the given bundle context to
         * look up service instances.
         * 
         * @param ctx
         *          the bundle context
         * @param filter
         *          the service filter
         */
        SiteServletTracker(BundleContext ctx, Filter filter) {
            super(ctx, filter, null);
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.osgi.util.tracker.ServiceTracker#addingService(org.osgi.framework.ServiceReference)
         */
        @Override
        public Object addingService(ServiceReference reference) {
            Servlet servlet = (Servlet) super.addingService(reference);
            String site = (String) reference.getProperty(Site.class.getName().toLowerCase());
            addSiteServlet(site, servlet);
            return servlet;
        }

        /**
         * {@inheritDoc}
         * 
         * @see org.osgi.util.tracker.ServiceTracker#removedService(org.osgi.framework.ServiceReference,
         *      java.lang.Object)
         */
        @Override
        public void removedService(ServiceReference reference, Object service) {
            String site = (String) reference.getProperty("site");
            removeSiteServlet(site);
        }

    }

}