001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.servlet; 020 021import java.io.IOException; 022import java.util.Arrays; 023 024import javax.servlet.ServletException; 025import javax.servlet.http.HttpServletRequest; 026import javax.servlet.http.HttpServletResponse; 027 028import org.apache.hadoop.conf.Configuration; 029import org.apache.oozie.BaseEngineException; 030import org.apache.oozie.ErrorCode; 031import org.apache.oozie.client.OozieClient; 032import org.apache.oozie.client.XOozieClient; 033import org.apache.oozie.client.rest.JsonBean; 034import org.apache.oozie.client.rest.JsonTags; 035import org.apache.oozie.client.rest.RestConstants; 036import org.apache.oozie.service.AuthorizationException; 037import org.apache.oozie.service.AuthorizationService; 038import org.apache.oozie.service.Services; 039import org.apache.oozie.service.XLogService; 040import org.apache.oozie.util.ConfigUtils; 041import org.apache.oozie.util.JobUtils; 042import org.apache.oozie.util.XConfiguration; 043import org.apache.oozie.util.XLog; 044import org.json.simple.JSONArray; 045import org.json.simple.JSONObject; 046 047public abstract class BaseJobServlet extends JsonRestServlet { 048 049 private static final ResourceInfo RESOURCES_INFO[] = new ResourceInfo[1]; 050 051 final static String NOT_SUPPORTED_MESSAGE = "Not supported in this version"; 052 053 static { 054 RESOURCES_INFO[0] = new ResourceInfo("*", Arrays.asList("PUT", "GET"), Arrays.asList(new ParameterInfo( 055 RestConstants.ACTION_PARAM, String.class, true, Arrays.asList("PUT")), new ParameterInfo( 056 RestConstants.JOB_SHOW_PARAM, String.class, false, Arrays.asList("GET")), new ParameterInfo( 057 RestConstants.ORDER_PARAM, String.class, false, Arrays.asList("GET")))); 058 } 059 060 public BaseJobServlet(String instrumentationName) { 061 super(instrumentationName, RESOURCES_INFO); 062 } 063 064 /** 065 * Perform various job related actions - start, suspend, resume, kill, etc. 066 */ 067 @Override 068 protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 069 String jobId = getResourceName(request); 070 request.setAttribute(AUDIT_PARAM, jobId); 071 request.setAttribute(AUDIT_OPERATION, request.getParameter(RestConstants.ACTION_PARAM)); 072 try { 073 AuthorizationService auth = Services.get().get(AuthorizationService.class); 074 auth.authorizeForJob(getUser(request), jobId, true); 075 } 076 catch (AuthorizationException ex) { 077 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex); 078 } 079 080 String action = request.getParameter(RestConstants.ACTION_PARAM); 081 if (action.equals(RestConstants.JOB_ACTION_START)) { 082 stopCron(); 083 startJob(request, response); 084 startCron(); 085 response.setStatus(HttpServletResponse.SC_OK); 086 } 087 else if (action.equals(RestConstants.JOB_ACTION_RESUME)) { 088 stopCron(); 089 resumeJob(request, response); 090 startCron(); 091 response.setStatus(HttpServletResponse.SC_OK); 092 } 093 else if (action.equals(RestConstants.JOB_ACTION_SUSPEND)) { 094 stopCron(); 095 suspendJob(request, response); 096 startCron(); 097 response.setStatus(HttpServletResponse.SC_OK); 098 } 099 else if (action.equals(RestConstants.JOB_ACTION_KILL)) { 100 stopCron(); 101 JSONObject json = killJob(request, response); 102 startCron(); 103 if (json != null) { 104 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 105 } 106 else { 107 response.setStatus(HttpServletResponse.SC_OK); 108 } 109 } 110 else if (action.equals(RestConstants.JOB_ACTION_CHANGE)) { 111 stopCron(); 112 changeJob(request, response); 113 startCron(); 114 response.setStatus(HttpServletResponse.SC_OK); 115 } 116 else if (action.equals(RestConstants.JOB_ACTION_IGNORE)) { 117 stopCron(); 118 JSONObject json = ignoreJob(request, response); 119 startCron(); 120 if (json != null) { 121 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 122 } 123 else { 124 response.setStatus(HttpServletResponse.SC_OK); 125 } 126 } 127 else if (action.equals(RestConstants.JOB_ACTION_RERUN)) { 128 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 129 Configuration conf = new XConfiguration(request.getInputStream()); 130 stopCron(); 131 String requestUser = getUser(request); 132 if (!requestUser.equals(UNDEF)) { 133 conf.set(OozieClient.USER_NAME, requestUser); 134 } 135 if (conf.get(OozieClient.APP_PATH) != null) { 136 BaseJobServlet.checkAuthorizationForApp(conf); 137 JobUtils.normalizeAppPath(conf.get(OozieClient.USER_NAME), conf.get(OozieClient.GROUP_NAME), conf); 138 } 139 reRunJob(request, response, conf); 140 startCron(); 141 response.setStatus(HttpServletResponse.SC_OK); 142 } 143 else if (action.equals(RestConstants.JOB_COORD_ACTION_RERUN)) { 144 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 145 stopCron(); 146 JSONObject json = reRunJob(request, response, null); 147 startCron(); 148 if (json != null) { 149 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 150 } 151 else { 152 response.setStatus(HttpServletResponse.SC_OK); 153 } 154 } 155 else if (action.equals(RestConstants.JOB_BUNDLE_ACTION_RERUN)) { 156 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 157 stopCron(); 158 JSONObject json = reRunJob(request, response, null); 159 startCron(); 160 if (json != null) { 161 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 162 } 163 else { 164 response.setStatus(HttpServletResponse.SC_OK); 165 } 166 } 167 else if (action.equals(RestConstants.JOB_COORD_UPDATE)) { 168 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 169 Configuration conf = new XConfiguration(request.getInputStream()); 170 stopCron(); 171 String requestUser = getUser(request); 172 if (!requestUser.equals(UNDEF)) { 173 conf.set(OozieClient.USER_NAME, requestUser); 174 } 175 if (conf.get(OozieClient.COORDINATOR_APP_PATH) != null) { 176 //If coord is submitted from bundle, user may want to update individual coord job with bundle properties 177 //If COORDINATOR_APP_PATH is set, we should check only COORDINATOR_APP_PATH path permission 178 String bundlePath = conf.get(OozieClient.BUNDLE_APP_PATH); 179 if (bundlePath != null) { 180 conf.unset(OozieClient.BUNDLE_APP_PATH); 181 } 182 BaseJobServlet.checkAuthorizationForApp(conf); 183 JobUtils.normalizeAppPath(conf.get(OozieClient.USER_NAME), conf.get(OozieClient.GROUP_NAME), conf); 184 if (bundlePath != null) { 185 conf.set(OozieClient.BUNDLE_APP_PATH, bundlePath); 186 } 187 } 188 JSONObject json = updateJob(request, response, conf); 189 startCron(); 190 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 191 } 192 else if (action.equals(RestConstants.SLA_ENABLE_ALERT)) { 193 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 194 stopCron(); 195 slaEnableAlert(request, response); 196 startCron(); 197 response.setStatus(HttpServletResponse.SC_OK); 198 } 199 else if (action.equals(RestConstants.SLA_DISABLE_ALERT)) { 200 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 201 stopCron(); 202 slaDisableAlert(request, response); 203 startCron(); 204 response.setStatus(HttpServletResponse.SC_OK); 205 } 206 else if (action.equals(RestConstants.SLA_CHANGE)) { 207 validateContentType(request, RestConstants.XML_CONTENT_TYPE); 208 stopCron(); 209 slaChange(request, response); 210 startCron(); 211 response.setStatus(HttpServletResponse.SC_OK); 212 } 213 else { 214 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303, 215 RestConstants.ACTION_PARAM, action); 216 } 217 } 218 219 abstract JSONObject ignoreJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 220 IOException; 221 222 /** 223 * Validate the configuration user/group. <p> 224 * 225 * @param conf configuration. 226 * @throws XServletException thrown if the configuration does not have a property {@link 227 * org.apache.oozie.client.OozieClient#USER_NAME}. 228 */ 229 static void checkAuthorizationForApp(Configuration conf) throws XServletException { 230 String user = conf.get(OozieClient.USER_NAME); 231 String acl = ConfigUtils.getWithDeprecatedCheck(conf, OozieClient.GROUP_NAME, OozieClient.JOB_ACL, null); 232 try { 233 if (user == null) { 234 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0401, OozieClient.USER_NAME); 235 } 236 AuthorizationService auth = Services.get().get(AuthorizationService.class); 237 238 if (acl != null){ 239 conf.set(OozieClient.GROUP_NAME, acl); 240 } 241 else if (acl == null && auth.useDefaultGroupAsAcl()) { 242 acl = auth.getDefaultGroup(user); 243 conf.set(OozieClient.GROUP_NAME, acl); 244 } 245 XLog.Info.get().setParameter(XLogService.GROUP, acl); 246 String wfPath = conf.get(OozieClient.APP_PATH); 247 String coordPath = conf.get(OozieClient.COORDINATOR_APP_PATH); 248 String bundlePath = conf.get(OozieClient.BUNDLE_APP_PATH); 249 250 if (wfPath == null && coordPath == null && bundlePath == null) { 251 String[] libPaths = conf.getStrings(XOozieClient.LIBPATH); 252 if (libPaths != null && libPaths.length > 0 && libPaths[0].trim().length() > 0) { 253 conf.set(OozieClient.APP_PATH, libPaths[0].trim()); 254 wfPath = libPaths[0].trim(); 255 } 256 else { 257 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0405); 258 } 259 } 260 ServletUtilities.ValidateAppPath(wfPath, coordPath, bundlePath); 261 262 if (wfPath != null) { 263 auth.authorizeForApp(user, acl, wfPath, "workflow.xml", conf); 264 } 265 else if (coordPath != null){ 266 auth.authorizeForApp(user, acl, coordPath, "coordinator.xml", conf); 267 } 268 else if (bundlePath != null){ 269 auth.authorizeForApp(user, acl, bundlePath, "bundle.xml", conf); 270 } 271 } 272 catch (AuthorizationException ex) { 273 XLog.getLog(BaseJobServlet.class).info("AuthorizationException ", ex); 274 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex); 275 } 276 } 277 278 /** 279 * Return information about jobs. 280 */ 281 @Override 282 @SuppressWarnings("unchecked") 283 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 284 String jobId = getResourceName(request); 285 String show = request.getParameter(RestConstants.JOB_SHOW_PARAM); 286 String timeZoneId = request.getParameter(RestConstants.TIME_ZONE_PARAM) == null 287 ? "GMT" : request.getParameter(RestConstants.TIME_ZONE_PARAM); 288 289 try { 290 AuthorizationService auth = Services.get().get(AuthorizationService.class); 291 auth.authorizeForJob(getUser(request), jobId, false); 292 } 293 catch (AuthorizationException ex) { 294 throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex); 295 } 296 297 if (show == null || show.equals(RestConstants.JOB_SHOW_INFO)) { 298 stopCron(); 299 JsonBean job = null; 300 try { 301 job = getJob(request, response); 302 } 303 catch (BaseEngineException e) { 304 // TODO Auto-generated catch block 305 // e.printStackTrace(); 306 307 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, e); 308 } 309 startCron(); 310 sendJsonResponse(response, HttpServletResponse.SC_OK, job, timeZoneId); 311 } 312 else if (show.equals(RestConstants.ALL_WORKFLOWS_FOR_COORD_ACTION)) { 313 stopCron(); 314 JSONObject json = getJobsByParentId(request, response); 315 startCron(); 316 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 317 } 318 else if (show.equals(RestConstants.JOB_SHOW_JMS_TOPIC)) { 319 stopCron(); 320 String jmsTopicName = getJMSTopicName(request, response); 321 JSONObject json = new JSONObject(); 322 json.put(JsonTags.JMS_TOPIC_NAME, jmsTopicName); 323 startCron(); 324 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 325 } 326 327 else if (show.equals(RestConstants.JOB_SHOW_LOG)) { 328 response.setContentType(TEXT_UTF8); 329 streamJobLog(request, response); 330 } 331 else if (show.equals(RestConstants.JOB_SHOW_ERROR_LOG)) { 332 response.setContentType(TEXT_UTF8); 333 streamJobErrorLog(request, response); 334 } 335 else if (show.equals(RestConstants.JOB_SHOW_AUDIT_LOG)) { 336 response.setContentType(TEXT_UTF8); 337 streamJobAuditLog(request, response); 338 } 339 340 else if (show.equals(RestConstants.JOB_SHOW_DEFINITION)) { 341 stopCron(); 342 response.setContentType(XML_UTF8); 343 String wfDefinition = getJobDefinition(request, response); 344 startCron(); 345 response.setStatus(HttpServletResponse.SC_OK); 346 response.getWriter().write(wfDefinition); 347 } 348 else if (show.equals(RestConstants.JOB_SHOW_GRAPH)) { 349 stopCron(); 350 streamJobGraph(request, response); 351 startCron(); // -- should happen before you stream anything in response? 352 } else if (show.equals(RestConstants.JOB_SHOW_STATUS)) { 353 stopCron(); 354 String status = getJobStatus(request, response); 355 JSONObject json = new JSONObject(); 356 json.put(JsonTags.STATUS, status); 357 startCron(); 358 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 359 } else if (show.equals(RestConstants.JOB_SHOW_ACTION_RETRIES_PARAM)) { 360 stopCron(); 361 JSONArray retries = getActionRetries(request, response); 362 JSONObject json = new JSONObject(); 363 json.put(JsonTags.WORKFLOW_ACTION_RETRIES, retries); 364 startCron(); 365 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 366 } 367 else if (show.equals(RestConstants.COORD_ACTION_MISSING_DEPENDENCIES)) { 368 stopCron(); 369 JSONObject json = getCoordActionMissingDependencies(request, response); 370 startCron(); 371 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 372 } 373 else if (show.equals(RestConstants.JOB_SHOW_WF_ACTIONS_IN_COORD)) { 374 stopCron(); 375 JSONObject json = getWfActionByJobIdAndName(request, response); 376 startCron(); 377 sendJsonResponse(response, HttpServletResponse.SC_OK, json); 378 } 379 else { 380 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303, 381 RestConstants.JOB_SHOW_PARAM, show); 382 } 383 } 384 385 /** 386 * abstract method to start a job, either workflow or coordinator 387 * 388 * @param request the request 389 * @param response the response 390 * @throws XServletException in case of any servlet error 391 * @throws IOException in case of any I/O error 392 */ 393 abstract void startJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 394 IOException; 395 396 /** 397 * abstract method to resume a job, either workflow or coordinator 398 * 399 * @param request the request 400 * @param response the response 401 * @throws XServletException in case of any servlet error 402 * @throws IOException in case of any I/O error 403 */ 404 abstract void resumeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 405 IOException; 406 407 /** 408 * abstract method to suspend a job, either workflow or coordinator 409 * 410 * @param request the request 411 * @param response the response 412 * @throws XServletException in case of any servlet error 413 * @throws IOException in case of any I/O error 414 */ 415 abstract void suspendJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 416 IOException; 417 418 /** 419 * abstract method to kill a job, either workflow or coordinator 420 * 421 * @param request the request 422 * @param response the response 423 * @throws XServletException in case of any servlet error 424 * @throws IOException in case of any I/O error 425 */ 426 abstract JSONObject killJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 427 IOException; 428 429 /** 430 * abstract method to change a coordinator job 431 * 432 * @param request the request 433 * @param response the response 434 * @throws XServletException in case of any servlet error 435 * @throws IOException in case of any I/O error 436 */ 437 abstract void changeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 438 IOException; 439 440 /** 441 * abstract method to re-run a job, either workflow or coordinator 442 * 443 * @param request the request 444 * @param response the response 445 * @param conf the configuration to use 446 * @throws XServletException in case of any servlet error 447 * @throws IOException in case of any I/O error 448 */ 449 abstract JSONObject reRunJob(HttpServletRequest request, HttpServletResponse response, Configuration conf) 450 throws XServletException, IOException; 451 452 /** 453 * abstract method to get a job, either workflow or coordinator, in JsonBean representation 454 * @param request the request 455 * @param response the response 456 * @return JsonBean representation of a job, either workflow or coordinator 457 * @throws XServletException in case of any servlet error 458 * @throws IOException in case of any I/O error 459 * @throws BaseEngineException thrown if the job could not be retrieved 460 */ 461 abstract JsonBean getJob(HttpServletRequest request, HttpServletResponse response) throws XServletException, 462 IOException, BaseEngineException; 463 464 /** 465 * abstract method to get definition of a job, either workflow or coordinator 466 * 467 * @param request the request 468 * @param response the response 469 * @return job, either workflow or coordinator, definition in string format 470 * @throws XServletException in case of any servlet error 471 * @throws IOException in case of any I/O error 472 */ 473 abstract String getJobDefinition(HttpServletRequest request, HttpServletResponse response) 474 throws XServletException, IOException; 475 476 /** 477 * abstract method to get and stream log information of job, either workflow or coordinator 478 * 479 * @param request the request 480 * @param response the response 481 * @throws XServletException in case of any servlet error 482 * @throws IOException in case of any I/O error 483 */ 484 abstract void streamJobLog(HttpServletRequest request, HttpServletResponse response) throws XServletException, 485 IOException; 486 487 /** 488 * abstract method to get and stream error log information of job, either workflow, coordinator or bundle 489 * 490 * @param request the request 491 * @param response the response 492 * @throws XServletException in case of any servlet error 493 * @throws IOException in case of any I/O error 494 */ 495 abstract void streamJobErrorLog(HttpServletRequest request, HttpServletResponse response) throws XServletException, 496 IOException; 497 498 499 abstract void streamJobAuditLog(HttpServletRequest request, HttpServletResponse response) throws XServletException, 500 IOException; 501 502 /** 503 * abstract method to create and stream image for runtime DAG -- workflow only 504 * 505 * @param request the request 506 * @param response the response 507 * @throws XServletException in case of any servlet error 508 * @throws IOException in case of any I/O error 509 */ 510 abstract void streamJobGraph(HttpServletRequest request, HttpServletResponse response) 511 throws XServletException, IOException; 512 513 /** 514 * abstract method to get JMS topic name for a job 515 * @param request the request 516 * @param response the response 517 * @throws XServletException in case of any servlet error 518 * @throws IOException in case of any I/O error 519 */ 520 abstract String getJMSTopicName(HttpServletRequest request, HttpServletResponse response) 521 throws XServletException, IOException; 522 523 /** 524 * abstract method to get workflow job ids from the parent id 525 * i.e. coordinator action 526 * @param request the request 527 * @param response the response 528 * @return comma-separated list of workflow job ids 529 * @throws XServletException in case of any servlet error 530 * @throws IOException in case of any I/O error 531 */ 532 abstract JSONObject getJobsByParentId(HttpServletRequest request, HttpServletResponse response) 533 throws XServletException, IOException; 534 535 /** 536 * Abstract method to Update coord job. 537 * 538 * @param request the request 539 * @param response the response 540 * @param conf the Configuration 541 * @return the JSON object 542 * @throws XServletException the x servlet exception 543 * @throws IOException Signals that an I/O exception has occurred. 544 */ 545 abstract JSONObject updateJob(HttpServletRequest request, HttpServletResponse response, Configuration conf) 546 throws XServletException, IOException; 547 548 /** 549 * Abstract method to get status for a job 550 * 551 * @param request the request 552 * @param response the response 553 * @return the JSON object 554 * @throws XServletException the x servlet exception 555 * @throws IOException Signals that an I/O exception has occurred. 556 */ 557 abstract String getJobStatus(HttpServletRequest request, HttpServletResponse response) 558 throws XServletException, IOException; 559 560 /** 561 * Abstract method to enable SLA alert. 562 * 563 * @param request the request 564 * @param response the response 565 * @throws XServletException the x servlet exception 566 * @throws IOException Signals that an I/O exception has occurred. 567 */ 568 abstract void slaEnableAlert(HttpServletRequest request, HttpServletResponse response) throws XServletException, 569 IOException; 570 571 /** 572 * Abstract method to disable SLA alert. 573 * 574 * @param request the request 575 * @param response the response 576 * @throws XServletException the x servlet exception 577 * @throws IOException Signals that an I/O exception has occurred. 578 */ 579 abstract void slaDisableAlert(HttpServletRequest request, HttpServletResponse response) throws XServletException, 580 IOException; 581 582 /** 583 * Abstract method to change SLA definition. 584 * 585 * @param request the request 586 * @param response the response 587 * @throws XServletException the x servlet exception 588 * @throws IOException Signals that an I/O exception has occurred. 589 */ 590 abstract void slaChange(HttpServletRequest request, HttpServletResponse response) throws XServletException, 591 IOException; 592 593 /** 594 * Gets the action retries. 595 * 596 * @param request the request 597 * @param response the response 598 * @return the action retries 599 * @throws XServletException the x servlet exception 600 * @throws IOException Signals that an I/O exception has occurred. 601 */ 602 abstract JSONArray getActionRetries(HttpServletRequest request, HttpServletResponse response) 603 throws XServletException, IOException; 604 605 /** 606 * Abstract method to get the coord action missing dependencies. 607 * 608 * @param request the request 609 * @param response the response 610 * @return the coord input dependencies 611 * @throws XServletException the x servlet exception 612 * @throws IOException Signals that an I/O exception has occurred. 613 */ 614 abstract JSONObject getCoordActionMissingDependencies(HttpServletRequest request, HttpServletResponse response) 615 throws XServletException, IOException; 616 617 /** 618 * get wf actions by name in coordinator job 619 * 620 * @param request the request 621 * @param response the response 622 * @return JSONObject the JSON object 623 * @throws XServletException the x servlet exception 624 * @throws IOException Signals that an I/O exception has occurred. 625 */ 626 protected JSONObject getWfActionByJobIdAndName(HttpServletRequest request, HttpServletResponse response) 627 throws XServletException, IOException { 628 throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0302, NOT_SUPPORTED_MESSAGE); 629 } 630}