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