001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.console.servlet;
018    
019    import org.apache.commons.logging.Log;
020    import org.apache.commons.logging.LogFactory;
021    import org.apache.geronimo.console.gbean.ContextForward;
022    import org.apache.geronimo.gbean.AbstractName;
023    import org.apache.geronimo.gbean.AbstractNameQuery;
024    import org.apache.geronimo.kernel.Kernel;
025    import org.apache.geronimo.kernel.KernelRegistry;
026    import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
027    import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
028    
029    import javax.servlet.RequestDispatcher;
030    import javax.servlet.ServletConfig;
031    import javax.servlet.ServletContext;
032    import javax.servlet.ServletException;
033    import javax.servlet.http.HttpServlet;
034    import javax.servlet.http.HttpServletRequest;
035    import javax.servlet.http.HttpServletResponse;
036    import java.io.IOException;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.Map;
040    import java.util.Set;
041    
042    /**
043     * Servlet that forwards GET and POST requests to a servlet in an alternate
044     * context. The servlet path and alternate context are defined in GBeans of
045     * type ContextForward, and this one servlet handles the forwarding for all
046     * those different paths.
047     *
048     * NOTE: This does not work for DWR, because it changes the request path info
049     * while forwarding, and DWR requires the exact initial request info in order
050     * to construct URLs in the data that it returns.  It should work to forward
051     * to most typical servlets, JSPs, and static content.
052     */
053    public class GenericForwardServlet extends HttpServlet {
054        private final static Log log = LogFactory.getLog(GenericForwardServlet.class);
055        private Map forwards = new HashMap(); // Maps a prefix String to ForwardData
056        private Kernel kernel;
057        private LifecycleListener listener;
058    
059        public void init(ServletConfig config) throws ServletException {
060            super.init(config);
061    
062            kernel = KernelRegistry.getSingleKernel();
063            AbstractNameQuery query = new AbstractNameQuery(ContextForward.class.getName());
064            Set set = kernel.listGBeans(query);
065            for (Iterator it = set.iterator(); it.hasNext();) {
066                AbstractName name = (AbstractName) it.next();
067                addGBean(name);
068            }
069            kernel.getLifecycleMonitor().addLifecycleListener(listener = new LifecycleAdapter() {
070                public void running(AbstractName abstractName) {
071                    addGBean(abstractName);
072                }
073    
074                public void stopping(AbstractName abstractName) {
075                    removeGBean(abstractName);
076                }
077    
078                public void stopped(AbstractName abstractName) {
079                    removeGBean(abstractName);
080                }
081    
082                public void failed(AbstractName abstractName) {
083                    removeGBean(abstractName);
084                }
085    
086                public void unloaded(AbstractName abstractName) {
087                    removeGBean(abstractName);
088                }
089            }, query);
090        }
091    
092        public void destroy() {
093            if(listener != null) {
094                kernel.getLifecycleMonitor().removeLifecycleListener(listener);
095                listener = null;
096            }
097        }
098    
099        private void addGBean(AbstractName name) {
100            ContextForward forward = (ContextForward) kernel.getProxyManager().createProxy(name, ContextForward.class);
101            forwards.put(forward.getPortalPathPrefix(), new ForwardData(getServletContext().getContext(forward.getPortletContextPath()),
102                                                                        forward.getPortletServletPath(), name));
103        }
104    
105        private void removeGBean(AbstractName name) {
106            for (Iterator it = forwards.entrySet().iterator(); it.hasNext();) {
107                Map.Entry entry = (Map.Entry) it.next();
108                ForwardData data = (ForwardData) entry.getValue();
109                if(data.getGbean().equals(name)) {
110                    it.remove();
111                    break;
112                }
113            }
114        }
115    
116        public void doGet(HttpServletRequest req, HttpServletResponse resp)
117                throws ServletException, IOException {
118            doPost(req, resp);
119        }
120    
121        public void doPost(HttpServletRequest req, HttpServletResponse resp)
122                throws ServletException, IOException {
123            String path = req.getPathInfo();
124            if(path == null) {
125                log.error("Unable to forward request; no path information provided.  Path is used to identify where to forward to.");
126                return;
127            }
128            ForwardData forward = null;
129            for (Iterator it = forwards.keySet().iterator(); it.hasNext();) {
130                String prefix = (String) it.next();
131                if(path.startsWith(prefix)) {
132                    forward = (ForwardData) forwards.get(prefix);
133                    path = path.substring(prefix.length());
134                }
135            }
136            if(forward == null) {
137                log.error("Unable to forward URL "+path+"; does not match any known ContextForward definitions.");
138                return;
139            }
140            if(!path.equals("") && !path.startsWith("/")) path = "/"+path;
141            String queryString = req.getQueryString();
142            if (queryString != null) {
143                path += "?" + queryString;
144            }
145            path = forward.getServletPath()+path;
146            RequestDispatcher dispatcher = forward.getForwardContext().getRequestDispatcher(path);
147            dispatcher.forward(req, resp);
148        }
149    
150        private static class ForwardData {
151            private ServletContext forwardContext;
152            private String servletPath;
153            private AbstractName gbean;
154    
155            public ForwardData(ServletContext forwardContext, String servletPath, AbstractName gbean) {
156                this.forwardContext = forwardContext;
157                this.servletPath = servletPath;
158                this.gbean = gbean;
159            }
160    
161            public ServletContext getForwardContext() {
162                return forwardContext;
163            }
164    
165            public String getServletPath() {
166                return servletPath;
167            }
168    
169            public AbstractName getGbean() {
170                return gbean;
171            }
172        }
173    }