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.servlet;
016    
017    import java.io.IOException;
018    import java.util.Arrays;
019    
020    import javax.servlet.ServletException;
021    import javax.servlet.http.HttpServletRequest;
022    import javax.servlet.http.HttpServletResponse;
023    
024    import org.apache.hadoop.conf.Configuration;
025    import org.apache.hadoop.fs.FileStatus;
026    import org.apache.hadoop.fs.FileSystem;
027    import org.apache.hadoop.fs.Path;
028    import org.apache.oozie.BaseEngineException;
029    import org.apache.oozie.ErrorCode;
030    import org.apache.oozie.client.OozieClient;
031    import org.apache.oozie.client.XOozieClient;
032    import org.apache.oozie.client.rest.JsonBean;
033    import org.apache.oozie.client.rest.RestConstants;
034    import org.apache.oozie.service.AuthorizationException;
035    import org.apache.oozie.service.AuthorizationService;
036    import org.apache.oozie.service.HadoopAccessorException;
037    import org.apache.oozie.service.HadoopAccessorService;
038    import org.apache.oozie.service.Services;
039    import org.apache.oozie.service.XLogService;
040    import org.apache.oozie.util.JobUtils;
041    import org.apache.oozie.util.XConfiguration;
042    import org.apache.oozie.util.XLog;
043    import org.json.simple.JSONObject;
044    
045    public abstract class BaseJobServlet extends JsonRestServlet {
046    
047        private static final ResourceInfo RESOURCES_INFO[] = new ResourceInfo[1];
048    
049        static {
050            RESOURCES_INFO[0] = new ResourceInfo("*", Arrays.asList("PUT", "GET"), Arrays.asList(new ParameterInfo(
051                    RestConstants.ACTION_PARAM, String.class, true, Arrays.asList("PUT")), new ParameterInfo(
052                    RestConstants.JOB_SHOW_PARAM, String.class, false, Arrays.asList("GET"))));
053        }
054    
055        public BaseJobServlet(String instrumentationName) {
056            super(instrumentationName, RESOURCES_INFO);
057        }
058    
059        /**
060         * Perform various job related actions - start, suspend, resume, kill, etc.
061         */
062        @Override
063        protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
064            String jobId = getResourceName(request);
065            request.setAttribute(AUDIT_PARAM, jobId);
066            request.setAttribute(AUDIT_OPERATION, request.getParameter(RestConstants.ACTION_PARAM));
067            try {
068                AuthorizationService auth = Services.get().get(AuthorizationService.class);
069                auth.authorizeForJob(getUser(request), jobId, true);
070            }
071            catch (AuthorizationException ex) {
072                throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
073            }
074    
075            String action = request.getParameter(RestConstants.ACTION_PARAM);
076            if (action.equals(RestConstants.JOB_ACTION_START)) {
077                stopCron();
078                startJob(request, response);
079                startCron();
080                response.setStatus(HttpServletResponse.SC_OK);
081            }
082            else if (action.equals(RestConstants.JOB_ACTION_RESUME)) {
083                stopCron();
084                resumeJob(request, response);
085                startCron();
086                response.setStatus(HttpServletResponse.SC_OK);
087            }
088            else if (action.equals(RestConstants.JOB_ACTION_SUSPEND)) {
089                stopCron();
090                suspendJob(request, response);
091                startCron();
092                response.setStatus(HttpServletResponse.SC_OK);
093            }
094            else if (action.equals(RestConstants.JOB_ACTION_KILL)) {
095                stopCron();
096                killJob(request, response);
097                startCron();
098                response.setStatus(HttpServletResponse.SC_OK);
099            }
100            else if (action.equals(RestConstants.JOB_ACTION_CHANGE)) {
101                stopCron();
102                changeJob(request, response);
103                startCron();
104                response.setStatus(HttpServletResponse.SC_OK);
105            }
106            else if (action.equals(RestConstants.JOB_ACTION_RERUN)) {
107                validateContentType(request, RestConstants.XML_CONTENT_TYPE);
108                Configuration conf = new XConfiguration(request.getInputStream());
109                stopCron();
110                checkAuthorizationForApp(getUser(request), conf);
111                JobUtils.normalizeAppPath(conf.get(OozieClient.USER_NAME), conf.get(OozieClient.GROUP_NAME), conf);
112                reRunJob(request, response, conf);
113                startCron();
114                response.setStatus(HttpServletResponse.SC_OK);
115            }
116            else if (action.equals(RestConstants.JOB_COORD_ACTION_RERUN)) {
117                validateContentType(request, RestConstants.XML_CONTENT_TYPE);
118                stopCron();
119                JSONObject json = reRunJob(request, response, null);
120                startCron();
121                if (json != null) {
122                    sendJsonResponse(response, HttpServletResponse.SC_OK, json);
123                }
124                else {
125                    response.setStatus(HttpServletResponse.SC_OK);
126                }
127            }
128            else if (action.equals(RestConstants.JOB_BUNDLE_ACTION_RERUN)) {
129                validateContentType(request, RestConstants.XML_CONTENT_TYPE);
130                stopCron();
131                JSONObject json = reRunJob(request, response, null);
132                startCron();
133                if (json != null) {
134                    sendJsonResponse(response, HttpServletResponse.SC_OK, json);
135                }
136                else {
137                    response.setStatus(HttpServletResponse.SC_OK);
138                }
139            }
140            else {
141                throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
142                        RestConstants.ACTION_PARAM, action);
143            }
144        }
145    
146        /**
147         * Validate the configuration user/group. <p/>
148         *
149         * @param requestUser user in request.
150         * @param conf configuration.
151         * @throws XServletException thrown if the configuration does not have a property {@link
152         * org.apache.oozie.client.OozieClient#USER_NAME}.
153         */
154        static void checkAuthorizationForApp(String requestUser, Configuration conf) throws XServletException {
155            String user = conf.get(OozieClient.USER_NAME);
156            String group = conf.get(OozieClient.GROUP_NAME);
157            try {
158                if (user == null) {
159                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0401, OozieClient.USER_NAME);
160                }
161                if (!requestUser.equals(UNDEF) && !user.equals(requestUser)) {
162                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0400, requestUser, user);
163                }
164                AuthorizationService auth = Services.get().get(AuthorizationService.class);
165                if (group == null) {
166                    group = auth.getDefaultGroup(user);
167                    conf.set(OozieClient.GROUP_NAME, group);
168                }
169                else {
170                    auth.authorizeForGroup(user, group);
171                }
172                XLog.Info.get().setParameter(XLogService.GROUP, group);
173                String wfPath = conf.get(OozieClient.APP_PATH);
174                String coordPath = conf.get(OozieClient.COORDINATOR_APP_PATH);
175                String bundlePath = conf.get(OozieClient.BUNDLE_APP_PATH);
176    
177                if (wfPath == null && coordPath == null && bundlePath == null) {
178                    String libPath = conf.get(XOozieClient.LIBPATH);
179                    conf.set(OozieClient.APP_PATH, libPath);
180                    wfPath = libPath;
181                }
182                ServletUtilities.ValidateAppPath(wfPath, coordPath, bundlePath);
183    
184                if (wfPath != null) {
185                    auth.authorizeForApp(user, group, wfPath, "workflow.xml", conf);
186                }
187                else if (coordPath != null){
188                    auth.authorizeForApp(user, group, coordPath, "coordinator.xml", conf);
189                }
190                else if (bundlePath != null){
191                    auth.authorizeForApp(user, group, bundlePath, "bundle.xml", conf);
192                }
193            }
194            catch (AuthorizationException ex) {
195                XLog.getLog(BaseJobServlet.class).info("AuthorizationException ", ex);
196                throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
197            }
198        }
199    
200        /**
201         * Return information about jobs.
202         */
203        @Override
204        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
205            String jobId = getResourceName(request);
206            String show = request.getParameter(RestConstants.JOB_SHOW_PARAM);
207    
208            try {
209                AuthorizationService auth = Services.get().get(AuthorizationService.class);
210                auth.authorizeForJob(getUser(request), jobId, false);
211            }
212            catch (AuthorizationException ex) {
213                throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
214            }
215    
216            if (show == null || show.equals(RestConstants.JOB_SHOW_INFO)) {
217                stopCron();
218                JsonBean job = null;
219                try {
220                    job = getJob(request, response);
221                }
222                catch (BaseEngineException e) {
223                    // TODO Auto-generated catch block
224                    // e.printStackTrace();
225    
226                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, e);
227                }
228                startCron();
229                sendJsonResponse(response, HttpServletResponse.SC_OK, job);
230            }
231            else if (show.equals(RestConstants.JOB_SHOW_LOG)) {
232                response.setContentType(TEXT_UTF8);
233                streamJobLog(request, response);
234            }
235            else if (show.equals(RestConstants.JOB_SHOW_DEFINITION)) {
236                stopCron();
237                response.setContentType(XML_UTF8);
238                String wfDefinition = getJobDefinition(request, response);
239                startCron();
240                response.setStatus(HttpServletResponse.SC_OK);
241                response.getWriter().write(wfDefinition);
242            }
243            else {
244                throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
245                        RestConstants.JOB_SHOW_PARAM, show);
246            }
247        }
248    
249        /**
250         * abstract method to start a job, either workflow or coordinator
251         *
252         * @param request
253         * @param response
254         * @throws XServletException
255         * @throws IOException TODO
256         */
257        abstract void startJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
258                IOException;
259    
260        /**
261         * abstract method to resume a job, either workflow or coordinator
262         *
263         * @param request
264         * @param response
265         * @throws XServletException
266         * @throws IOException TODO
267         */
268        abstract void resumeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
269                IOException;
270    
271        /**
272         * abstract method to suspend a job, either workflow or coordinator
273         *
274         * @param request
275         * @param response
276         * @throws XServletException
277         * @throws IOException TODO
278         */
279        abstract void suspendJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
280                IOException;
281    
282        /**
283         * abstract method to kill a job, either workflow or coordinator
284         *
285         * @param request
286         * @param response
287         * @throws XServletException
288         * @throws IOException TODO
289         */
290        abstract void killJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
291                IOException;
292    
293        /**
294         * abstract method to change a coordinator job
295         *
296         * @param request
297         * @param response
298         * @throws XServletException
299         * @throws IOException TODO
300         */
301        abstract void changeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
302                IOException;
303    
304        /**
305         * abstract method to re-run a job, either workflow or coordinator
306         *
307         * @param request
308         * @param response
309         * @param conf
310         * @throws XServletException
311         * @throws IOException TODO
312         */
313        abstract JSONObject reRunJob(HttpServletRequest request, HttpServletResponse response, Configuration conf)
314                throws XServletException, IOException;
315    
316        /**
317         * abstract method to get a job, either workflow or coordinator, in JsonBean representation
318         *
319         * @param request
320         * @param response
321         * @return JsonBean representation of a job, either workflow or coordinator
322         * @throws XServletException
323         * @throws IOException TODO
324         * @throws BaseEngineException
325         */
326        abstract JsonBean getJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
327                IOException, BaseEngineException;
328    
329        /**
330         * abstract method to get definition of a job, either workflow or coordinator
331         *
332         * @param request
333         * @param response
334         * @return job, either workflow or coordinator, definition in string format
335         * @throws XServletException
336         * @throws IOException TODO
337         */
338        abstract String getJobDefinition(HttpServletRequest request, HttpServletResponse response)
339                throws XServletException, IOException;
340    
341        /**
342         * abstract method to get and stream log information of job, either workflow or coordinator
343         *
344         * @param request
345         * @param response
346         * @throws XServletException
347         * @throws IOException
348         */
349        abstract void streamJobLog(HttpServletRequest request, HttpServletResponse response) throws XServletException,
350                IOException;
351    
352    }