001/*
002 * (C) Copyright 2006-2010 Nuxeo SAS (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 *     bstefanescu
016 */
017package org.nuxeo.runtime.tomcat.dev;
018
019import java.io.File;
020import java.io.FileInputStream;
021import java.io.IOException;
022import java.io.InputStreamReader;
023import java.io.Reader;
024import java.io.Writer;
025
026import javax.servlet.ServletException;
027
028import org.apache.catalina.connector.Request;
029import org.apache.catalina.connector.Response;
030import org.apache.catalina.valves.ValveBase;
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033
034/**
035 * Enable remote hot deploy and getting configuration from remote Nuxeo SDK servers
036 * <p>
037 * This valve is enabled only in SDK profile (i.e. dev mode). It will intercept any call to '/sdk' under the context
038 * path (i.e. /nuxeo/sdk)
039 *
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042public class DevValve extends ValveBase {
043
044    Log log = LogFactory.getLog(DevValve.class);
045
046    @Override
047    public void invoke(Request req, Response resp) throws IOException, ServletException {
048
049        String path = req.getServletPath();
050        if (path != null && path.startsWith("/sdk/")) {
051            path = path.substring("/sdk/".length());
052            if ("reload".equals(path)) {
053                if ("GET".equals(req.getMethod())) {
054                    getReload(req, resp);
055                } else if ("POST".equals(req.getMethod())) {
056                    postReload(req, resp);
057                }
058                return;
059            } else if (path.startsWith("files/")) {
060                path = path.substring("files/".length());
061                getFile(path, req, resp);
062                return;
063            }
064            resp.setStatus(404);
065            return;
066        }
067        getNext().invoke(req, resp);
068    }
069
070    private final File getHome() {
071        return new File(System.getProperty("catalina.base"));
072    }
073
074    private final File getSDKFile(String path) {
075        return new File(new File(getHome(), "sdk"), path);
076    }
077
078    private void getFile(String path, Request req, Response resp) {
079        File file = getSDKFile(path);
080        if (file == null) {
081            resp.setStatus(404);
082        } else {
083            resp.setContentType("text/plain");
084            resp.setCharacterEncoding("UTF-8");
085            resp.setStatus(200);
086            try {
087                Writer out = resp.getWriter();
088                sendFile(file, out);
089                out.flush();
090            } catch (IOException e) {
091                resp.setStatus(500);
092                log.error("Failed to send file: " + file, e);
093            }
094        }
095    }
096
097    private void sendFile(File file, Writer out) throws IOException {
098        Reader in = new InputStreamReader(new FileInputStream(file), "UTF-8");
099        try {
100            char cbuf[] = new char[64 * 1024];
101            int r = -1;
102            while ((r = in.read(cbuf)) != -1) {
103                if (r > 0) {
104                    out.write(cbuf, 0, r);
105                }
106            }
107        } finally {
108            in.close();
109        }
110    }
111
112    private void getReload(Request req, Response resp) {
113        ClassLoader webLoader = req.getContext().getLoader().getClassLoader();
114        if (webLoader instanceof NuxeoDevWebappClassLoader) {
115            NuxeoDevWebappClassLoader loader = (NuxeoDevWebappClassLoader) webLoader;
116            // only if dev.bundles was modified
117            loader.getBootstrap().loadDevBundles();
118            // log.error("###### reloaded dev bundles");
119        }
120        resp.setStatus(200);
121    }
122
123    private void postReload(Request req, Response resp) {
124        log.error("#### TODO: POST RELOAD");
125        resp.setStatus(200);
126    }
127
128}