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