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    }