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