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 boolean isInitialized = false; 043 private Exception initException; 044 045 046 @Override /* Servlet */ 047 public final synchronized void init(ServletConfig servletConfig) throws ServletException { 048 try { 049 builder = RestContext.create(servletConfig, this.getClass(), null).init(this); 050 super.init(servletConfig); 051 if (! isInitialized) { 052 builder.servletContext(this.getServletContext()); 053 context = builder.build(); 054 isInitialized = true; 055 } 056 context.postInit(); 057 context.postInitChildFirst(); 058 } catch (RestException e) { 059 // Thrown RestExceptions are simply caught and re-thrown on subsequent calls to service(). 060 initException = e; 061 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 062 } catch (ServletException e) { 063 initException = e; 064 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 065 throw e; 066 } catch (Exception e) { 067 initException = e; 068 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 069 throw new ServletException(e); 070 } catch (Throwable e) { 071 initException = new Exception(e); 072 log(SEVERE, e, "Servlet init error on class ''{0}''", getClass().getName()); 073 throw new ServletException(e); 074 } finally { 075 isInitialized = true; 076 } 077 } 078 079 /* 080 * Bypasses the init(ServletConfig) method and just calls the super.init(ServletConfig) method directly. 081 * Used when subclasses of RestServlet are attached as child resources. 082 */ 083 synchronized void innerInit(ServletConfig servletConfig) throws ServletException { 084 super.init(servletConfig); 085 } 086 087 /* 088 * Sets the context object for this servlet. 089 * Used when subclasses of RestServlet are attached as child resources. 090 */ 091 synchronized void setContext(RestContext context) { 092 this.builder = context.builder; 093 this.context = context; 094 } 095 096 @Override /* GenericServlet */ 097 public synchronized RestContextBuilder getServletConfig() { 098 return builder; 099 } 100 101 /** 102 * Returns the read-only context object that contains all the configuration information about this resource. 103 * 104 * <p> 105 * This object is <jk>null</jk> during the call to {@link #init(ServletConfig)} but is populated by the time 106 * {@link #init()} is called. 107 * 108 * <p> 109 * Resource classes that don't extend from {@link RestServlet} can add the following method to their class to get 110 * access to this context object: 111 * <p class='bcode w800'> 112 * <jk>public void</jk> init(RestServletContext context) <jk>throws</jk> Exception; 113 * </p> 114 * 115 * @return The context information on this servlet. 116 */ 117 protected synchronized RestContext getContext() { 118 return context; 119 } 120 121 122 //----------------------------------------------------------------------------------------------------------------- 123 // Other methods 124 //----------------------------------------------------------------------------------------------------------------- 125 126 /** 127 * The main service method. 128 * 129 * <p> 130 * Subclasses can optionally override this method if they want to tailor the behavior of requests. 131 */ 132 @Override /* Servlet */ 133 public void service(HttpServletRequest r1, HttpServletResponse r2) throws ServletException, InternalServerError, IOException { 134 try { 135 if (initException != null) { 136 if (initException instanceof RestException) 137 throw (RestException)initException; 138 throw new InternalServerError(initException); 139 } 140 if (context == null) 141 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()); 142 if (! isInitialized) 143 throw new InternalServerError("Servlet {0} has not been initialized", getClass().getName()); 144 145 context.getCallHandler().service(r1, r2); 146 147 } catch (RestException e) { 148 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 149 } catch (Throwable e) { 150 r2.sendError(SC_INTERNAL_SERVER_ERROR, e.getLocalizedMessage()); 151 } 152 } 153 154 @Override /* GenericServlet */ 155 public void log(String msg) { 156 if (context != null) 157 context.getLogger().log(INFO, msg); 158 } 159 160 @Override /* GenericServlet */ 161 public void log(String msg, Throwable cause) { 162 if (context != null) 163 context.getLogger().log(INFO, cause, msg); 164 } 165 166 /** 167 * Convenience method for calling <code>getContext().getLogger().log(level, msg, args);</code> 168 * 169 * @param level The log level. 170 * @param msg The message to log. 171 * @param args Optional {@link MessageFormat}-style arguments. 172 */ 173 public void log(Level level, String msg, Object...args) { 174 if (context != null) 175 context.getLogger().log(level, msg, args); 176 } 177 178 /** 179 * Convenience method for calling <code>getContext().getLogger().logObjects(level, msg, args);</code> 180 * 181 * @param level The log level. 182 * @param msg The message to log. 183 * @param args Optional {@link MessageFormat}-style arguments. 184 */ 185 public void logObjects(Level level, String msg, Object...args) { 186 if (context != null) 187 context.getLogger().logObjects(level, msg, args); 188 } 189 190 /** 191 * Convenience method for calling <code>getContext().getLogger().log(level, cause, msg, args);</code> 192 * 193 * @param level The log level. 194 * @param cause The cause. 195 * @param msg The message to log. 196 * @param args Optional {@link MessageFormat}-style arguments. 197 */ 198 public void log(Level level, Throwable cause, String msg, Object...args) { 199 if (context != null) 200 context.getLogger().log(level, cause, msg, args); 201 else { 202 // If context failed to initialize, log to the console. 203 System.err.println(format(msg, args)); 204 if (cause != null) 205 cause.printStackTrace(); 206 } 207 } 208 209 @Override /* GenericServlet */ 210 public synchronized void destroy() { 211 if (context != null) 212 context.destroy(); 213 super.destroy(); 214 } 215}