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.service;
016    
017    import java.util.ArrayList;
018    import java.util.List;
019    
020    import org.apache.hadoop.conf.Configuration;
021    import org.apache.oozie.CoordinatorActionBean;
022    import org.apache.oozie.ErrorCode;
023    import org.apache.oozie.WorkflowActionBean;
024    import org.apache.oozie.command.CommandException;
025    import org.apache.oozie.command.coord.CoordActionCheckCommand;
026    import org.apache.oozie.command.coord.CoordActionCheckXCommand;
027    import org.apache.oozie.command.wf.ActionCheckCommand;
028    import org.apache.oozie.command.wf.ActionCheckXCommand;
029    import org.apache.oozie.executor.jpa.CoordActionsRunningGetJPAExecutor;
030    import org.apache.oozie.executor.jpa.JPAExecutorException;
031    import org.apache.oozie.executor.jpa.WorkflowActionsRunningGetJPAExecutor;
032    import org.apache.oozie.util.XCallable;
033    import org.apache.oozie.util.XLog;
034    
035    /**
036     * The Action Checker Service queue ActionCheckCommands to check the status of
037     * running actions and CoordActionCheckCommands to check the status of
038     * coordinator actions. The delay between checks on the same action can be
039     * configured.
040     */
041    public class ActionCheckerService implements Service {
042    
043        public static final String CONF_PREFIX = Service.CONF_PREFIX + "ActionCheckerService.";
044        /**
045         * The frequency at which the ActionCheckService will run.
046         */
047        public static final String CONF_ACTION_CHECK_INTERVAL = CONF_PREFIX + "action.check.interval";
048        /**
049         * The time, in seconds, between an ActionCheck for the same action.
050         */
051        public static final String CONF_ACTION_CHECK_DELAY = CONF_PREFIX + "action.check.delay";
052    
053        /**
054         * The number of callables to be queued in a batch.
055         */
056        public static final String CONF_CALLABLE_BATCH_SIZE = CONF_PREFIX + "callable.batch.size";
057    
058        protected static final String INSTRUMENTATION_GROUP = "actionchecker";
059        protected static final String INSTR_CHECK_ACTIONS_COUNTER = "checks_wf_actions";
060        protected static final String INSTR_CHECK_COORD_ACTIONS_COUNTER = "checks_coord_actions";
061    
062        private static boolean useXCommand = true;
063    
064        /**
065         * {@link ActionCheckRunnable} is the runnable which is scheduled to run and
066         * queue Action checks.
067         */
068        static class ActionCheckRunnable implements Runnable {
069            private int actionCheckDelay;
070            private List<XCallable<Void>> callables;
071            private StringBuilder msg = null;
072    
073            public ActionCheckRunnable(int actionCheckDelay) {
074                this.actionCheckDelay = actionCheckDelay;
075            }
076    
077            public void run() {
078                XLog.Info.get().clear();
079                XLog LOG = XLog.getLog(getClass());
080                msg = new StringBuilder();
081                try {
082                    runWFActionCheck();
083                    runCoordActionCheck();
084                }
085                catch (CommandException ce) {
086                    LOG.error("Unable to run action checks, ", ce);
087                }
088    
089                LOG.debug("QUEUING [{0}] for potential checking", msg.toString());
090                if (null != callables) {
091                    boolean ret = Services.get().get(CallableQueueService.class).queueSerial(callables);
092                    if (ret == false) {
093                        LOG.warn("Unable to queue the callables commands for CheckerService. "
094                                + "Most possibly command queue is full. Queue size is :"
095                                + Services.get().get(CallableQueueService.class).queueSize());
096                    }
097                    callables = null;
098                }
099            }
100    
101            /**
102             * check workflow actions
103             *
104             * @throws CommandException
105             */
106            private void runWFActionCheck() throws CommandException {
107                JPAService jpaService = Services.get().get(JPAService.class);
108                if (jpaService == null) {
109                    throw new CommandException(ErrorCode.E0610);
110                }
111    
112                List<WorkflowActionBean> actions;
113                try {
114                    actions = jpaService
115                            .execute(new WorkflowActionsRunningGetJPAExecutor(actionCheckDelay));
116                }
117                catch (JPAExecutorException je) {
118                    throw new CommandException(je);
119                }
120    
121                if (actions == null || actions.size() == 0) {
122                    return;
123                }
124                msg.append(" WF_ACTIONS : " + actions.size());
125    
126                for (WorkflowActionBean action : actions) {
127                    Services.get().get(InstrumentationService.class).get().incr(INSTRUMENTATION_GROUP,
128                            INSTR_CHECK_ACTIONS_COUNTER, 1);
129                    if (useXCommand) {
130                        queueCallable(new ActionCheckXCommand(action.getId()));
131                    }
132                    else {
133                        queueCallable(new ActionCheckCommand(action.getId()));
134                    }
135                }
136    
137            }
138    
139            /**
140             * check coordinator actions
141             *
142             * @throws CommandException
143             */
144            private void runCoordActionCheck() throws CommandException {
145                JPAService jpaService = Services.get().get(JPAService.class);
146                if (jpaService == null) {
147                    throw new CommandException(ErrorCode.E0610);
148                }
149    
150                List<CoordinatorActionBean> cactions;
151                try {
152                    cactions = jpaService.execute(new CoordActionsRunningGetJPAExecutor(
153                            actionCheckDelay));
154                }
155                catch (JPAExecutorException je) {
156                    throw new CommandException(je);
157                }
158    
159                if (cactions == null || cactions.size() == 0) {
160                    return;
161                }
162    
163                msg.append(" COORD_ACTIONS : " + cactions.size());
164    
165                for (CoordinatorActionBean caction : cactions) {
166                    Services.get().get(InstrumentationService.class).get().incr(INSTRUMENTATION_GROUP,
167                            INSTR_CHECK_COORD_ACTIONS_COUNTER, 1);
168                    if (useXCommand) {
169                        queueCallable(new CoordActionCheckXCommand(caction.getId(), actionCheckDelay));
170                    }
171                    else {
172                        queueCallable(new CoordActionCheckCommand(caction.getId(), actionCheckDelay));
173                    }
174                }
175    
176            }
177    
178            /**
179             * Adds callables to a list. If the number of callables in the list
180             * reaches {@link ActionCheckerService#CONF_CALLABLE_BATCH_SIZE}, the
181             * entire batch is queued and the callables list is reset.
182             *
183             * @param callable the callable to queue.
184             */
185            private void queueCallable(XCallable<Void> callable) {
186                if (callables == null) {
187                    callables = new ArrayList<XCallable<Void>>();
188                }
189                callables.add(callable);
190                if (callables.size() == Services.get().getConf().getInt(CONF_CALLABLE_BATCH_SIZE, 10)) {
191                    boolean ret = Services.get().get(CallableQueueService.class).queueSerial(callables);
192                    if (ret == false) {
193                        XLog.getLog(getClass()).warn(
194                                "Unable to queue the callables commands for CheckerService. "
195                                        + "Most possibly command queue is full. Queue size is :"
196                                        + Services.get().get(CallableQueueService.class).queueSize());
197                    }
198                    callables = new ArrayList<XCallable<Void>>();
199                }
200            }
201        }
202    
203        /**
204         * Initializes the Action Check service.
205         *
206         * @param services services instance.
207         */
208        @Override
209        public void init(Services services) {
210            Configuration conf = services.getConf();
211            Runnable actionCheckRunnable = new ActionCheckRunnable(conf.getInt(CONF_ACTION_CHECK_DELAY, 600));
212            services.get(SchedulerService.class).schedule(actionCheckRunnable, 10,
213                    conf.getInt(CONF_ACTION_CHECK_INTERVAL, 60), SchedulerService.Unit.SEC);
214    
215            if (Services.get().getConf().getBoolean(USE_XCOMMAND, true) == false) {
216                useXCommand = false;
217            }
218    
219        }
220    
221        /**
222         * Destroy the Action Checker Services.
223         */
224        @Override
225        public void destroy() {
226        }
227    
228        /**
229         * Return the public interface for the action checker service.
230         *
231         * @return {@link ActionCheckerService}.
232         */
233        @Override
234        public Class<? extends Service> getInterface() {
235            return ActionCheckerService.class;
236        }
237    }