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 026/** 027 * Servlet implementation of a REST resource. 028 * 029 * <h5 class='section'>See Also:</h5> 030 * <ul> 031 * <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-rest-server.RestServlet">Overview > juneau-rest-server > RestServlet</a> 032 * </ul> 033 */ 034public abstract class RestServlet extends HttpServlet { 035 036 private static final long serialVersionUID = 1L; 037 038 private RestContextBuilder builder; 039 private volatile RestContext context; 040 private boolean isInitialized = false; 041 private Exception initException; 042 043 044 @Override /* Servlet */ 045 public final synchronized void init(ServletConfig servletConfig) throws ServletException { 046 try { 047 builder = new RestContextBuilder(servletConfig, this.getClass(), null); 048 builder.init(this); 049 super.init(servletConfig); 050 if (! isInitialized) { 051 builder.servletContext(this.getServletContext()); 052 context = new RestContext(builder); 053 isInitialized = true; 054 } 055 context.postInit(); 056 context.postInitChildFirst(); 057 } catch (RestException e) { 058 // Thrown RestExceptions are simply caught and re-thrown on subsequent calls to service(). 059 initException = e; 060 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 061 } catch (ServletException e) { 062 initException = e; 063 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 064 throw e; 065 } catch (Exception e) { 066 initException = e; 067 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 068 throw new ServletException(e); 069 } catch (Throwable e) { 070 initException = new Exception(e); 071 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 072 throw new ServletException(e); 073 } finally { 074 isInitialized = true; 075 } 076 } 077 078 /* 079 * Bypasses the init(ServletConfig) method and just calls the super.init(ServletConfig) method directly. 080 * Used when subclasses of RestServlet are attached as child resources. 081 */ 082 synchronized void innerInit(ServletConfig servletConfig) throws ServletException { 083 super.init(servletConfig); 084 } 085 086 /* 087 * Sets the context object for this servlet. 088 * Used when subclasses of RestServlet are attached as child resources. 089 */ 090 synchronized void setContext(RestContext context) { 091 this.builder = context.builder; 092 this.context = context; 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'> 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, IOException { 133 try { 134 if (initException != null) { 135 if (initException instanceof RestException) 136 throw (RestException)initException; 137 throw new RestException(SC_INTERNAL_SERVER_ERROR, initException); 138 } 139 if (context == null) 140 throw new RestException(SC_INTERNAL_SERVER_ERROR, "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()); 141 if (! isInitialized) 142 throw new RestException(SC_INTERNAL_SERVER_ERROR, "Servlet {0} has not been initialized", getClass().getName()); 143 144 context.getCallHandler().service(r1, r2); 145 146 } catch (RestException e) { 147 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 148 } catch (Throwable e) { 149 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 150 } 151 } 152 153 @Override /* GenericServlet */ 154 public void log(String msg) { 155 if (context != null) 156 context.getLogger().log(INFO, msg); 157 } 158 159 @Override /* GenericServlet */ 160 public void log(String msg, Throwable cause) { 161 if (context != null) 162 context.getLogger().log(INFO, cause, msg); 163 } 164 165 /** 166 * Convenience method for calling <code>getContext().getLogger().log(level, msg, args);</code> 167 * 168 * @param level The log level. 169 * @param msg The message to log. 170 * @param args Optional {@link MessageFormat}-style arguments. 171 */ 172 public void log(Level level, String msg, Object...args) { 173 if (context != null) 174 context.getLogger().log(level, msg, args); 175 } 176 177 /** 178 * Convenience method for calling <code>getContext().getLogger().logObjects(level, msg, args);</code> 179 * 180 * @param level The log level. 181 * @param msg The message to log. 182 * @param args Optional {@link MessageFormat}-style arguments. 183 */ 184 public void logObjects(Level level, String msg, Object...args) { 185 if (context != null) 186 context.getLogger().logObjects(level, msg, args); 187 } 188 189 /** 190 * Convenience method for calling <code>getContext().getLogger().log(level, cause, msg, args);</code> 191 * 192 * @param level The log level. 193 * @param cause The cause. 194 * @param msg The message to log. 195 * @param args Optional {@link MessageFormat}-style arguments. 196 */ 197 public void log(Level level, Throwable cause, String msg, Object...args) { 198 if (context != null) 199 context.getLogger().log(level, cause, msg, args); 200 else { 201 // If context failed to initialize, log to the console. 202 System.err.println(format(msg, args)); 203 if (cause != null) 204 cause.printStackTrace(); 205 } 206 } 207 208 @Override /* GenericServlet */ 209 public synchronized void destroy() { 210 if (context != null) 211 context.destroy(); 212 super.destroy(); 213 } 214 215 /** 216 * Convenience method for calling <code>getContext().getProperties();</code> 217 * 218 * @return The resource properties as an {@link RestContextProperties}. 219 * @see RestContext#getProperties() 220 */ 221 public RestContextProperties getProperties() { 222 return getContext().getProperties(); 223 } 224}