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 org.apache.oozie.service.Service;
018    import org.apache.oozie.service.Services;
019    import org.apache.oozie.util.ParamChecker;
020    import org.apache.hadoop.conf.Configuration;
021    
022    import java.io.UnsupportedEncodingException;
023    import java.net.URLDecoder;
024    import java.text.MessageFormat;
025    
026    /**
027     * Service that generates and parses callback URLs.
028     */
029    public class CallbackService implements Service {
030    
031        public static final String CONF_PREFIX = Service.CONF_PREFIX + "CallbackService.";
032    
033        public static final String CONF_BASE_URL = CONF_PREFIX + "base.url";
034    
035        private Configuration oozieConf;
036    
037        /**
038         * Initialize the service.
039         *
040         * @param services services instance.
041         */
042        public void init(Services services) {
043            oozieConf = services.getConf();
044        }
045    
046        /**
047         * Destroy the service.
048         */
049        public void destroy() {
050        }
051    
052        /**
053         * Return the public interface of the Dag engine service.
054         *
055         * @return {@link CallbackService}.
056         */
057        public Class<? extends Service> getInterface() {
058            return CallbackService.class;
059        }
060    
061        private static final String ID_PARAM = "id=";
062        private static final String STATUS_PARAM = "&status=";
063        private static final String CALL_BACK_QUERY_STRING = "{0}?" + ID_PARAM + "{1}" + STATUS_PARAM + "{2}&";
064    
065        /**
066         * Create a callback URL.
067         *
068         * @param actionId action ID for the callback URL.
069         * @param externalStatusVar variable for the caller to inject the external status.
070         * @return the callback URL.
071         */
072        public String createCallBackUrl(String actionId, String externalStatusVar) {
073            ParamChecker.notEmpty(actionId, "actionId");
074            ParamChecker.notEmpty(externalStatusVar, "externalStatusVar");
075            //TODO: figure out why double encoding is happening in case of hadoop callbacks.
076            String baseCallbackUrl = oozieConf.get(CONF_BASE_URL, "http://localhost:8080/oozie/v0/callback");
077            return MessageFormat.format(CALL_BACK_QUERY_STRING, baseCallbackUrl, actionId, externalStatusVar);
078        }
079    
080        private String getParam(String str, String name) {
081            String value = null;
082            int start = str.indexOf(name);
083            if (start > -1) {
084                int end = str.indexOf("&", start + 1);
085                start = start + name.length();
086                value = (end > -1) ? str.substring(start, end) : str.substring(start);
087            }
088            return value;
089        }
090    
091        /**
092         * Return if a callback URL is valid or not.
093         *
094         * @param callback callback URL (it can be just the callback portion of it).
095         * @return <code>true</code> if the callback URL is valid, <code>false</code> if it is not.
096         */
097        public boolean isValid(String callback) {
098            return callback != null && getParam(callback, ID_PARAM) != null && getParam(callback, STATUS_PARAM) != null;
099        }
100    
101        /**
102         * Return the action ID from a callback URL.
103         *
104         * @param callback callback URL (it can be just the callback portion of it).
105         * @return the action ID from a callback URL.
106         */
107        public String getActionId(String callback) {
108            try {
109                return URLDecoder.decode(getParam(ParamChecker.notEmpty(callback, "callback"), ID_PARAM), "UTF-8");
110            }
111            catch (UnsupportedEncodingException ex) {
112                throw new RuntimeException(ex);
113            }
114        }
115    
116        /**
117         * Return the action external status from a callback URL.
118         *
119         * @param callback callback URL (it can be just the callback portion of it).
120         * @return the action external status from a callback URL.
121         */
122        public String getExternalStatus(String callback) {
123            try {
124                return URLDecoder.decode(getParam(ParamChecker.notEmpty(callback, "callback"), STATUS_PARAM), "UTF-8");
125            }
126            catch (UnsupportedEncodingException ex) {
127                throw new RuntimeException(ex);
128            }
129        }
130    
131    }