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) 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 List<WorkflowJobBean> workflows = q.getResultList();
783 if (workflows.size() != 0) {
784 for (WorkflowJobBean w : workflows) {
785 String wfId = w.getId();
786 entityManager.remove(w);
787 Query g = entityManager.createNamedQuery("DELETE_ACTIONS_FOR_WORKFLOW");
788 g.setParameter("wfId", wfId);
789 int deleted_action = g.executeUpdate();
790 }
791 }
792
793 return null;
794 }
795 });
796 }
797
798 private <V> V doOperation(String name, Callable<V> command) throws StoreException {
799 try {
800 Instrumentation.Cron cron = new Instrumentation.Cron();
801 cron.start();
802 V retVal;
803 try {
804 retVal = command.call();
805 }
806 finally {
807 cron.stop();
808 }
809 Services.get().get(InstrumentationService.class).get().addCron(INSTR_GROUP, name, cron);
810 return retVal;
811 }
812 catch (StoreException ex) {
813 throw ex;
814 }
815 catch (SQLException ex) {
816 throw new StoreException(ErrorCode.E0603, name, ex.getMessage(), ex);
817 }
818 catch (Exception e) {
819 throw new StoreException(ErrorCode.E0607, name, e.getMessage(), e);
820 }
821 }
822
823 private WorkflowJobBean getWorkflowOnly(final String id, boolean locking) throws SQLException,
824 InterruptedException, StoreException {
825 WorkflowJobBean wfBean = null;
826 Query q = entityManager.createNamedQuery("GET_WORKFLOW");
827 /*
828 * if (locking) { // q.setHint("openjpa.FetchPlan.ReadLockMode","READ");
829 * OpenJPAQuery oq = OpenJPAPersistence.cast(q); FetchPlan fetch =
830 * oq.getFetchPlan(); fetch.setReadLockMode(LockModeType.WRITE);
831 * fetch.setLockTimeout(-1); // unlimited }
832 */
833 q.setParameter("id", id);
834 List<WorkflowJobBean> w = q.getResultList();
835 if (w.size() > 0) {
836 wfBean = w.get(0);
837 }
838 return wfBean;
839 // return getBeanForRunningWorkflow(wfBean);
840 }
841
842 private WorkflowJobBean getWorkflowforInfo(final String id, boolean locking) throws SQLException,
843 InterruptedException, StoreException {
844 WorkflowJobBean wfBean = null;
845 Query q = entityManager.createNamedQuery("GET_WORKFLOW");
846 q.setParameter("id", id);
847 List<WorkflowJobBean> w = q.getResultList();
848 if (w.size() > 0) {
849 wfBean = w.get(0);
850 return getBeanForRunningWorkflow(wfBean);
851 }
852 return null;
853 }
854
855 private WorkflowJobBean getBeanForRunningWorkflow(WorkflowJobBean w) throws SQLException {
856 WorkflowJobBean wfBean = new WorkflowJobBean();
857 wfBean.setId(w.getId());
858 wfBean.setAppName(w.getAppName());
859 wfBean.setAppPath(w.getAppPath());
860 wfBean.setConf(w.getConf());
861 wfBean.setGroup(w.getGroup());
862 wfBean.setRun(w.getRun());
863 wfBean.setUser(w.getUser());
864 wfBean.setAuthToken(w.getAuthToken());
865 wfBean.setCreatedTime(w.getCreatedTime());
866 wfBean.setEndTime(w.getEndTime());
867 wfBean.setExternalId(w.getExternalId());
868 wfBean.setLastModifiedTime(w.getLastModifiedTime());
869 wfBean.setLogToken(w.getLogToken());
870 wfBean.setProtoActionConf(w.getProtoActionConf());
871 wfBean.setSlaXml(w.getSlaXml());
872 wfBean.setStartTime(w.getStartTime());
873 wfBean.setStatus(w.getStatus());
874 wfBean.setWfInstance(w.getWfInstance());
875 return wfBean;
876 }
877
878 private WorkflowJobBean getBeanForWorkflowFromArray(Object[] arr) {
879
880 WorkflowJobBean wfBean = new WorkflowJobBean();
881 wfBean.setId((String) arr[0]);
882 if (arr[1] != null) {
883 wfBean.setAppName((String) arr[1]);
884 }
885 if (arr[2] != null) {
886 wfBean.setStatus(Status.valueOf((String) arr[2]));
887 }
888 if (arr[3] != null) {
889 wfBean.setRun((Integer) arr[3]);
890 }
891 if (arr[4] != null) {
892 wfBean.setUser((String) arr[4]);
893 }
894 if (arr[5] != null) {
895 wfBean.setGroup((String) arr[5]);
896 }
897 if (arr[6] != null) {
898 wfBean.setCreatedTime((Timestamp) arr[6]);
899 }
900 if (arr[7] != null) {
901 wfBean.setStartTime((Timestamp) arr[7]);
902 }
903 if (arr[8] != null) {
904 wfBean.setLastModifiedTime((Timestamp) arr[8]);
905 }
906 if (arr[9] != null) {
907 wfBean.setEndTime((Timestamp) arr[9]);
908 }
909 return wfBean;
910 }
911
912 private WorkflowActionBean getBeanForRunningAction(WorkflowActionBean a) throws SQLException {
913 if (a != null) {
914 WorkflowActionBean action = new WorkflowActionBean();
915 action.setId(a.getId());
916 action.setConf(a.getConf());
917 action.setConsoleUrl(a.getConsoleUrl());
918 action.setData(a.getData());
919 action.setErrorInfo(a.getErrorCode(), a.getErrorMessage());
920 action.setExternalId(a.getExternalId());
921 action.setExternalStatus(a.getExternalStatus());
922 action.setName(a.getName());
923 action.setRetries(a.getRetries());
924 action.setTrackerUri(a.getTrackerUri());
925 action.setTransition(a.getTransition());
926 action.setType(a.getType());
927 action.setEndTime(a.getEndTime());
928 action.setExecutionPath(a.getExecutionPath());
929 action.setLastCheckTime(a.getLastCheckTime());
930 action.setLogToken(a.getLogToken());
931 if (a.getPending() == true) {
932 action.setPending();
933 }
934 action.setPendingAge(a.getPendingAge());
935 action.setSignalValue(a.getSignalValue());
936 action.setSlaXml(a.getSlaXml());
937 action.setStartTime(a.getStartTime());
938 action.setStatus(a.getStatus());
939 action.setJobId(a.getWfId());
940 return action;
941 }
942 return null;
943 }
944
945 private void setWFQueryParameters(WorkflowJobBean wfBean, Query q) {
946 q.setParameter("appName", wfBean.getAppName());
947 q.setParameter("appPath", wfBean.getAppPath());
948 q.setParameter("conf", wfBean.getConf());
949 q.setParameter("groupName", wfBean.getGroup());
950 q.setParameter("run", wfBean.getRun());
951 q.setParameter("user", wfBean.getUser());
952 q.setParameter("authToken", wfBean.getAuthToken());
953 q.setParameter("createdTime", wfBean.getCreatedTimestamp());
954 q.setParameter("endTime", wfBean.getEndTimestamp());
955 q.setParameter("externalId", wfBean.getExternalId());
956 q.setParameter("lastModTime", new Date());
957 q.setParameter("logToken", wfBean.getLogToken());
958 q.setParameter("protoActionConf", wfBean.getProtoActionConf());
959 q.setParameter("slaXml", wfBean.getSlaXml());
960 q.setParameter("startTime", wfBean.getStartTimestamp());
961 q.setParameter("status", wfBean.getStatusStr());
962 q.setParameter("wfInstance", wfBean.getWfInstance());
963 }
964
965 private void setActionQueryParameters(WorkflowActionBean aBean, Query q) {
966 q.setParameter("conf", aBean.getConf());
967 q.setParameter("consoleUrl", aBean.getConsoleUrl());
968 q.setParameter("data", aBean.getData());
969 q.setParameter("errorCode", aBean.getErrorCode());
970 q.setParameter("errorMessage", aBean.getErrorMessage());
971 q.setParameter("externalId", aBean.getExternalId());
972 q.setParameter("externalStatus", aBean.getExternalStatus());
973 q.setParameter("name", aBean.getName());
974 q.setParameter("retries", aBean.getRetries());
975 q.setParameter("trackerUri", aBean.getTrackerUri());
976 q.setParameter("transition", aBean.getTransition());
977 q.setParameter("type", aBean.getType());
978 q.setParameter("endTime", aBean.getEndTimestamp());
979 q.setParameter("executionPath", aBean.getExecutionPath());
980 q.setParameter("lastCheckTime", aBean.getLastCheckTimestamp());
981 q.setParameter("logToken", aBean.getLogToken());
982 q.setParameter("pending", aBean.isPending() ? 1 : 0);
983 q.setParameter("pendingAge", aBean.getPendingAgeTimestamp());
984 q.setParameter("signalValue", aBean.getSignalValue());
985 q.setParameter("slaXml", aBean.getSlaXml());
986 q.setParameter("startTime", aBean.getStartTimestamp());
987 q.setParameter("status", aBean.getStatusStr());
988 q.setParameter("wfId", aBean.getWfId());
989 }
990 }