001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     * 
010     *      http://www.apache.org/licenses/LICENSE-2.0
011     * 
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.oozie.util;
019    
020    import java.util.Iterator;
021    import java.util.regex.Matcher;
022    import java.util.regex.Pattern;
023    import org.apache.oozie.ErrorCode;
024    import org.jdom.Element;
025    import org.jdom.Namespace;
026    
027    import org.apache.hadoop.conf.Configuration;
028    
029    /**
030     * Utility class to parse and verify the <parameters> section in a workflow or coordinator job
031     */
032    public abstract class ParameterVerifier 
033    {
034        private static final Pattern nsVersionPattern = Pattern.compile("uri:oozie:(workflow|coordinator|bundle):(\\d+.\\d+)");
035        
036        private static final double workflowMinVersion = 0.4;
037        private static final double coordinatorMinVersion = 0.4;
038        private static final double bundleMinVersion = 0.2;
039        
040        /**
041         * Verify the parameters section (if supported in the schema)
042         *
043         * @param conf The job configuration
044         * @param rootElement The root element of the workflow, coordinator, or bundle definition
045         * @throws ParameterVerifierException If required parameters are not defined and have no default values, or if a name is empty
046         */
047        public static void verifyParameters(Configuration conf, Element rootElement) throws ParameterVerifierException {
048            ParamChecker.notNull(conf, "conf");
049            if (rootElement == null) {
050                return;
051            }
052            
053            if (supportsParameters(rootElement.getNamespaceURI())) {
054                Element params = rootElement.getChild("parameters", rootElement.getNamespace());
055                if (params != null) {
056                    int numMissing = 0;
057                    StringBuilder missingParameters = new StringBuilder();
058                    Namespace paramsNs = params.getNamespace();
059                    Iterator<Element> it = params.getChildren("property", paramsNs).iterator();
060                    while (it.hasNext()) {
061                        Element prop = it.next();
062                        String name = prop.getChildTextTrim("name", paramsNs);
063                        if (name != null) {
064                            if (name.isEmpty()) {
065                                throw new ParameterVerifierException(ErrorCode.E0739);
066                            }
067                            if (conf.get(name) == null) {
068                                String defaultValue = prop.getChildTextTrim("value", paramsNs);
069                                if (defaultValue != null) {
070                                    conf.set(name, defaultValue);
071                                } else {
072                                    missingParameters.append(name);
073                                    missingParameters.append(", ");
074                                    numMissing++;
075                                }
076                            }
077                        }
078                    }
079                    if (numMissing > 0) {
080                        missingParameters.setLength(missingParameters.length() - 2);    //remove the trailing ", "
081                        throw new ParameterVerifierException(ErrorCode.E0738, numMissing, missingParameters.toString());
082                    }
083                } else {
084                    // Log a warning when the <parameters> section is missing
085                    XLog.getLog(ParameterVerifier.class).warn("The application does not define formal parameters in its XML "
086                            + "definition");
087                }
088            }
089        }
090        
091        static boolean supportsParameters(String namespaceURI) {
092            boolean supports = false;
093            Matcher m = nsVersionPattern.matcher(namespaceURI);
094            if (m.matches() && m.groupCount() == 2) {
095                String type = m.group(1);
096                double ver = Double.parseDouble(m.group(2));
097                supports = ((type.equals("workflow") && ver >= workflowMinVersion) || 
098                        (type.equals("coordinator") && ver >= coordinatorMinVersion) || 
099                        (type.equals("bundle") && ver >= bundleMinVersion));
100            }
101            return supports;
102        }
103    }