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 {
129                throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
130                        RestConstants.ACTION_PARAM, action);
131            }
132        }
133    
134        /**
135         * Validate the configuration user/group. <p/>
136         *
137         * @param requestUser user in request.
138         * @param conf configuration.
139         * @throws XServletException thrown if the configuration does not have a property {@link
140         * org.apache.oozie.client.OozieClient#USER_NAME}.
141         */
142        static void checkAuthorizationForApp(String requestUser, Configuration conf) throws XServletException {
143            String user = conf.get(OozieClient.USER_NAME);
144            String group = conf.get(OozieClient.GROUP_NAME);
145            try {
146                if (user == null) {
147                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0401, OozieClient.USER_NAME);
148                }
149                if (!requestUser.equals(UNDEF) && !user.equals(requestUser)) {
150                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0400, requestUser, user);
151                }
152                AuthorizationService auth = Services.get().get(AuthorizationService.class);
153                if (group == null) {
154                    group = auth.getDefaultGroup(user);
155                    conf.set(OozieClient.GROUP_NAME, group);
156                }
157                else {
158                    auth.authorizeForGroup(user, group);
159                }
160                XLog.Info.get().setParameter(XLogService.GROUP, group);
161                String wfPath = conf.get(OozieClient.APP_PATH);
162                String coordPath = conf.get(OozieClient.COORDINATOR_APP_PATH);
163                if (wfPath == null && coordPath == null) {
164                    String libPath = conf.get(OozieClient.LIBPATH);
165                    conf.set(OozieClient.APP_PATH, libPath);
166                    wfPath = libPath;
167                }
168                ServletUtilities.ValidateAppPath(wfPath, coordPath);
169    
170                if (wfPath != null) {
171                    auth.authorizeForApp(user, group, wfPath, "workflow.xml", conf);
172                }
173                else {
174                    auth.authorizeForApp(user, group, coordPath, "coordinator.xml", conf);
175                }
176            }
177            catch (AuthorizationException ex) {
178                XLog.getLog(BaseJobServlet.class).info("AuthorizationException ", ex);
179                throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
180            }
181        }
182        
183        /**
184         * Return information about jobs.
185         */
186        @Override
187        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
188            String jobId = getResourceName(request);
189            String show = request.getParameter(RestConstants.JOB_SHOW_PARAM);
190    
191            try {
192                AuthorizationService auth = Services.get().get(AuthorizationService.class);
193                auth.authorizeForJob(getUser(request), jobId, false);
194            }
195            catch (AuthorizationException ex) {
196                throw new XServletException(HttpServletResponse.SC_UNAUTHORIZED, ex);
197            }
198    
199            if (show == null || show.equals(RestConstants.JOB_SHOW_INFO)) {
200                stopCron();
201                JsonBean job = null;
202                try {
203                    job = getJob(request, response);
204                }
205                catch (BaseEngineException e) {
206                    // TODO Auto-generated catch block
207                    // e.printStackTrace();
208    
209                    throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, e);
210                }
211                startCron();
212                sendJsonResponse(response, HttpServletResponse.SC_OK, job);
213            }
214            else if (show.equals(RestConstants.JOB_SHOW_LOG)) {
215                response.setContentType(TEXT_UTF8);
216                streamJobLog(request, response);
217            }
218            else if (show.equals(RestConstants.JOB_SHOW_DEFINITION)) {
219                stopCron();
220                response.setContentType(XML_UTF8);
221                String wfDefinition = getJobDefinition(request, response);
222                startCron();
223                response.setStatus(HttpServletResponse.SC_OK);
224                response.getWriter().write(wfDefinition);
225            }
226            else {
227                throw new XServletException(HttpServletResponse.SC_BAD_REQUEST, ErrorCode.E0303,
228                        RestConstants.JOB_SHOW_PARAM, show);
229            }
230        }
231    
232        /**
233         * abstract method to start a job, either workflow or coordinator
234         *
235         * @param request
236         * @param response
237         * @throws XServletException
238         * @throws IOException TODO
239         */
240        abstract void startJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
241                IOException;
242    
243        /**
244         * abstract method to resume a job, either workflow or coordinator
245         *
246         * @param request
247         * @param response
248         * @throws XServletException
249         * @throws IOException TODO
250         */
251        abstract void resumeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
252                IOException;
253    
254        /**
255         * abstract method to suspend a job, either workflow or coordinator
256         *
257         * @param request
258         * @param response
259         * @throws XServletException
260         * @throws IOException TODO
261         */
262        abstract void suspendJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
263                IOException;
264    
265        /**
266         * abstract method to kill a job, either workflow or coordinator
267         *
268         * @param request
269         * @param response
270         * @throws XServletException
271         * @throws IOException TODO
272         */
273        abstract void killJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
274                IOException;
275    
276        /**
277         * abstract method to change a coordinator job
278         *
279         * @param request
280         * @param response
281         * @throws XServletException
282         * @throws IOException TODO
283         */
284        abstract void changeJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
285                IOException;
286    
287        /**
288         * abstract method to re-run a job, either workflow or coordinator
289         *
290         * @param request
291         * @param response
292         * @param conf
293         * @throws XServletException
294         * @throws IOException TODO
295         */
296        abstract JSONObject reRunJob(HttpServletRequest request, HttpServletResponse response, Configuration conf)
297                throws XServletException, IOException;
298    
299        /**
300         * abstract method to get a job, either workflow or coordinator, in JsonBean representation
301         *
302         * @param request
303         * @param response
304         * @return JsonBean representation of a job, either workflow or coordinator
305         * @throws XServletException
306         * @throws IOException TODO
307         * @throws BaseEngineException
308         */
309        abstract JsonBean getJob(HttpServletRequest request, HttpServletResponse response) throws XServletException,
310                IOException, BaseEngineException;
311    
312        /**
313         * abstract method to get definition of a job, either workflow or coordinator
314         *
315         * @param request
316         * @param response
317         * @return job, either workflow or coordinator, definition in string format
318         * @throws XServletException
319         * @throws IOException TODO
320         */
321        abstract String getJobDefinition(HttpServletRequest request, HttpServletResponse response)
322                throws XServletException, IOException;
323    
324        /**
325         * abstract method to get and stream log information of job, either workflow or coordinator
326         *
327         * @param request
328         * @param response
329         * @throws XServletException
330         * @throws IOException
331         */
332        abstract void streamJobLog(HttpServletRequest request, HttpServletResponse response) throws XServletException,
333                IOException;
334    
335    }