001    /**
002     *
003     *  Licensed to the Apache Software Foundation (ASF) under one or more
004     *  contributor license agreements.  See the NOTICE file distributed with
005     *  this work for additional information regarding copyright ownership.
006     *  The ASF licenses this file to You under the Apache License, Version 2.0
007     *  (the "License"); you may not use this file except in compliance with
008     *  the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing, software
013     *  distributed under the License is distributed on an "AS IS" BASIS,
014     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     *  See the License for the specific language governing permissions and
016     *  limitations under the License.
017     */
018    package org.apache.geronimo.jetty;
019    
020    import java.io.IOException;
021    import java.util.Map;
022    import java.util.Set;
023    
024    import javax.security.auth.Subject;
025    import javax.servlet.ServletException;
026    import javax.servlet.ServletRequest;
027    import javax.servlet.ServletResponse;
028    import javax.servlet.UnavailableException;
029    
030    import org.apache.geronimo.gbean.GBeanInfo;
031    import org.apache.geronimo.gbean.GBeanInfoBuilder;
032    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
033    import org.apache.geronimo.management.Servlet;
034    import org.apache.geronimo.security.Callers;
035    import org.apache.geronimo.security.ContextManager;
036    import org.mortbay.jetty.servlet.ServletHolder;
037    
038    
039    /**
040     * This ServletHolder's sole purpose is to provide the thread's current
041     * ServletHolder for realms that are interested in the current servlet, e.g.
042     * current servlet name.
043     * <p/>
044     * It is also being our servlet gbean for now.  We could gbean-ize the superclass to avoid the thread local access.
045     *
046     * @version $Rev: 470597 $ $Date: 2006-11-02 15:30:55 -0800 (Thu, 02 Nov 2006) $
047     * @see JAASJettyRealm#isUserInRole(java.security.Principal, String)
048     */
049    public class JettyServletHolder extends ServletHolder implements Servlet {
050        private static final ThreadLocal currentServletName = new ThreadLocal();
051        private final Subject runAsSubject;
052        private final String objectName;
053    
054        //todo consider interface instead of this constructor for endpoint use.
055        public JettyServletHolder() {
056            this.objectName = null;
057            this.runAsSubject = null;
058        }
059    
060        public JettyServletHolder(String objectName,
061                String servletName,
062                String servletClassName,
063                String jspFile,
064                Map initParams,
065                Integer loadOnStartup,
066                Set servletMappings,
067                Subject runAsSubject,
068                ServletHolder previous,  //dependency for startup ordering
069                JettyServletRegistration context) throws Exception {
070            super(context == null ? null : context.getServletHandler(), servletName, servletClassName, jspFile);
071            //context will be null only for use as "default servlet info holder" in deployer.
072    
073            if (context != null) {
074                putAll(initParams);
075                if (loadOnStartup != null) {
076                    //This has no effect on the actual start order, the gbean references "previous" control that.
077                    setInitOrder(loadOnStartup.intValue());
078                }
079                //this now starts the servlet in the appropriate context
080                context.registerServletHolder(this, servletName, servletMappings, objectName);
081            }
082            this.runAsSubject = runAsSubject;
083            this.objectName = objectName;
084        }
085    
086        //todo how do we stop/destroy the servlet?
087        //todo is start called twice???
088    
089        public String getServletName() {
090            return getName();
091        }
092    
093        //TODO probably need to override init and destroy (?) to handle runAsSubject since we are not setting it in the superclass any more.
094    
095        /**
096         * Service a request with this servlet.  Set the ThreadLocal to hold the
097         * current JettyServletHolder.
098         */
099        public void handle(ServletRequest request, ServletResponse response)
100                throws ServletException, UnavailableException, IOException {
101    
102            setCurrentServletName(getServletName());
103            if (runAsSubject == null) {
104                super.handle(request, response);
105            } else {
106                Callers oldCallers = ContextManager.pushNextCaller(runAsSubject);
107                try {
108                    super.handle(request, response);
109                } finally {
110                    ContextManager.popCallers(oldCallers);
111                }
112            }
113        }
114    
115        /**
116         * Provide the thread's current JettyServletHolder
117         *
118         * @return the thread's current JettyServletHolder
119         * @see org.apache.geronimo.jetty.JAASJettyRealm#isUserInRole(java.security.Principal, java.lang.String)
120         */
121        static String getCurrentServletName() {
122            return (String) currentServletName.get();
123        }
124    
125        static void setCurrentServletName(String servletName) {
126            currentServletName.set(servletName);
127        }
128    
129        public String getObjectName() {
130            return objectName;
131        }
132    
133        public boolean isStateManageable() {
134            return false;
135        }
136    
137        public boolean isStatisticsProvider() {
138            return false;
139        }
140    
141        public boolean isEventProvider() {
142            return false;
143        }
144    
145        public static final GBeanInfo GBEAN_INFO;
146    
147        static {
148            GBeanInfoBuilder infoBuilder = GBeanInfoBuilder.createStatic(JettyServletHolder.class, NameFactory.SERVLET);
149            //todo replace with interface
150            infoBuilder.addInterface(ServletHolder.class);
151    
152            infoBuilder.addAttribute("servletName", String.class, true);
153            infoBuilder.addAttribute("servletClass", String.class, true);
154            infoBuilder.addAttribute("jspFile", String.class, true);
155            infoBuilder.addAttribute("initParams", Map.class, true);
156            infoBuilder.addAttribute("loadOnStartup", Integer.class, true);
157            infoBuilder.addAttribute("servletMappings", Set.class, true);
158            infoBuilder.addAttribute("runAsSubject", Subject.class, true);
159            infoBuilder.addAttribute("objectName", String.class, false);
160            infoBuilder.addInterface(Servlet.class);
161    
162            infoBuilder.addReference("Previous", ServletHolder.class, NameFactory.SERVLET);
163            infoBuilder.addReference("JettyServletRegistration", JettyServletRegistration.class, NameFactory.WEB_MODULE);
164    
165            infoBuilder.setConstructor(new String[]{"objectName",
166                    "servletName",
167                    "servletClass",
168                    "jspFile",
169                    "initParams",
170                    "loadOnStartup",
171                    "servletMappings",
172                    "runAsSubject",
173                    "Previous",
174                    "JettyServletRegistration"});
175    
176            GBEAN_INFO = infoBuilder.getBeanInfo();
177        }
178    
179        public static GBeanInfo getGBeanInfo() {
180            return GBEAN_INFO;
181        }
182    }