001 /**
002 * Copyright (c) 2010 Yahoo! Inc. All rights reserved.
003 * Licensed under the Apache License, Version 2.0 (the "License");
004 * you may not use this file except in compliance with the License.
005 * 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
010 * distributed under the License is distributed on an "AS IS" BASIS,
011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 * See the License for the specific language governing permissions and
013 * limitations under the License. See accompanying LICENSE file.
014 */
015 package org.apache.oozie.servlet;
016
017 import java.io.IOException;
018 import java.util.Arrays;
019
020 import javax.servlet.ServletException;
021 import javax.servlet.http.HttpServletRequest;
022 import javax.servlet.http.HttpServletResponse;
023
024 import org.apache.hadoop.conf.Configuration;
025 import org.apache.oozie.BaseEngineException;
026 import org.apache.oozie.ErrorCode;
027 import org.apache.oozie.client.OozieClient;
028 import org.apache.oozie.client.XOozieClient;
029 import org.apache.oozie.client.rest.JsonBean;
030 import org.apache.oozie.client.rest.RestConstants;
031 import org.apache.oozie.service.AuthorizationException;
032 import org.apache.oozie.service.AuthorizationService;
033 import org.apache.oozie.service.Services;
034 import org.apache.oozie.service.XLogService;
035 import org.apache.oozie.util.XConfiguration;
036 import org.apache.oozie.util.XLog;
037 import org.json.simple.JSONObject;
038
039 public abstract class BaseJobServlet extends JsonRestServlet {
040
041 private static final ResourceInfo RESOURCES_INFO[] = new ResourceInfo[1];
042
043 static {
044 RESOURCES_INFO[0] = new ResourceInfo("*", Arrays.asList("PUT", "GET"), Arrays.asList(new ParameterInfo(
045 RestConstants.ACTION_PARAM, String.class, true, Arrays.asList("PUT")), new ParameterInfo(
046 RestConstants.JOB_SHOW_PARAM, String.class, false, Arrays.asList("GET"))));
047 }
048
049 public BaseJobServlet(String instrumentationName) {
050 super(instrumentationName, RESOURCES_INFO);
051 }
052
053 /**
054 * Perform various job related actions - start, suspend, resume, kill, etc.
055 */
056 @Override
057 protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
058 String jobId = getResourceName(request);
059 request.setAttribute(AUDIT_PARAM, jobId);
060 request.setAttribute(AUDIT_OPERATION, request.getParameter(RestConstants.ACTION_PARAM));
061 try {
062 AuthorizationService auth = Services.get().get(AuthorizationService.class);
063 auth.authorizeForJob(getUser(request), jobId, true);
064 }
065 catch (AuthorizationException ex) {
066 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
067 }
068
069 String action = request.getParameter(RestConstants.ACTION_PARAM);
070 if (action.equals(RestConstants.JOB_ACTION_START)) {
071 stopCron();
072 startJob(request, response);
073 startCron();
074 response.setStatus(HttpServletResponse.SC_OK);
075 }
076 else if (action.equals(RestConstants.JOB_ACTION_RESUME)) {
077 stopCron();
078 resumeJob(request, response);
079 startCron();
080 response.setStatus(HttpServletResponse.SC_OK);
081 }
082 else if (action.equals(RestConstants.JOB_ACTION_SUSPEND)) {
083 stopCron();
084 suspendJob(request, response);
085 startCron();
086 response.setStatus(HttpServletResponse.SC_OK);
087 }
088 else if (action.equals(RestConstants.JOB_ACTION_KILL)) {
089 stopCron();
090 killJob(request, response);
091 startCron();
092 response.setStatus(HttpServletResponse.SC_OK);
093 }
094 else if (action.equals(RestConstants.JOB_ACTION_CHANGE)) {
095 stopCron();
096 changeJob(request, response);
097 startCron();
098 response.setStatus(HttpServletResponse.SC_OK);
099 }
100 else if (action.equals(RestConstants.JOB_ACTION_RERUN)) {
101 validateContentType(request, RestConstants.XML_CONTENT_TYPE);
102 Configuration conf = new XConfiguration(request.getInputStream());
103 stopCron();
104 checkAuthorizationForApp(getUser(request), conf);
105 reRunJob(request, response, conf);
106 startCron();
107 response.setStatus(HttpServletResponse.SC_OK);
108 }
109 else if (action.equals(RestConstants.JOB_COORD_ACTION_RERUN)) {
110 validateContentType(request, RestConstants.XML_CONTENT_TYPE);
111 stopCron();
112 JSONObject json = reRunJob(request, response, null);
113 startCron();
114 if (json != null) {
115 sendJsonResponse(response, HttpServletResponse.SC_OK, json);
116 }
117 else {
118 response.setStatus(HttpServletResponse.SC_OK);
119 }
120 }
121 else {
122 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
123 RestConstants.ACTION_PARAM, action);
124 }
125 }
126
127 /**
128 * Validate the configuration user/group. <p/>
129 *
130 * @param requestUser user in request.
131 * @param conf configuration.
132 * @throws XServletException thrown if the configuration does not have a property {@link
133 * org.apache.oozie.client.OozieClient#USER_NAME}.
134 */
135 static void checkAuthorizationForApp(String requestUser, Configuration conf) throws XServletException {
136 String user = conf.get(OozieClient.USER_NAME);
137 String group = conf.get(OozieClient.GROUP_NAME);
138 try {
139 if (user == null) {
140 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0401, OozieClient.USER_NAME);
141 }
142 if (!requestUser.equals(UNDEF) && !user.equals(requestUser)) {
143 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0400, requestUser, user);
144 }
145 AuthorizationService auth = Services.get().get(AuthorizationService.class);
146 if (group == null) {
147 group = auth.getDefaultGroup(user);
148 conf.set(OozieClient.GROUP_NAME, group);
149 }
150 else {
151 auth.authorizeForGroup(user, group);
152 }
153 XLog.Info.get().setParameter(XLogService.GROUP, group);
154 String wfPath = conf.get(OozieClient.APP_PATH);
155 String coordPath = conf.get(OozieClient.COORDINATOR_APP_PATH);
156 if (wfPath == null && coordPath == null) {
157 String libPath = conf.get(XOozieClient.LIBPATH);
158 conf.set(OozieClient.APP_PATH, libPath);
159 wfPath = libPath;
160 }
161 ServletUtilities.ValidateAppPath(wfPath, coordPath);
162
163 if (wfPath != null) {
164 auth.authorizeForApp(user, group, wfPath, "workflow.xml", conf);
165 }
166 else {
167 auth.authorizeForApp(user, group, coordPath, "coordinator.xml", conf);
168 }
169 }
170 catch (AuthorizationException ex) {
171 XLog.getLog(BaseJobServlet.class).info("AuthorizationException ", ex);
172 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
173 }
174 }
175
176 /**
177 * Return information about jobs.
178 */
179 @Override
180 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
181 String jobId = getResourceName(request);
182 String show = request.getParameter(RestConstants.JOB_SHOW_PARAM);
183
184 try {
185 AuthorizationService auth = Services.get().get(AuthorizationService.class);
186 auth.authorizeForJob(getUser(request), jobId, false);
187 }
188 catch (AuthorizationException ex) {
189 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
190 }
191
192 if (show == null || show.equals(RestConstants.JOB_SHOW_INFO)) {
193 stopCron();
194 JsonBean job = null;
195 try {
196 job = getJob(request, response);
197 }
198 catch (BaseEngineException e) {
199 // TODO Auto-generated catch block
200 // e.printStackTrace();
201
202 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, e);
203 }
204 startCron();
205 sendJsonResponse(response, HttpServletResponse.SC_OK, job);
206 }
207 else if (show.equals(RestConstants.JOB_SHOW_LOG)) {
208 response.setContentType(TEXT_UTF8);
209 streamJobLog(request, response);
210 }
211 else if (show.equals(RestConstants.JOB_SHOW_DEFINITION)) {
212 stopCron();
213 response.setContentType(XML_UTF8);
214 String wfDefinition = getJobDefinition(request, response);
215 startCron();
216 response.setStatus(HttpServletResponse.SC_OK);
217 response.getWriter().write(wfDefinition);
218 }
219 else {
220 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
221 RestConstants.JOB_SHOW_PARAM, show);
222 }
223 }
224
225 /**
226 * abstract method to start a job, either workflow or coordinator
227 *
228 * @param request
229 * @param response
230 * @throws XServletException
231 * @throws IOException TODO
232 */
233 abstract void startJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
234 IOException;
235
236 /**
237 * abstract method to resume a job, either workflow or coordinator
238 *
239 * @param request
240 * @param response
241 * @throws XServletException
242 * @throws IOException TODO
243 */
244 abstract void resumeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
245 IOException;
246
247 /**
248 * abstract method to suspend a job, either workflow or coordinator
249 *
250 * @param request
251 * @param response
252 * @throws XServletException
253 * @throws IOException TODO
254 */
255 abstract void suspendJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
256 IOException;
257
258 /**
259 * abstract method to kill a job, either workflow or coordinator
260 *
261 * @param request
262 * @param response
263 * @throws XServletException
264 * @throws IOException TODO
265 */
266 abstract void killJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
267 IOException;
268
269 /**
270 * abstract method to change a coordinator job
271 *
272 * @param request
273 * @param response
274 * @throws XServletException
275 * @throws IOException TODO
276 */
277 abstract void changeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
278 IOException;
279
280 /**
281 * abstract method to re-run a job, either workflow or coordinator
282 *
283 * @param request
284 * @param response
285 * @param conf
286 * @throws XServletException
287 * @throws IOException TODO
288 */
289 abstract JSONObject reRunJob(HttpServletRequest request, HttpServletResponse response, Configuration conf)
290 throws XServletException, IOException;
291
292 /**
293 * abstract method to get a job, either workflow or coordinator, in JsonBean representation
294 *
295 * @param request
296 * @param response
297 * @return JsonBean representation of a job, either workflow or coordinator
298 * @throws XServletException
299 * @throws IOException TODO
300 * @throws BaseEngineException
301 */
302 abstract JsonBean getJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
303 IOException, BaseEngineException;
304
305 /**
306 * abstract method to get definition of a job, either workflow or coordinator
307 *
308 * @param request
309 * @param response
310 * @return job, either workflow or coordinator, definition in string format
311 * @throws XServletException
312 * @throws IOException TODO
313 */
314 abstract String getJobDefinition(HttpServletRequest request, HttpServletResponse response)
315 throws XServletException, IOException;
316
317 /**
318 * abstract method to get and stream log information of job, either workflow or coordinator
319 *
320 * @param request
321 * @param response
322 * @throws XServletException
323 * @throws IOException
324 */
325 abstract void streamJobLog(HttpServletRequest request, HttpServletResponse response) throws XServletException,
326 IOException;
327
328 }