001    /*
002     * ============================================================================
003     * GNU Lesser General Public License
004     * ============================================================================
005     *
006     * Beanlet - JSE Application Container.
007     * Copyright (C) 2006  Leon van Zantvoort
008     *
009     * This library is free software; you can redistribute it and/or
010     * modify it under the terms of the GNU Lesser General Public
011     * License as published by the Free Software Foundation; either
012     * version 2.1 of the License, or (at your option) any later version.
013     *
014     * This library is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this library; if not, write to the Free Software
021     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
022     *
023     * Leon van Zantvoort
024     * 243 Acalanes Drive #11
025     * Sunnyvale, CA 94086
026     * USA
027     *
028     * zantvoort@users.sourceforge.net
029     * http://beanlet.org
030     */
031    package org.beanlet.rest;
032    
033    import java.io.BufferedReader;
034    import java.io.IOException;
035    import java.io.InputStreamReader;
036    import java.lang.reflect.Constructor;
037    import java.lang.reflect.Modifier;
038    import java.net.URL;
039    import java.security.AccessController;
040    import java.security.PrivilegedActionException;
041    import java.security.PrivilegedExceptionAction;
042    import java.util.Enumeration;
043    import javax.servlet.*;
044    import javax.servlet.http.HttpServlet;
045    import org.beanlet.BeanletApplicationException;
046    
047    /**
048     * Add the following configuration to the web application's {@code web.xml} file
049     * to support restlets.
050     * </br>
051     * Not required for Servlet 3.0 containers.
052     * </br>
053     * <pre>
054     * &lt;web-app&gt;
055     *   ...
056     *   &lt;listener&gt;
057     *     &lt;listener-class&gt;org.beanlet.rest.RequestContextListener&lt;listener-class&gt;
058     *   &lt;/listener&gt;
059     *   ...
060     * &lt;/web-app&gt;
061     * </pre>
062     *
063     * Restlets are deployed automatically by defining a RestFilter or RestServlet in the web.xml:
064     *
065     * <pre>
066     * &lt;servlet&gt;
067     *   &lt;servlet-name&gt;Beanlet Rest Servlet&lt;/servlet-name&gt;
068     *   &lt;servlet-class&gt;org.beanlet.rest.RestServlet&lt;/servlet-class&gt;
069     *   &lt;init-param&gt;
070     *     &lt;param-name&gt;om.sun.jersey.config.property.packages&lt;/param-name&gt;
071     *     &lt;param-value&gt;com.acme&lt;/param-value&gt;
072     *   &lt;/init-param&gt;
073     *   &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
074     * &lt;/servlet&gt;
075     *
076     * &lt;servlet-mapping&gt;
077     *   &lt;servlet-name&gt;Beanlet Rest Servlet&lt;/servlet-name&gt;
078     *   &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
079     * &lt;/servlet-mapping&gt;  
080     * </pre>
081     *
082     * @author Leon van Zantvoort
083     */
084    public class RestServlet extends HttpServlet {
085    
086        private static class LazyHolder {
087            static final Constructor<HttpServlet> delegate;
088            static {
089                try {
090                    try {
091                        Constructor constructor = AccessController.doPrivileged(
092                                new PrivilegedExceptionAction<Constructor>() {
093                                    public Constructor run() throws Exception {
094                                        String path = "META-INF/services/" +
095                                                RestServlet.class.getName();
096    
097                                        // PERMISSION: java.lang.RuntimePermission getClassLoader
098                                        ClassLoader loader = Thread.currentThread().
099                                                getContextClassLoader();
100                                        final Enumeration<URL> urls;
101                                        if (loader == null) {
102                                            urls = RequestContextListener.class.
103                                                    getClassLoader().getResources(path);
104                                        } else {
105                                            urls = loader.getResources(path);
106                                        }
107                                        while (urls.hasMoreElements()) {
108                                            URL url = urls.nextElement();
109                                            BufferedReader reader = new BufferedReader(new InputStreamReader(
110                                                    url.openStream()));
111                                            try {
112                                                String className = null;
113                                                while ((className = reader.readLine()) != null) {
114                                                    final String name = className.trim();
115                                                    if (!name.startsWith("#") && !name.startsWith(";") &&
116                                                            !name.startsWith("//")) {
117                                                        final Class<?> cls;
118                                                        if (loader == null) {
119                                                            cls = Class.forName(name);
120                                                        } else {
121                                                            cls = Class.forName(name, true, loader);
122                                                        }
123                                                        int m = cls.getModifiers();
124                                                        if (HttpServlet.class.isAssignableFrom(cls) &&
125                                                                !Modifier.isAbstract(m) &&
126                                                                !Modifier.isInterface(m)) {
127                                                            // PERMISSION: java.lang.RuntimePermission accessDeclaredMembers
128                                                            Constructor constructor = cls.getDeclaredConstructor();
129                                                            // PERMISSION: java.lang.reflect.ReflectPermission suppressAccessChecks
130                                                            if (!Modifier.isPublic(constructor.getModifiers())) {
131                                                                constructor.setAccessible(true);
132                                                            }
133                                                            return constructor;
134                                                        } else {
135                                                            throw new ClassCastException(cls.getName());
136                                                        }
137                                                    }
138                                                }
139                                            } finally {
140                                                reader.close();
141                                            }
142                                        }
143                                        throw new BeanletApplicationException("No " +
144                                                "RestServlet implementation " +
145                                                "found.");
146                                    }
147                                });
148                        @SuppressWarnings("unchecked")
149                        Constructor<HttpServlet> tmp =
150                                (Constructor<HttpServlet>) constructor;
151                        delegate = tmp;
152                    } catch (PrivilegedActionException e) {
153                        throw e.getException();
154                    }
155                } catch (BeanletApplicationException e) {
156                    throw e;
157                } catch (RuntimeException e) {
158                    throw new BeanletApplicationException(e);
159                } catch (Error e) {
160                    throw e;
161                } catch (Throwable t) {
162                    throw new BeanletApplicationException(t);
163                }
164            }
165        }
166    
167        private final HttpServlet delegate;
168    
169        public RestServlet() {
170            try {
171                try {
172                    delegate = LazyHolder.delegate.newInstance();
173                } catch (ExceptionInInitializerError e) {
174                    try {
175                        throw e.getException();
176                    } catch (Throwable t) {
177                        throw new BeanletApplicationException(t);
178                    }
179                }
180            } catch (BeanletApplicationException e) {
181                throw e;
182            } catch (RuntimeException e) {
183                throw e;
184            } catch (Error e) {
185                throw e;
186            } catch (Exception e) {
187                throw new BeanletApplicationException(e);
188            }
189        }
190    
191        @Override
192        public String getServletName() {
193            return delegate.getServletName();    
194        }
195    
196        @Override
197        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
198            delegate.service(servletRequest, servletResponse);    
199        }
200    
201        @Override
202        public void destroy() {
203            delegate.destroy();    
204        }
205    
206        @Override
207        public String getInitParameter(String s) {
208            return delegate.getInitParameter(s);    
209        }
210    
211        @Override
212        public Enumeration<String> getInitParameterNames() {
213            return delegate.getInitParameterNames();    
214        }
215    
216        @Override
217        public ServletConfig getServletConfig() {
218            return delegate.getServletConfig();    
219        }
220    
221        @Override
222        public ServletContext getServletContext() {
223            return delegate.getServletContext();    
224        }
225    
226        @Override
227        public String getServletInfo() {
228            return delegate.getServletInfo();    
229        }
230    
231        @Override
232        public void init(ServletConfig servletConfig) throws ServletException {
233            delegate.init(servletConfig);    
234        }
235    
236        @Override
237        public void init() throws ServletException {
238            delegate.init();    
239        }
240    
241        @Override
242        public void log(String s) {
243            delegate.log(s);    
244        }
245    
246        @Override
247        public void log(String s, Throwable throwable) {
248            delegate.log(s, throwable);    
249        }
250    }