001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.rest; 014 015import static java.util.logging.Level.*; 016import static javax.servlet.http.HttpServletResponse.*; 017import static org.apache.juneau.internal.StringUtils.*; 018 019import java.io.*; 020import java.text.*; 021import java.util.logging.*; 022 023import javax.servlet.*; 024import javax.servlet.http.*; 025 026import org.apache.juneau.rest.exception.*; 027 028/** 029 * Servlet implementation of a REST resource. 030 * 031 * <h5 class='section'>See Also:</h5> 032 * <ul> 033 * <li class='link'>{@doc juneau-rest-server.Instantiation.RestServlet} 034 * </ul> 035 */ 036public abstract class RestServlet extends HttpServlet { 037 038 private static final long serialVersionUID = 1L; 039 040 private RestContextBuilder builder; 041 private volatile RestContext context; 042 private volatile Exception initException; 043 private boolean isInitialized = false; // Should not be volatile. 044 045 @Override /* Servlet */ 046 public final synchronized void init(ServletConfig servletConfig) throws ServletException { 047 try { 048 if (context != null) 049 return; 050 builder = RestContext.create(servletConfig, this.getClass(), null).init(this); 051 super.init(servletConfig); 052 builder.servletContext(this.getServletContext()); 053 setContext(builder.build()); 054 context.postInitChildFirst(); 055 } catch (RestException e) { 056 // Thrown RestExceptions are simply caught and re-thrown on subsequent calls to service(). 057 initException = e; 058 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 059 } catch (ServletException e) { 060 initException = e; 061 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 062 throw e; 063 } catch (Exception e) { 064 initException = e; 065 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 066 throw new ServletException(e); 067 } catch (Throwable e) { 068 initException = new Exception(e); 069 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 070 throw new ServletException(e); 071 } 072 } 073 074 /* 075 * Bypasses the init(ServletConfig) method and just calls the super.init(ServletConfig) method directly. 076 * Used when subclasses of RestServlet are attached as child resources. 077 */ 078 synchronized void innerInit(ServletConfig servletConfig) throws ServletException { 079 super.init(servletConfig); 080 } 081 082 /** 083 * Sets the context object for this servlet. 084 * 085 * @param context 086 * @throws ServletException 087 */ 088 public synchronized void setContext(RestContext context) throws ServletException { 089 this.builder = context.builder; 090 this.context = context; 091 isInitialized = true; 092 context.postInit(); 093 } 094 095 @Override /* GenericServlet */ 096 public synchronized RestContextBuilder getServletConfig() { 097 return builder; 098 } 099 100 /** 101 * Returns the read-only context object that contains all the configuration information about this resource. 102 * 103 * <p> 104 * This object is <jk>null</jk> during the call to {@link #init(ServletConfig)} but is populated by the time 105 * {@link #init()} is called. 106 * 107 * <p> 108 * Resource classes that don't extend from {@link RestServlet} can add the following method to their class to get 109 * access to this context object: 110 * <p class='bcode w800'> 111 * <jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception; 112 * </p> 113 * 114 * @return The context information on this servlet. 115 */ 116 protected synchronized RestContext getContext() { 117 return context; 118 } 119 120 121 //----------------------------------------------------------------------------------------------------------------- 122 // Other methods 123 //----------------------------------------------------------------------------------------------------------------- 124 125 /** 126 * The main service method. 127 * 128 * <p> 129 * Subclasses can optionally override this method if they want to tailor the behavior of requests. 130 */ 131 @Override /* Servlet */ 132 public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, InternalServerError, IOException { 133 try { 134 // To avoid checking the volatile field context on every call, use the non-volatile isInitialized field as a first-check check. 135 if (! isInitialized) { 136 if (initException != null) { 137 if (initException instanceof RestException) 138 throw (RestException)initException; 139 throw new InternalServerError(initException); 140 } 141 if (context == null) 142 throw new InternalServerError("Servlet {0} not initialized. init(ServletConfig) was not called. This can occur if you've overridden this method but didn't call super.init(RestConfig).", getClass().getName()); 143 isInitialized = true; 144 } 145 146 context.getCallHandler().service(r1, r2); 147 148 } catch (RestException e) { 149 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 150 } catch (Throwable e) { 151 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 152 } 153 } 154 155 @Override /* GenericServlet */ 156 public void log(String msg) { 157 if (context != null) 158 context.getLogger().log(INFO, msg); 159 } 160 161 @Override /* GenericServlet */ 162 public void log(String msg, Throwable cause) { 163 if (context != null) 164 context.getLogger().log(INFO, cause, msg); 165 } 166 167 /** 168 * Convenience method for calling <code>getContext().getLogger().log(level, msg, args);</code> 169 * 170 * @param level The log level. 171 * @param msg The message to log. 172 * @param args Optional {@link MessageFormat}-style arguments. 173 */ 174 public void log(Level level, String msg, Object...args) { 175 if (context != null) 176 context.getLogger().log(level, msg, args); 177 } 178 179 /** 180 * Convenience method for calling <code>getContext().getLogger().logObjects(level, msg, args);</code> 181 * 182 * @param level The log level. 183 * @param msg The message to log. 184 * @param args Optional {@link MessageFormat}-style arguments. 185 */ 186 public void logObjects(Level level, String msg, Object...args) { 187 if (context != null) 188 context.getLogger().logObjects(level, msg, args); 189 } 190 191 /** 192 * Convenience method for calling <code>getContext().getLogger().log(level, cause, msg, args);</code> 193 * 194 * @param level The log level. 195 * @param cause The cause. 196 * @param msg The message to log. 197 * @param args Optional {@link MessageFormat}-style arguments. 198 */ 199 public void log(Level level, Throwable cause, String msg, Object...args) { 200 if (context != null) 201 context.getLogger().log(level, cause, msg, args); 202 else { 203 // If context failed to initialize, log to the console. 204 System.err.println(format(msg, args)); 205 if (cause != null) 206 cause.printStackTrace(); 207 } 208 } 209 210 /** 211 * Returns the current HTTP request. 212 * 213 * @return The current HTTP request, or <jk>null</jk> if it wasn't created. 214 */ 215 public RestRequest getRequest() { 216 if (context == null) 217 return null; 218 return context.getRequest(); 219 } 220 221 /** 222 * Returns the current HTTP response. 223 * 224 * @return The current HTTP response, or <jk>null</jk> if it wasn't created. 225 */ 226 public RestResponse getResponse() { 227 if (context == null) 228 return null; 229 return context.getResponse(); 230 } 231 232 @Override /* GenericServlet */ 233 public synchronized void destroy() { 234 if (context != null) 235 context.destroy(); 236 super.destroy(); 237 } 238 239 /** 240 * Convenience method for calling <code>getContext().getProperties();</code> 241 * 242 * @return The resource properties as an {@link RestContextProperties}. 243 * @see RestContext#getProperties() 244 */ 245 public RestContextProperties getProperties() { 246 return getContext().getProperties(); 247 } 248}