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