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 }