|
16 | 16 |
|
17 | 17 | package com.google.apphosting.utils.servlet.ee10; |
18 | 18 |
|
19 | | -import com.google.appengine.api.taskqueue.DeferredTask; |
20 | | -import com.google.appengine.api.taskqueue.ee10.DeferredTaskContext; |
21 | | -import com.google.apphosting.api.ApiProxy; |
22 | | -import jakarta.servlet.ServletException; |
23 | | -import jakarta.servlet.ServletInputStream; |
24 | | -import jakarta.servlet.http.HttpServlet; |
25 | | -import jakarta.servlet.http.HttpServletRequest; |
26 | | -import jakarta.servlet.http.HttpServletResponse; |
27 | | -import java.io.IOException; |
28 | | -import java.io.ObjectInputStream; |
29 | | -import java.io.ObjectStreamClass; |
30 | | -import java.lang.reflect.Modifier; |
31 | | -import java.lang.reflect.Proxy; |
32 | | -import java.net.HttpURLConnection; |
33 | | -import java.util.Map; |
34 | | - |
35 | 19 | /** |
36 | | - * Implementation of {@link HttpServlet} to dispatch tasks with a {@link DeferredTask} payload; see |
37 | | - * {@link com.google.appengine.api.taskqueue.TaskOptions#payload(DeferredTask)}. |
38 | | - * |
39 | | - * <p>This servlet is mapped to {@link DeferredTaskContext#DEFAULT_DEFERRED_URL} by default. Below |
40 | | - * is a snippet of the web.xml configuration.<br> |
41 | | - * |
42 | | - * <pre> |
43 | | - * <servlet> |
44 | | - * <servlet-name>/_ah/queue/__deferred__</servlet-name> |
45 | | - * <servlet-class |
46 | | - * >com.google.apphosting.utils.servlet.DeferredTaskServlet</servlet-class> |
47 | | - * </servlet> |
48 | | - * |
49 | | - * <servlet-mapping> |
50 | | - * <servlet-name>_ah_queue_deferred</servlet-name> |
51 | | - * <url-pattern>/_ah/queue/__deferred__</url-pattern> |
52 | | - * </servlet-mapping> |
53 | | - * </pre> |
54 | | - * |
| 20 | + * @deprecated as of version 3.0, use generic com.google.apphosting.utils.servlet.jakarta package. |
55 | 21 | */ |
56 | | -public class DeferredTaskServlet extends HttpServlet { |
57 | | - // Keep this in sync with X_APPENGINE_QUEUENAME and |
58 | | - // in google3/apphosting/base/http_proto.cc |
59 | | - static final String X_APPENGINE_QUEUENAME = "X-AppEngine-QueueName"; |
60 | | - |
61 | | - static final String DEFERRED_TASK_SERVLET_KEY = |
62 | | - DeferredTaskContext.class.getName() + ".httpServlet"; |
63 | | - static final String DEFERRED_TASK_REQUEST_KEY = |
64 | | - DeferredTaskContext.class.getName() + ".httpServletRequest"; |
65 | | - static final String DEFERRED_TASK_RESPONSE_KEY = |
66 | | - DeferredTaskContext.class.getName() + ".httpServletResponse"; |
67 | | - static final String DEFERRED_DO_NOT_RETRY_KEY = |
68 | | - DeferredTaskContext.class.getName() + ".doNotRetry"; |
69 | | - static final String DEFERRED_MARK_RETRY_KEY = DeferredTaskContext.class.getName() + ".markRetry"; |
70 | | - |
71 | | - /** Thrown by readRequest when an error occurred during deserialization. */ |
72 | | - protected static class DeferredTaskException extends Exception { |
73 | | - public DeferredTaskException(Exception e) { |
74 | | - super(e); |
75 | | - } |
76 | | - } |
77 | | - |
78 | | - @Override |
79 | | - protected void service(HttpServletRequest req, HttpServletResponse resp) |
80 | | - throws ServletException, IOException { |
81 | | - // See http://b/3479189. All task queue requests have the X-AppEngine-QueueName |
82 | | - // header set. Non admin users cannot set this header so it's a signal that |
83 | | - // this came from task queue or an admin smart enough to set the header. |
84 | | - if (req.getHeader(X_APPENGINE_QUEUENAME) == null) { |
85 | | - resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Not a taskqueue request."); |
86 | | - return; |
87 | | - } |
88 | | - |
89 | | - String method = req.getMethod(); |
90 | | - if (!method.equals("POST")) { |
91 | | - String protocol = req.getProtocol(); |
92 | | - String msg = "DeferredTaskServlet does not support method: " + method; |
93 | | - if (protocol.endsWith("1.1")) { |
94 | | - resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); |
95 | | - } else { |
96 | | - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); |
97 | | - } |
98 | | - return; |
99 | | - } |
100 | | - |
101 | | - // Place the current servlet, request and response in the environment for |
102 | | - // situations where the task may need to get to it. |
103 | | - Map<String, Object> attributes = ApiProxy.getCurrentEnvironment().getAttributes(); |
104 | | - attributes.put(DEFERRED_TASK_SERVLET_KEY, this); |
105 | | - attributes.put(DEFERRED_TASK_REQUEST_KEY, req); |
106 | | - attributes.put(DEFERRED_TASK_RESPONSE_KEY, resp); |
107 | | - attributes.put(DEFERRED_MARK_RETRY_KEY, false); |
108 | | - |
109 | | - try { |
110 | | - performRequest(req, resp); |
111 | | - if ((Boolean) attributes.get(DEFERRED_MARK_RETRY_KEY)) { |
112 | | - resp.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); |
113 | | - } else { |
114 | | - resp.setStatus(HttpURLConnection.HTTP_OK); |
115 | | - } |
116 | | - } catch (DeferredTaskException e) { |
117 | | - resp.setStatus(HttpURLConnection.HTTP_UNSUPPORTED_TYPE); |
118 | | - log("Deferred task failed exception: " + e); |
119 | | - return; |
120 | | - } catch (RuntimeException e) { |
121 | | - Boolean doNotRetry = (Boolean) attributes.get(DEFERRED_DO_NOT_RETRY_KEY); |
122 | | - if (doNotRetry == null || !doNotRetry) { |
123 | | - throw new ServletException(e); |
124 | | - } else if (doNotRetry) { |
125 | | - resp.setStatus(HttpURLConnection.HTTP_NOT_AUTHORITATIVE); // Alternate success code. |
126 | | - log( |
127 | | - DeferredTaskServlet.class.getName() |
128 | | - + " - Deferred task failed but doNotRetry specified. Exception: " |
129 | | - + e); |
130 | | - } |
131 | | - } finally { |
132 | | - // Clean out the attributes. |
133 | | - attributes.remove(DEFERRED_TASK_SERVLET_KEY); |
134 | | - attributes.remove(DEFERRED_TASK_REQUEST_KEY); |
135 | | - attributes.remove(DEFERRED_TASK_RESPONSE_KEY); |
136 | | - attributes.remove(DEFERRED_DO_NOT_RETRY_KEY); |
137 | | - } |
138 | | - } |
139 | | - |
140 | | - /** |
141 | | - * Performs a task enqueued with {@link TaskOptions#payload(DeferredTask)} by deserializing the |
142 | | - * input stream of the {@link HttpServletRequest}. |
143 | | - * |
144 | | - * @param req The HTTP request. |
145 | | - * @param resp The HTTP response. |
146 | | - * @throws DeferredTaskException If an error occurred while deserializing the task. |
147 | | - * <p>Note that other exceptions may be thrown by the {@link DeferredTask#run()} method. |
148 | | - */ |
149 | | - protected void performRequest(HttpServletRequest req, HttpServletResponse resp) |
150 | | - throws DeferredTaskException { |
151 | | - readRequest(req, resp).run(); |
152 | | - } |
153 | | - |
154 | | - /** |
155 | | - * De-serializes the {@link DeferredTask} object from the input stream. |
156 | | - * |
157 | | - * @throws DeferredTaskException With the chained exception being one of the following: |
158 | | - * <li>{@link IllegalArgumentException}: Indicates a content-type header mismatch. |
159 | | - * <li>{@link ClassNotFoundException}: Deserialization failure. |
160 | | - * <li>{@link IOException}: Deserialization failure. |
161 | | - * <li>{@link ClassCastException}: Deserialization failure. |
162 | | - */ |
163 | | - protected Runnable readRequest(HttpServletRequest req, HttpServletResponse resp) |
164 | | - throws DeferredTaskException { |
165 | | - String contentType = req.getHeader("content-type"); |
166 | | - if (contentType == null |
167 | | - || !contentType.equals(DeferredTaskContext.RUNNABLE_TASK_CONTENT_TYPE)) { |
168 | | - throw new DeferredTaskException( |
169 | | - new IllegalArgumentException( |
170 | | - "Invalid content-type header." |
171 | | - + " received: '" |
172 | | - + (String.valueOf(contentType)) |
173 | | - + "' expected: '" |
174 | | - + DeferredTaskContext.RUNNABLE_TASK_CONTENT_TYPE |
175 | | - + "'")); |
176 | | - } |
177 | | - |
178 | | - try { |
179 | | - ServletInputStream stream = req.getInputStream(); |
180 | | - ObjectInputStream objectStream = |
181 | | - new ObjectInputStream(stream) { |
182 | | - @Override |
183 | | - protected Class<?> resolveClass(ObjectStreamClass desc) |
184 | | - throws IOException, ClassNotFoundException { |
185 | | - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
186 | | - String name = desc.getName(); |
187 | | - try { |
188 | | - return Class.forName(name, false, classLoader); |
189 | | - } catch (ClassNotFoundException ex) { |
190 | | - // This one should also handle primitive types |
191 | | - return super.resolveClass(desc); |
192 | | - } |
193 | | - } |
194 | | - |
195 | | - @Override |
196 | | - protected Class<?> resolveProxyClass(String[] interfaces) |
197 | | - throws IOException, ClassNotFoundException { |
198 | | - // Note This logic was copied from ObjectInputStream.java in the |
199 | | - // JDK, and then modified to use the thread context class loader instead of the |
200 | | - // "latest" loader that is used there. |
201 | | - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
202 | | - ClassLoader nonPublicLoader = null; |
203 | | - boolean hasNonPublicInterface = false; |
204 | | - |
205 | | - // define proxy in class loader of non-public interface(s), if any |
206 | | - Class<?>[] classObjs = new Class<?>[interfaces.length]; |
207 | | - for (int i = 0; i < interfaces.length; i++) { |
208 | | - Class<?> cl = Class.forName(interfaces[i], false, classLoader); |
209 | | - if ((cl.getModifiers() & Modifier.PUBLIC) == 0) { |
210 | | - if (hasNonPublicInterface) { |
211 | | - if (nonPublicLoader != cl.getClassLoader()) { |
212 | | - throw new IllegalAccessError( |
213 | | - "conflicting non-public interface class loaders"); |
214 | | - } |
215 | | - } else { |
216 | | - nonPublicLoader = cl.getClassLoader(); |
217 | | - hasNonPublicInterface = true; |
218 | | - } |
219 | | - } |
220 | | - classObjs[i] = cl; |
221 | | - } |
222 | | - try { |
223 | | - return Proxy.getProxyClass( |
224 | | - hasNonPublicInterface ? nonPublicLoader : classLoader, classObjs); |
225 | | - } catch (IllegalArgumentException e) { |
226 | | - throw new ClassNotFoundException(null, e); |
227 | | - } |
228 | | - } |
229 | | - }; |
230 | | - // Replacing DeferredTask to Runnable as we have DeferredTask in the 2 classloaders |
231 | | - // (runtime and application), but we cannot cast one with another one. |
232 | | - return (Runnable) objectStream.readObject(); |
233 | | - } catch (ClassNotFoundException | IOException | ClassCastException e) { |
234 | | - throw new DeferredTaskException(e); |
235 | | - } |
236 | | - } |
237 | | -} |
| 22 | +@Deprecated |
| 23 | +public class DeferredTaskServlet |
| 24 | + extends com.google.apphosting.utils.servlet.jakarta.DeferredTaskServlet {} |
0 commit comments