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.store; 016 017 import java.sql.Connection; 018 import java.sql.SQLException; 019 import java.sql.Timestamp; 020 import java.util.ArrayList; 021 import java.util.Date; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.concurrent.Callable; 025 026 import javax.persistence.EntityManager; 027 import javax.persistence.Query; 028 029 import org.apache.oozie.ErrorCode; 030 import org.apache.oozie.WorkflowActionBean; 031 import org.apache.oozie.WorkflowJobBean; 032 import org.apache.oozie.WorkflowsInfo; 033 import org.apache.oozie.client.OozieClient; 034 import org.apache.oozie.client.WorkflowJob.Status; 035 import org.apache.oozie.service.InstrumentationService; 036 import org.apache.oozie.service.SchemaService; 037 import org.apache.oozie.service.Services; 038 import org.apache.oozie.service.SchemaService.SchemaName; 039 import org.apache.oozie.util.Instrumentation; 040 import org.apache.oozie.util.ParamChecker; 041 import org.apache.oozie.util.XLog; 042 import org.apache.oozie.workflow.WorkflowException; 043 import org.apache.openjpa.persistence.OpenJPAEntityManager; 044 import org.apache.openjpa.persistence.OpenJPAPersistence; 045 import org.apache.openjpa.persistence.OpenJPAQuery; 046 import org.apache.openjpa.persistence.jdbc.FetchDirection; 047 import org.apache.openjpa.persistence.jdbc.JDBCFetchPlan; 048 import org.apache.openjpa.persistence.jdbc.LRSSizeAlgorithm; 049 import org.apache.openjpa.persistence.jdbc.ResultSetType; 050 051 /** 052 * DB Implementation of Workflow Store 053 */ 054 public class WorkflowStore extends Store { 055 private Connection conn; 056 private EntityManager entityManager; 057 private boolean selectForUpdate; 058 private static final String INSTR_GROUP = "db"; 059 public static final int LOCK_TIMEOUT = 50000; 060 private static final String seletStr = "Select w.id, w.appName, w.status, w.run, w.user, w.group, w.createdTimestamp, " 061 + "w.startTimestamp, w.lastModifiedTimestamp, w.endTimestamp from WorkflowJobBean w"; 062 private static final String countStr = "Select count(w) from WorkflowJobBean w"; 063 064 public WorkflowStore() { 065 } 066 067 public WorkflowStore(Connection connection, boolean selectForUpdate) throws StoreException { 068 super(); 069 conn = ParamChecker.notNull(connection, "conn"); 070 entityManager = getEntityManager(); 071 this.selectForUpdate = selectForUpdate; 072 } 073 074 public WorkflowStore(Connection connection, Store store, boolean selectForUpdate) throws StoreException { 075 super(store); 076 conn = ParamChecker.notNull(connection, "conn"); 077 entityManager = getEntityManager(); 078 this.selectForUpdate = selectForUpdate; 079 } 080 081 public WorkflowStore(boolean selectForUpdate) throws StoreException { 082 super(); 083 entityManager = getEntityManager(); 084 javax.xml.validation.Schema schema = Services.get().get(SchemaService.class).getSchema(SchemaName.WORKFLOW); 085 OpenJPAEntityManager kem = OpenJPAPersistence.cast(entityManager); 086 conn = (Connection) kem.getConnection(); 087 this.selectForUpdate = selectForUpdate; 088 } 089 090 public WorkflowStore(Store store, boolean selectForUpdate) throws StoreException { 091 super(store); 092 entityManager = getEntityManager(); 093 this.selectForUpdate = selectForUpdate; 094 } 095 096 /** 097 * Create a Workflow and return a WorkflowJobBean. It also creates the process instance for the job. 098 * 099 * @param workflow workflow bean 100 * @throws StoreException 101 */ 102 103 public void insertWorkflow(final WorkflowJobBean workflow) throws StoreException { 104 ParamChecker.notNull(workflow, "workflow"); 105 106 doOperation("insertWorkflow", new Callable<Void>() { 107 public Void call() throws SQLException, StoreException, WorkflowException { 108 entityManager.persist(workflow); 109 return null; 110 } 111 }); 112 } 113 114 /** 115 * Load the Workflow into a Bean and return it. Also load the Workflow Instance into the bean. And lock the Workflow 116 * depending on the locking parameter. 117 * 118 * @param id Workflow ID 119 * @param locking true if Workflow is to be locked 120 * @return WorkflowJobBean 121 * @throws StoreException 122 */ 123 public WorkflowJobBean getWorkflow(final String id, final boolean locking) throws StoreException { 124 ParamChecker.notEmpty(id, "WorkflowID"); 125 WorkflowJobBean wfBean = doOperation("getWorkflow", new Callable<WorkflowJobBean>() { 126 public WorkflowJobBean call() throws SQLException, StoreException, WorkflowException, InterruptedException { 127 WorkflowJobBean wfBean = null; 128 wfBean = getWorkflowOnly(id, locking); 129 if (wfBean == null) { 130 throw new StoreException(ErrorCode.E0604, id); 131 } 132 /* 133 * WorkflowInstance wfInstance; //krishna and next line 134 * wfInstance = workflowLib.get(id); wfInstance = 135 * wfBean.get(wfBean.getWfInstance()); 136 * wfBean.setWorkflowInstance(wfInstance); 137 * wfBean.setWfInstance(wfInstance); 138 */ 139 return wfBean; 140 } 141 }); 142 return wfBean; 143 } 144 145 /** 146 * Get the number of Workflows with the given status. 147 * 148 * @param status Workflow Status. 149 * @return number of Workflows with given status. 150 * @throws StoreException 151 */ 152 public int getWorkflowCountWithStatus(final String status) throws StoreException { 153 ParamChecker.notEmpty(status, "status"); 154 Integer cnt = doOperation("getWorkflowCountWithStatus", new Callable<Integer>() { 155 public Integer call() throws SQLException { 156 Query q = entityManager.createNamedQuery("GET_WORKFLOWS_COUNT_WITH_STATUS"); 157 q.setParameter("status", status); 158 Long count = (Long) q.getSingleResult(); 159 return Integer.valueOf(count.intValue()); 160 } 161 }); 162 return cnt.intValue(); 163 } 164 165 /** 166 * Get the number of Workflows with the given status which was modified in given time limit. 167 * 168 * @param status Workflow Status. 169 * @param secs No. of seconds within which the workflow got modified. 170 * @return number of Workflows modified within given time with given status. 171 * @throws StoreException 172 */ 173 public int getWorkflowCountWithStatusInLastNSeconds(final String status, final int secs) throws StoreException { 174 ParamChecker.notEmpty(status, "status"); 175 ParamChecker.notEmpty(status, "secs"); 176 Integer cnt = doOperation("getWorkflowCountWithStatusInLastNSecs", new Callable<Integer>() { 177 public Integer call() throws SQLException { 178 Query q = entityManager.createNamedQuery("GET_WORKFLOWS_COUNT_WITH_STATUS_IN_LAST_N_SECS"); 179 Timestamp ts = new Timestamp(System.currentTimeMillis() - (secs * 1000)); 180 q.setParameter("status", status); 181 q.setParameter("lastModTime", ts); 182 Long count = (Long) q.getSingleResult(); 183 return Integer.valueOf(count.intValue()); 184 } 185 }); 186 return cnt.intValue(); 187 } 188 189 /** 190 * Update the data from Workflow Bean to DB along with the workflow instance data. Action table is not updated 191 * 192 * @param wfBean Workflow Bean 193 * @throws StoreException If Workflow doesn't exist 194 */ 195 public void updateWorkflow(final WorkflowJobBean wfBean) throws StoreException { 196 ParamChecker.notNull(wfBean, "WorkflowJobBean"); 197 doOperation("updateWorkflow", new Callable<Void>() { 198 public Void call() throws SQLException, StoreException, WorkflowException { 199 Query q = entityManager.createNamedQuery("UPDATE_WORKFLOW"); 200 q.setParameter("id", wfBean.getId()); 201 setWFQueryParameters(wfBean, q); 202 q.executeUpdate(); 203 return null; 204 } 205 }); 206 } 207 208 /** 209 * Create a new Action record in the ACTIONS table with the given Bean. 210 * 211 * @param action WorkflowActionBean 212 * @throws StoreException If the action is already present 213 */ 214 public void insertAction(final WorkflowActionBean action) throws StoreException { 215 ParamChecker.notNull(action, "WorkflowActionBean"); 216 doOperation("insertAction", new Callable<Void>() { 217 public Void call() throws SQLException, StoreException, WorkflowException { 218 entityManager.persist(action); 219 return null; 220 } 221 }); 222 } 223 224 /** 225 * Load the action data and returns a bean. 226 * 227 * @param id Action Id 228 * @param locking true if the action is to be locked 229 * @return Action Bean 230 * @throws StoreException If action doesn't exist 231 */ 232 public WorkflowActionBean getAction(final String id, final boolean locking) throws StoreException { 233 ParamChecker.notEmpty(id, "ActionID"); 234 WorkflowActionBean action = doOperation("getAction", new Callable<WorkflowActionBean>() { 235 public WorkflowActionBean call() throws SQLException, StoreException, WorkflowException, 236 InterruptedException { 237 Query q = entityManager.createNamedQuery("GET_ACTION"); 238 /* 239 * if (locking) { OpenJPAQuery oq = OpenJPAPersistence.cast(q); 240 * FetchPlan fetch = oq.getFetchPlan(); 241 * fetch.setReadLockMode(LockModeType.WRITE); 242 * fetch.setLockTimeout(1000); // 1 seconds } 243 */ 244 WorkflowActionBean action = null; 245 q.setParameter("id", id); 246 List<WorkflowActionBean> actions = q.getResultList(); 247 // action = (WorkflowActionBean) q.getSingleResult(); 248 if (actions.size() > 0) { 249 action = actions.get(0); 250 } 251 else { 252 throw new StoreException(ErrorCode.E0605, id); 253 } 254 255 /* 256 * if (locking) return action; else 257 */ 258 // return action; 259 return getBeanForRunningAction(action); 260 } 261 }); 262 return action; 263 } 264 265 /** 266 * Update the given action bean to DB. 267 * 268 * @param action Action Bean 269 * @throws StoreException if action doesn't exist 270 */ 271 public void updateAction(final WorkflowActionBean action) throws StoreException { 272 ParamChecker.notNull(action, "WorkflowActionBean"); 273 doOperation("updateAction", new Callable<Void>() { 274 public Void call() throws SQLException, StoreException, WorkflowException { 275 Query q = entityManager.createNamedQuery("UPDATE_ACTION"); 276 q.setParameter("id", action.getId()); 277 setActionQueryParameters(action, q); 278 q.executeUpdate(); 279 return null; 280 } 281 }); 282 } 283 284 /** 285 * Delete the Action with given id. 286 * 287 * @param id Action ID 288 * @throws StoreException if Action doesn't exist 289 */ 290 public void deleteAction(final String id) throws StoreException { 291 ParamChecker.notEmpty(id, "ActionID"); 292 doOperation("deleteAction", new Callable<Void>() { 293 public Void call() throws SQLException, StoreException, WorkflowException { 294 /* 295 * Query q = entityManager.createNamedQuery("DELETE_ACTION"); 296 * q.setParameter("id", id); q.executeUpdate(); 297 */ 298 WorkflowActionBean action = entityManager.find(WorkflowActionBean.class, id); 299 if (action != null) { 300 entityManager.remove(action); 301 } 302 return null; 303 } 304 }); 305 } 306 307 /** 308 * Loads all the actions for the given Workflow. Also locks all the actions if locking is true. 309 * 310 * @param wfId Workflow ID 311 * @param locking true if Actions are to be locked 312 * @return A List of WorkflowActionBean 313 * @throws StoreException 314 */ 315 public List<WorkflowActionBean> getActionsForWorkflow(final String wfId, final boolean locking) 316 throws StoreException { 317 ParamChecker.notEmpty(wfId, "WorkflowID"); 318 List<WorkflowActionBean> actions = doOperation("getActionsForWorkflow", 319 new Callable<List<WorkflowActionBean>>() { 320 public List<WorkflowActionBean> call() throws SQLException, StoreException, WorkflowException, 321 InterruptedException { 322 List<WorkflowActionBean> actions; 323 List<WorkflowActionBean> actionList = new ArrayList<WorkflowActionBean>(); 324 try { 325 Query q = entityManager.createNamedQuery("GET_ACTIONS_FOR_WORKFLOW"); 326 327 /* 328 * OpenJPAQuery oq = OpenJPAPersistence.cast(q); 329 * if (locking) { // 330 * q.setHint("openjpa.FetchPlan.ReadLockMode" 331 * ,"WRITE"); FetchPlan fetch = oq.getFetchPlan(); 332 * fetch.setReadLockMode(LockModeType.WRITE); 333 * fetch.setLockTimeout(1000); // 1 seconds } 334 */ 335 q.setParameter("wfId", wfId); 336 actions = q.getResultList(); 337 for (WorkflowActionBean a : actions) { 338 WorkflowActionBean aa = getBeanForRunningAction(a); 339 actionList.add(aa); 340 } 341 } 342 catch (IllegalStateException e) { 343 throw new StoreException(ErrorCode.E0601, e.getMessage(), e); 344 } 345 /* 346 * if (locking) { return actions; } else { 347 */ 348 return actionList; 349 // } 350 } 351 }); 352 return actions; 353 } 354 355 /** 356 * Loads given number of actions for the given Workflow. Also locks all the actions if locking is true. 357 * 358 * @param wfId Workflow ID 359 * @param start offset for select statement 360 * @param len number of Workflow Actions to be returned 361 * @param locking true if Actions are to be locked 362 * @return A List of WorkflowActionBean 363 * @throws StoreException 364 */ 365 public List<WorkflowActionBean> getActionsSubsetForWorkflow(final String wfId, final int start, final int len) 366 throws StoreException { 367 ParamChecker.notEmpty(wfId, "WorkflowID"); 368 List<WorkflowActionBean> actions = doOperation("getActionsForWorkflow", 369 new Callable<List<WorkflowActionBean>>() { 370 public List<WorkflowActionBean> call() throws SQLException, StoreException, WorkflowException, 371 InterruptedException { 372 List<WorkflowActionBean> actions; 373 List<WorkflowActionBean> actionList = new ArrayList<WorkflowActionBean>(); 374 try { 375 Query q = entityManager.createNamedQuery("GET_ACTIONS_FOR_WORKFLOW"); 376 OpenJPAQuery oq = OpenJPAPersistence.cast(q); 377 q.setParameter("wfId", wfId); 378 q.setFirstResult(start - 1); 379 q.setMaxResults(len); 380 actions = q.getResultList(); 381 for (WorkflowActionBean a : actions) { 382 WorkflowActionBean aa = getBeanForRunningAction(a); 383 actionList.add(aa); 384 } 385 } 386 catch (IllegalStateException e) { 387 throw new StoreException(ErrorCode.E0601, e.getMessage(), e); 388 } 389 return actionList; 390 } 391 }); 392 return actions; 393 } 394 395 /** 396 * Load All the actions that are pending for more than given time. 397 * 398 * @param minimumPendingAgeSecs Minimum Pending age in seconds 399 * @return List of action beans 400 * @throws StoreException 401 */ 402 public List<WorkflowActionBean> getPendingActions(final long minimumPendingAgeSecs) throws StoreException { 403 List<WorkflowActionBean> actions = doOperation("getPendingActions", new Callable<List<WorkflowActionBean>>() { 404 public List<WorkflowActionBean> call() throws SQLException, StoreException, WorkflowException { 405 Timestamp ts = new Timestamp(System.currentTimeMillis() - minimumPendingAgeSecs * 1000); 406 List<WorkflowActionBean> actionList = null; 407 try { 408 Query q = entityManager.createNamedQuery("GET_PENDING_ACTIONS"); 409 q.setParameter("pendingAge", ts); 410 actionList = q.getResultList(); 411 } 412 catch (IllegalStateException e) { 413 throw new StoreException(ErrorCode.E0601, e.getMessage(), e); 414 } 415 return actionList; 416 } 417 }); 418 return actions; 419 } 420 421 /** 422 * Load All the actions that are running and were last checked after now - miminumCheckAgeSecs 423 * 424 * @param checkAgeSecs check age in seconds. 425 * @return List of action beans. 426 * @throws StoreException 427 */ 428 public List<WorkflowActionBean> getRunningActions(final long checkAgeSecs) throws StoreException { 429 List<WorkflowActionBean> actions = doOperation("getRunningActions", new Callable<List<WorkflowActionBean>>() { 430 431 public List<WorkflowActionBean> call() throws SQLException, StoreException, WorkflowException { 432 List<WorkflowActionBean> actions = new ArrayList<WorkflowActionBean>(); 433 Timestamp ts = new Timestamp(System.currentTimeMillis() - checkAgeSecs * 1000); 434 try { 435 Query q = entityManager.createNamedQuery("GET_RUNNING_ACTIONS"); 436 q.setParameter("lastCheckTime", ts); 437 actions = q.getResultList(); 438 } 439 catch (IllegalStateException e) { 440 throw new StoreException(ErrorCode.E0601, e.getMessage(), e); 441 } 442 443 return actions; 444 } 445 }); 446 return actions; 447 } 448 449 /** 450 * Load All the actions that are START_RETRY or START_MANUAL or END_RETRY or END_MANUAL. 451 * 452 * @param wfId String 453 * @return List of action beans 454 * @throws StoreException 455 */ 456 public List<WorkflowActionBean> getRetryAndManualActions(final String wfId) throws StoreException { 457 List<WorkflowActionBean> actions = doOperation("GET_RETRY_MANUAL_ACTIONS", 458 new Callable<List<WorkflowActionBean>>() { 459 public List<WorkflowActionBean> call() throws SQLException, StoreException, WorkflowException { 460 List<WorkflowActionBean> actionList = null; 461 try { 462 Query q = entityManager.createNamedQuery("GET_RETRY_MANUAL_ACTIONS"); 463 q.setParameter("wfId", wfId); 464 actionList = q.getResultList(); 465 } 466 catch (IllegalStateException e) { 467 throw new StoreException(ErrorCode.E0601, e.getMessage(), e); 468 } 469 470 return actionList; 471 } 472 }); 473 return actions; 474 } 475 476 /** 477 * Loads all the jobs that are satisfying the given filter condition. Filters can be applied on user, group, 478 * appName, status. 479 * 480 * @param filter Filter condition 481 * @param start offset for select statement 482 * @param len number of Workflows to be returned 483 * @return A list of workflows 484 * @throws StoreException 485 */ 486 public WorkflowsInfo getWorkflowsInfo(final Map<String, List<String>> filter, final int start, final int len) 487 throws StoreException { 488 489 WorkflowsInfo workFlowsInfo = doOperation("getWorkflowsInfo", new Callable<WorkflowsInfo>() { 490 @SuppressWarnings("unchecked") 491 public WorkflowsInfo call() throws SQLException, StoreException { 492 493 List<String> orArray = new ArrayList<String>(); 494 List<String> colArray = new ArrayList<String>(); 495 List<String> valArray = new ArrayList<String>(); 496 StringBuilder sb = new StringBuilder(""); 497 boolean isStatus = false; 498 boolean isGroup = false; 499 boolean isAppName = false; 500 boolean isUser = false; 501 boolean isEnabled = false; 502 int index = 0; 503 for (Map.Entry<String, List<String>> entry : filter.entrySet()) { 504 String colName = null; 505 String colVar = null; 506 if (entry.getKey().equals(OozieClient.FILTER_GROUP)) { 507 List<String> values = filter.get(OozieClient.FILTER_GROUP); 508 colName = "group"; 509 for (int i = 0; i < values.size(); i++) { 510 colVar = "group"; 511 colVar = colVar + index; 512 if (!isEnabled && !isGroup) { 513 sb.append(seletStr).append(" where w.group IN (:group" + index); 514 isGroup = true; 515 isEnabled = true; 516 } 517 else { 518 if (isEnabled && !isGroup) { 519 sb.append(" and w.group IN (:group" + index); 520 isGroup = true; 521 } 522 else { 523 if (isGroup) { 524 sb.append(", :group" + index); 525 } 526 } 527 } 528 if (i == values.size() - 1) { 529 sb.append(")"); 530 } 531 index++; 532 valArray.add(values.get(i)); 533 orArray.add(colName); 534 colArray.add(colVar); 535 } 536 } 537 else { 538 if (entry.getKey().equals(OozieClient.FILTER_STATUS)) { 539 List<String> values = filter.get(OozieClient.FILTER_STATUS); 540 colName = "status"; 541 for (int i = 0; i < values.size(); i++) { 542 colVar = "status"; 543 colVar = colVar + index; 544 if (!isEnabled && !isStatus) { 545 sb.append(seletStr).append(" where w.status IN (:status" + index); 546 isStatus = true; 547 isEnabled = true; 548 } 549 else { 550 if (isEnabled && !isStatus) { 551 sb.append(" and w.status IN (:status" + index); 552 isStatus = true; 553 } 554 else { 555 if (isStatus) { 556 sb.append(", :status" + index); 557 } 558 } 559 } 560 if (i == values.size() - 1) { 561 sb.append(")"); 562 } 563 index++; 564 valArray.add(values.get(i)); 565 orArray.add(colName); 566 colArray.add(colVar); 567 } 568 } 569 else { 570 if (entry.getKey().equals(OozieClient.FILTER_NAME)) { 571 List<String> values = filter.get(OozieClient.FILTER_NAME); 572 colName = "appName"; 573 for (int i = 0; i < values.size(); i++) { 574 colVar = "appName"; 575 colVar = colVar + index; 576 if (!isEnabled && !isAppName) { 577 sb.append(seletStr).append(" where w.appName IN (:appName" + index); 578 isAppName = true; 579 isEnabled = true; 580 } 581 else { 582 if (isEnabled && !isAppName) { 583 sb.append(" and w.appName IN (:appName" + index); 584 isAppName = true; 585 } 586 else { 587 if (isAppName) { 588 sb.append(", :appName" + index); 589 } 590 } 591 } 592 if (i == values.size() - 1) { 593 sb.append(")"); 594 } 595 index++; 596 valArray.add(values.get(i)); 597 orArray.add(colName); 598 colArray.add(colVar); 599 } 600 } 601 else { 602 if (entry.getKey().equals(OozieClient.FILTER_USER)) { 603 List<String> values = filter.get(OozieClient.FILTER_USER); 604 colName = "user"; 605 for (int i = 0; i < values.size(); i++) { 606 colVar = "user"; 607 colVar = colVar + index; 608 if (!isEnabled && !isUser) { 609 sb.append(seletStr).append(" where w.user IN (:user" + index); 610 isUser = true; 611 isEnabled = true; 612 } 613 else { 614 if (isEnabled && !isUser) { 615 sb.append(" and w.user IN (:user" + index); 616 isUser = true; 617 } 618 else { 619 if (isUser) { 620 sb.append(", :user" + index); 621 } 622 } 623 } 624 if (i == values.size() - 1) { 625 sb.append(")"); 626 } 627 index++; 628 valArray.add(values.get(i)); 629 orArray.add(colName); 630 colArray.add(colVar); 631 } 632 } 633 } 634 } 635 } 636 } 637 638 int realLen = 0; 639 640 Query q = null; 641 Query qTotal = null; 642 if (orArray.size() == 0) { 643 q = entityManager.createNamedQuery("GET_WORKFLOWS_COLUMNS"); 644 q.setFirstResult(start - 1); 645 q.setMaxResults(len); 646 qTotal = entityManager.createNamedQuery("GET_WORKFLOWS_COUNT"); 647 } 648 else { 649 if (orArray.size() > 0) { 650 StringBuilder sbTotal = new StringBuilder(sb); 651 sb.append(" order by w.startTimestamp desc "); 652 XLog.getLog(getClass()).debug("Created String is **** " + sb.toString()); 653 q = entityManager.createQuery(sb.toString()); 654 q.setFirstResult(start - 1); 655 q.setMaxResults(len); 656 qTotal = entityManager.createQuery(sbTotal.toString().replace(seletStr, countStr)); 657 for (int i = 0; i < orArray.size(); i++) { 658 q.setParameter(colArray.get(i), valArray.get(i)); 659 qTotal.setParameter(colArray.get(i), valArray.get(i)); 660 } 661 } 662 } 663 664 OpenJPAQuery kq = OpenJPAPersistence.cast(q); 665 JDBCFetchPlan fetch = (JDBCFetchPlan) kq.getFetchPlan(); 666 fetch.setFetchBatchSize(20); 667 fetch.setResultSetType(ResultSetType.SCROLL_INSENSITIVE); 668 fetch.setFetchDirection(FetchDirection.FORWARD); 669 fetch.setLRSSizeAlgorithm(LRSSizeAlgorithm.LAST); 670 List<?> resultList = q.getResultList(); 671 List<Object[]> objectArrList = (List<Object[]>) resultList; 672 List<WorkflowJobBean> wfBeansList = new ArrayList<WorkflowJobBean>(); 673 674 for (Object[] arr : objectArrList) { 675 WorkflowJobBean ww = getBeanForWorkflowFromArray(arr); 676 wfBeansList.add(ww); 677 } 678 679 realLen = ((Long) qTotal.getSingleResult()).intValue(); 680 681 return new WorkflowsInfo(wfBeansList, start, len, realLen); 682 } 683 }); 684 return workFlowsInfo; 685 686 } 687 688 /** 689 * Load the Workflow and all Action details and return a WorkflowJobBean. Workflow Instance is not loaded 690 * 691 * @param id Workflow Id 692 * @return Workflow Bean 693 * @throws StoreException If Workflow doesn't exist 694 */ 695 public WorkflowJobBean getWorkflowInfo(final String id) throws StoreException { 696 ParamChecker.notEmpty(id, "WorkflowID"); 697 WorkflowJobBean wfBean = doOperation("getWorkflowInfo", new Callable<WorkflowJobBean>() { 698 public WorkflowJobBean call() throws SQLException, StoreException, InterruptedException { 699 WorkflowJobBean wfBean = null; 700 wfBean = getWorkflowforInfo(id, false); 701 if (wfBean == null) { 702 throw new StoreException(ErrorCode.E0604, id); 703 } 704 else { 705 wfBean.setActions(getActionsForWorkflow(id, false)); 706 } 707 return wfBean; 708 } 709 }); 710 return wfBean; 711 } 712 713 /** 714 * Load the Workflow and subset Actions details and return a WorkflowJobBean. Workflow Instance is not loaded 715 * 716 * @param id Workflow Id 717 * @param start offset for select statement for actions 718 * @param len number of Workflow Actions to be returned 719 * @return Workflow Bean 720 * @throws StoreException If Workflow doesn't exist 721 */ 722 public WorkflowJobBean getWorkflowInfoWithActionsSubset(final String id, final int start, final int len) throws StoreException { 723 ParamChecker.notEmpty(id, "WorkflowID"); 724 WorkflowJobBean wfBean = doOperation("getWorkflowInfo", new Callable<WorkflowJobBean>() { 725 public WorkflowJobBean call() throws SQLException, StoreException, InterruptedException { 726 WorkflowJobBean wfBean = null; 727 wfBean = getWorkflowforInfo(id, false); 728 if (wfBean == null) { 729 throw new StoreException(ErrorCode.E0604, id); 730 } 731 else { 732 wfBean.setActions(getActionsSubsetForWorkflow(id, start, len)); 733 } 734 return wfBean; 735 } 736 }); 737 return wfBean; 738 } 739 740 /** 741 * Get the Workflow ID with given external ID which will be assigned for the subworkflows. 742 * 743 * @param externalId external ID 744 * @return Workflow ID 745 * @throws StoreException if there is no job with external ID 746 */ 747 public String getWorkflowIdForExternalId(final String externalId) throws StoreException { 748 ParamChecker.notEmpty(externalId, "externalId"); 749 String wfId = doOperation("getWorkflowIdForExternalId", new Callable<String>() { 750 public String call() throws SQLException, StoreException { 751 String id = ""; 752 Query q = entityManager.createNamedQuery("GET_WORKFLOW_ID_FOR_EXTERNAL_ID"); 753 q.setParameter("externalId", externalId); 754 List<String> w = q.getResultList(); 755 if (w.size() == 0) { 756 id = ""; 757 } 758 else { 759 int index = w.size() - 1; 760 id = w.get(index); 761 } 762 return id; 763 } 764 }); 765 return wfId; 766 } 767 768 private static final long DAY_IN_MS = 24 * 60 * 60 * 1000; 769 770 /** 771 * Purge the Workflows Completed older than given days. 772 * 773 * @param olderThanDays number of days for which to preserve the workflows 774 * @throws StoreException 775 */ 776 public void purge(final long olderThanDays, final int limit) throws StoreException { 777 doOperation("purge", new Callable<Void>() { 778 public Void call() throws SQLException, StoreException, WorkflowException { 779 Timestamp maxEndTime = new Timestamp(System.currentTimeMillis() - (olderThanDays * DAY_IN_MS)); 780 Query q = entityManager.createNamedQuery("GET_COMPLETED_WORKFLOWS_OLDER_THAN"); 781 q.setParameter("endTime", maxEndTime); 782 q.setMaxResults(limit); 783 List<WorkflowJobBean> workflows = q.getResultList(); 784 785 int actionDeleted = 0; 786 if (workflows.size() != 0) { 787 for (WorkflowJobBean w : workflows) { 788 String wfId = w.getId(); 789 entityManager.remove(w); 790 Query g = entityManager.createNamedQuery("DELETE_ACTIONS_FOR_WORKFLOW"); 791 g.setParameter("wfId", wfId); 792 actionDeleted += g.executeUpdate(); 793 } 794 } 795 796 XLog.getLog(getClass()).debug("ENDED Workflow Purge deleted jobs :" + workflows.size() + " and actions " + actionDeleted); 797 return null; 798 } 799 }); 800 } 801 802 private <V> V doOperation(String name, Callable<V> command) throws StoreException { 803 try { 804 Instrumentation.Cron cron = new Instrumentation.Cron(); 805 cron.start(); 806 V retVal; 807 try { 808 retVal = command.call(); 809 } 810 finally { 811 cron.stop(); 812 } 813 Services.get().get(InstrumentationService.class).get().addCron(INSTR_GROUP, name, cron); 814 return retVal; 815 } 816 catch (StoreException ex) { 817 throw ex; 818 } 819 catch (SQLException ex) { 820 throw new StoreException(ErrorCode.E0603, name, ex.getMessage(), ex); 821 } 822 catch (Exception e) { 823 throw new StoreException(ErrorCode.E0607, name, e.getMessage(), e); 824 } 825 } 826 827 private WorkflowJobBean getWorkflowOnly(final String id, boolean locking) throws SQLException, 828 InterruptedException, StoreException { 829 WorkflowJobBean wfBean = null; 830 Query q = entityManager.createNamedQuery("GET_WORKFLOW"); 831 /* 832 * if (locking) { // q.setHint("openjpa.FetchPlan.ReadLockMode","READ"); 833 * OpenJPAQuery oq = OpenJPAPersistence.cast(q); FetchPlan fetch = 834 * oq.getFetchPlan(); fetch.setReadLockMode(LockModeType.WRITE); 835 * fetch.setLockTimeout(-1); // unlimited } 836 */ 837 q.setParameter("id", id); 838 List<WorkflowJobBean> w = q.getResultList(); 839 if (w.size() > 0) { 840 wfBean = w.get(0); 841 } 842 return wfBean; 843 // return getBeanForRunningWorkflow(wfBean); 844 } 845 846 private WorkflowJobBean getWorkflowforInfo(final String id, boolean locking) throws SQLException, 847 InterruptedException, StoreException { 848 WorkflowJobBean wfBean = null; 849 Query q = entityManager.createNamedQuery("GET_WORKFLOW"); 850 q.setParameter("id", id); 851 List<WorkflowJobBean> w = q.getResultList(); 852 if (w.size() > 0) { 853 wfBean = w.get(0); 854 return getBeanForRunningWorkflow(wfBean); 855 } 856 return null; 857 } 858 859 private WorkflowJobBean getBeanForRunningWorkflow(WorkflowJobBean w) throws SQLException { 860 WorkflowJobBean wfBean = new WorkflowJobBean(); 861 wfBean.setId(w.getId()); 862 wfBean.setAppName(w.getAppName()); 863 wfBean.setAppPath(w.getAppPath()); 864 wfBean.setConf(w.getConf()); 865 wfBean.setGroup(w.getGroup()); 866 wfBean.setRun(w.getRun()); 867 wfBean.setUser(w.getUser()); 868 wfBean.setAuthToken(w.getAuthToken()); 869 wfBean.setCreatedTime(w.getCreatedTime()); 870 wfBean.setEndTime(w.getEndTime()); 871 wfBean.setExternalId(w.getExternalId()); 872 wfBean.setLastModifiedTime(w.getLastModifiedTime()); 873 wfBean.setLogToken(w.getLogToken()); 874 wfBean.setProtoActionConf(w.getProtoActionConf()); 875 wfBean.setSlaXml(w.getSlaXml()); 876 wfBean.setStartTime(w.getStartTime()); 877 wfBean.setStatus(w.getStatus()); 878 wfBean.setWfInstance(w.getWfInstance()); 879 return wfBean; 880 } 881 882 private WorkflowJobBean getBeanForWorkflowFromArray(Object[] arr) { 883 884 WorkflowJobBean wfBean = new WorkflowJobBean(); 885 wfBean.setId((String) arr[0]); 886 if (arr[1] != null) { 887 wfBean.setAppName((String) arr[1]); 888 } 889 if (arr[2] != null) { 890 wfBean.setStatus(Status.valueOf((String) arr[2])); 891 } 892 if (arr[3] != null) { 893 wfBean.setRun((Integer) arr[3]); 894 } 895 if (arr[4] != null) { 896 wfBean.setUser((String) arr[4]); 897 } 898 if (arr[5] != null) { 899 wfBean.setGroup((String) arr[5]); 900 } 901 if (arr[6] != null) { 902 wfBean.setCreatedTime((Timestamp) arr[6]); 903 } 904 if (arr[7] != null) { 905 wfBean.setStartTime((Timestamp) arr[7]); 906 } 907 if (arr[8] != null) { 908 wfBean.setLastModifiedTime((Timestamp) arr[8]); 909 } 910 if (arr[9] != null) { 911 wfBean.setEndTime((Timestamp) arr[9]); 912 } 913 return wfBean; 914 } 915 916 private WorkflowActionBean getBeanForRunningAction(WorkflowActionBean a) throws SQLException { 917 if (a != null) { 918 WorkflowActionBean action = new WorkflowActionBean(); 919 action.setId(a.getId()); 920 action.setConf(a.getConf()); 921 action.setConsoleUrl(a.getConsoleUrl()); 922 action.setData(a.getData()); 923 action.setErrorInfo(a.getErrorCode(), a.getErrorMessage()); 924 action.setExternalId(a.getExternalId()); 925 action.setExternalStatus(a.getExternalStatus()); 926 action.setName(a.getName()); 927 action.setRetries(a.getRetries()); 928 action.setTrackerUri(a.getTrackerUri()); 929 action.setTransition(a.getTransition()); 930 action.setType(a.getType()); 931 action.setEndTime(a.getEndTime()); 932 action.setExecutionPath(a.getExecutionPath()); 933 action.setLastCheckTime(a.getLastCheckTime()); 934 action.setLogToken(a.getLogToken()); 935 if (a.getPending() == true) { 936 action.setPending(); 937 } 938 action.setPendingAge(a.getPendingAge()); 939 action.setSignalValue(a.getSignalValue()); 940 action.setSlaXml(a.getSlaXml()); 941 action.setStartTime(a.getStartTime()); 942 action.setStatus(a.getStatus()); 943 action.setJobId(a.getWfId()); 944 return action; 945 } 946 return null; 947 } 948 949 private void setWFQueryParameters(WorkflowJobBean wfBean, Query q) { 950 q.setParameter("appName", wfBean.getAppName()); 951 q.setParameter("appPath", wfBean.getAppPath()); 952 q.setParameter("conf", wfBean.getConf()); 953 q.setParameter("groupName", wfBean.getGroup()); 954 q.setParameter("run", wfBean.getRun()); 955 q.setParameter("user", wfBean.getUser()); 956 q.setParameter("authToken", wfBean.getAuthToken()); 957 q.setParameter("createdTime", wfBean.getCreatedTimestamp()); 958 q.setParameter("endTime", wfBean.getEndTimestamp()); 959 q.setParameter("externalId", wfBean.getExternalId()); 960 q.setParameter("lastModTime", new Date()); 961 q.setParameter("logToken", wfBean.getLogToken()); 962 q.setParameter("protoActionConf", wfBean.getProtoActionConf()); 963 q.setParameter("slaXml", wfBean.getSlaXml()); 964 q.setParameter("startTime", wfBean.getStartTimestamp()); 965 q.setParameter("status", wfBean.getStatusStr()); 966 q.setParameter("wfInstance", wfBean.getWfInstance()); 967 } 968 969 private void setActionQueryParameters(WorkflowActionBean aBean, Query q) { 970 q.setParameter("conf", aBean.getConf()); 971 q.setParameter("consoleUrl", aBean.getConsoleUrl()); 972 q.setParameter("data", aBean.getData()); 973 q.setParameter("errorCode", aBean.getErrorCode()); 974 q.setParameter("errorMessage", aBean.getErrorMessage()); 975 q.setParameter("externalId", aBean.getExternalId()); 976 q.setParameter("externalStatus", aBean.getExternalStatus()); 977 q.setParameter("name", aBean.getName()); 978 q.setParameter("retries", aBean.getRetries()); 979 q.setParameter("trackerUri", aBean.getTrackerUri()); 980 q.setParameter("transition", aBean.getTransition()); 981 q.setParameter("type", aBean.getType()); 982 q.setParameter("endTime", aBean.getEndTimestamp()); 983 q.setParameter("executionPath", aBean.getExecutionPath()); 984 q.setParameter("lastCheckTime", aBean.getLastCheckTimestamp()); 985 q.setParameter("logToken", aBean.getLogToken()); 986 q.setParameter("pending", aBean.isPending() ? 1 : 0); 987 q.setParameter("pendingAge", aBean.getPendingAgeTimestamp()); 988 q.setParameter("signalValue", aBean.getSignalValue()); 989 q.setParameter("slaXml", aBean.getSlaXml()); 990 q.setParameter("startTime", aBean.getStartTimestamp()); 991 q.setParameter("status", aBean.getStatusStr()); 992 q.setParameter("wfId", aBean.getWfId()); 993 } 994 }