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.*; 018import static org.apache.juneau.rest.HttpRuntimeException.*; 019 020import java.io.*; 021import java.text.*; 022import java.util.logging.*; 023 024import javax.servlet.*; 025import javax.servlet.http.*; 026 027import org.apache.juneau.internal.*; 028import org.apache.juneau.reflect.*; 029import org.apache.juneau.rest.annotation.*; 030import org.apache.juneau.http.exception.*; 031 032/** 033 * Servlet implementation of a REST resource. 034 * 035 * <ul class='seealso'> 036 * <li class='link'>{@doc juneau-rest-server.Instantiation.RestServlet} 037 * </ul> 038 */ 039public abstract class RestServlet extends HttpServlet { 040 041 private static final long serialVersionUID = 1L; 042 043 private RestContextBuilder builder; 044 private volatile RestContext context; 045 private volatile Exception initException; 046 private boolean isInitialized = false; // Should not be volatile. 047 private volatile RestResourceResolver resourceResolver; 048 private JuneauLogger logger = JuneauLogger.getLogger(getClass()); 049 050 051 @Override /* Servlet */ 052 public final synchronized void init(ServletConfig servletConfig) throws ServletException { 053 try { 054 if (context != null) 055 return; 056 builder = RestContext.create(servletConfig, this.getClass(), null); 057 if (resourceResolver != null) 058 builder.resourceResolver(resourceResolver); 059 builder.init(this); 060 super.init(servletConfig); 061 builder.servletContext(this.getServletContext()); 062 setContext(builder.build()); 063 context.postInitChildFirst(); 064 } catch (ServletException e) { 065 initException = e; 066 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 067 throw e; 068 } catch (Throwable e) { 069 initException = toHttpException(e, InternalServerError.class); 070 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 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 Sets the context object on this servlet. 086 * @throws ServletException If error occurred during post-initialiation. 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 /** 096 * Sets the resource resolver to use for this servlet and all child servlets. 097 * <p> 098 * This method can be called immediately following object construction, but must be called before {@link #init(ServletConfig)} is called. 099 * Otherwise calling this method will have no effect. 100 * 101 * @param resourceResolver The resolver instance. Can be <jk>null</jk>. 102 * @return This object (for method chaining). 103 */ 104 public synchronized RestServlet setRestResourceResolver(RestResourceResolver resourceResolver) { 105 this.resourceResolver = resourceResolver; 106 return this; 107 } 108 109 /** 110 * Returns the path defined on this servlet if it's defined via {@link Rest#path()}. 111 * 112 * @return The path defined on this servlet, or an empty string if not specified. 113 */ 114 @SuppressWarnings("deprecation") 115 public synchronized String getPath() { 116 if (context != null) 117 return context.getPath(); 118 ClassInfo ci = ClassInfo.of(getClass()); 119 for (Rest rr : ci.getAnnotations(Rest.class)) 120 if (! rr.path().isEmpty()) 121 return trimSlashes(rr.path()); 122 for (RestResource rr : ci.getAnnotations(RestResource.class)) 123 if (! rr.path().isEmpty()) 124 return trimSlashes(rr.path()); 125 return ""; 126 } 127 128 @Override /* GenericServlet */ 129 public synchronized RestContextBuilder getServletConfig() { 130 return builder; 131 } 132 133 //----------------------------------------------------------------------------------------------------------------- 134 // Context methods. 135 //----------------------------------------------------------------------------------------------------------------- 136 137 /** 138 * Returns the read-only context object that contains all the configuration information about this resource. 139 * 140 * <p> 141 * This object is <jk>null</jk> during the call to {@link #init(ServletConfig)} but is populated by the time 142 * {@link #init()} is called. 143 * 144 * <p> 145 * Resource classes that don't extend from {@link RestServlet} can add the following method to their class to get 146 * access to this context object: 147 * <p class='bcode w800'> 148 * <jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception; 149 * </p> 150 * 151 * @return The context information on this servlet. 152 */ 153 protected synchronized RestContext getContext() { 154 if (context == null) 155 throw new InternalServerError("RestContext object not set on resource."); 156 return context; 157 } 158 159 /** 160 * Convenience method for calling <c>getContext().getProperties();</c> 161 * 162 * @return The resource properties as an {@link RestContextProperties}. 163 * @see RestContext#getProperties() 164 */ 165 public RestContextProperties getProperties() { 166 return getContext().getProperties(); 167 } 168 169 170 //----------------------------------------------------------------------------------------------------------------- 171 // Convenience logger methods 172 //----------------------------------------------------------------------------------------------------------------- 173 174 @Override /* GenericServlet */ 175 public void log(String msg) { 176 logger.info(msg); 177 } 178 179 @Override /* GenericServlet */ 180 public void log(String msg, Throwable cause) { 181 logger.info(cause, msg); 182 } 183 184 /** 185 * Log a message. 186 * 187 * @param level The log level. 188 * @param msg The message to log. 189 * @param args Optional {@link MessageFormat}-style arguments. 190 */ 191 public void log(Level level, String msg, Object...args) { 192 logger.log(level, msg, args); 193 } 194 195 /** 196 * Log a message. 197 * 198 * @param level The log level. 199 * @param msg The message to log. 200 * @param args Optional {@link MessageFormat}-style arguments. 201 */ 202 public void logObjects(Level level, String msg, Object...args) { 203 logger.logObjects(level, msg, args); 204 } 205 206 /** 207 * Log a message. 208 * 209 * @param level The log level. 210 * @param cause The cause. 211 * @param msg The message to log. 212 * @param args Optional {@link MessageFormat}-style arguments. 213 */ 214 public void log(Level level, Throwable cause, String msg, Object...args) { 215 logger.log(level, cause, msg, args); 216 } 217 218 //----------------------------------------------------------------------------------------------------------------- 219 // Lifecycle methods 220 //----------------------------------------------------------------------------------------------------------------- 221 222 /** 223 * The main service method. 224 * 225 * <p> 226 * Subclasses can optionally override this method if they want to tailor the behavior of requests. 227 */ 228 @Override /* Servlet */ 229 public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, InternalServerError, IOException { 230 try { 231 // To avoid checking the volatile field context on every call, use the non-volatile isInitialized field as a first-check check. 232 if (! isInitialized) { 233 if (initException != null) 234 throw initException; 235 if (context == null) 236 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()); 237 isInitialized = true; 238 } 239 240 context.getCallHandler().service(r1, r2); 241 242 } catch (Throwable e) { 243 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 244 } 245 } 246 247 @Override /* GenericServlet */ 248 public synchronized void destroy() { 249 if (context != null) 250 context.destroy(); 251 super.destroy(); 252 } 253 254 //----------------------------------------------------------------------------------------------------------------- 255 // Request-time methods. 256 //----------------------------------------------------------------------------------------------------------------- 257 258 /** 259 * Returns the current HTTP request. 260 * 261 * @return The current HTTP request, or <jk>null</jk> if it wasn't created. 262 */ 263 public synchronized RestRequest getRequest() { 264 return getContext().getRequest(); 265 } 266 267 /** 268 * Returns the current HTTP response. 269 * 270 * @return The current HTTP response, or <jk>null</jk> if it wasn't created. 271 */ 272 public synchronized RestResponse getResponse() { 273 return getContext().getResponse(); 274 } 275}