001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Thierry Delprat
016 *     Florent Guillaume
017 */
018
019package org.nuxeo.ecm.platform.ui.web.restAPI;
020
021import java.io.IOException;
022
023import javax.servlet.ServletException;
024import javax.servlet.http.HttpServlet;
025import javax.servlet.http.HttpServletRequest;
026import javax.servlet.http.HttpServletResponse;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.nuxeo.ecm.platform.ui.web.restAPI.service.PluggableRestletService;
031import org.nuxeo.ecm.platform.ui.web.restAPI.service.RestletPluginDescriptor;
032import org.nuxeo.runtime.api.Framework;
033import org.nuxeo.runtime.transaction.TransactionHelper;
034import org.restlet.Filter;
035import org.restlet.Restlet;
036import org.restlet.Route;
037import org.restlet.Router;
038
039import com.noelios.restlet.ext.servlet.ServletConverter;
040
041/**
042 * Servlet used to run a Restlet inside Nuxeo.
043 * <p>
044 * Setup Seam Restlet filter if needed.
045 * <p>
046 * Ensures a transaction is started/committed.
047 */
048public class RestletServlet extends HttpServlet {
049
050    private static final Log log = LogFactory.getLog(RestletServlet.class);
051
052    private static final long serialVersionUID = 1764653653643L;
053
054    protected ServletConverter converter;
055
056    protected PluggableRestletService service;
057
058    @Override
059    public synchronized void init() throws ServletException {
060        super.init();
061
062        if (converter != null) {
063            log.error("RestletServlet initialized several times");
064            return;
065        }
066        converter = new ServletConverter(getServletContext());
067
068        // init the router
069        Router restletRouter = new Router();
070
071        // get the service
072        service = (PluggableRestletService) Framework.getRuntime().getComponent(PluggableRestletService.NAME);
073        if (service == null) {
074            log.error("Unable to get Service " + PluggableRestletService.NAME);
075            throw new ServletException("Can't initialize Nuxeo Pluggable Restlet Service");
076        }
077
078        for (String restletName : service.getContributedRestletNames()) {
079            RestletPluginDescriptor plugin = service.getContributedRestletDescriptor(restletName);
080
081            Restlet restletToAdd;
082            if (plugin.getUseSeam()) {
083                Filter seamFilter = new SeamRestletFilter(plugin.getUseConversation());
084
085                Restlet seamRestlet = service.getContributedRestletByName(restletName);
086
087                seamFilter.setNext(seamRestlet);
088
089                restletToAdd = seamFilter;
090            } else {
091
092                if (plugin.isSingleton()) {
093                    restletToAdd = service.getContributedRestletByName(restletName);
094                } else {
095                    Filter threadSafeRestletFilter = new ThreadSafeRestletFilter();
096
097                    Restlet restlet = service.getContributedRestletByName(restletName);
098
099                    threadSafeRestletFilter.setNext(restlet);
100                    restletToAdd = threadSafeRestletFilter;
101                }
102            }
103
104            // force regexp init
105            for (String urlPattern : plugin.getUrlPatterns()) {
106                log.debug("Pre-compiling restlet pattern " + urlPattern);
107                Route route = restletRouter.attach(urlPattern, restletToAdd);
108                route.getTemplate().match("");
109            }
110        }
111
112        converter.setTarget(restletRouter);
113    }
114
115    @Override
116    protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
117        boolean tx = false;
118        if (!TransactionHelper.isTransactionActive()) {
119            tx = TransactionHelper.startTransaction();
120        }
121        try {
122            converter.service(req, res);
123        } catch (ServletException e) {
124            if (tx) {
125                TransactionHelper.setTransactionRollbackOnly();
126            }
127            throw e;
128        } catch (IOException e) {
129            if (tx) {
130                TransactionHelper.setTransactionRollbackOnly();
131            }
132            throw e;
133        } finally {
134            if (tx) {
135                if (TransactionHelper.isTransactionActiveOrMarkedRollback()) {
136                    // SeamRestletFilter might have done an early commit to
137                    // avoid race condition on the core session on restlets
138                    // who rely upon the conversation lock to fetch it
139                    // thread-safely
140                    TransactionHelper.commitOrRollbackTransaction();
141                }
142            }
143        }
144    }
145
146}