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 */
018package org.apache.oozie.util;
019
020import java.util.Date;
021import java.util.List;
022import java.util.TimeZone;
023
024import org.apache.commons.lang.StringUtils;
025import org.quartz.CronExpression;
026import java.text.ParseException;
027
028/**
029 * Utility class to check common parameter preconditions.
030 */
031public class ParamChecker {
032
033    /**
034     * Check that a value is not null. If null throws an IllegalArgumentException.
035     *
036     * @param obj value.
037     * @param name parameter name for the exception message.
038     * @return the given value.
039     */
040    public static <T> T notNull(T obj, String name) {
041        if (obj == null) {
042            throw new IllegalArgumentException(name + " cannot be null");
043        }
044        return obj;
045    }
046
047    /**
048     * Check that a list is not null and that none of its elements is null. If null or if the list has emtpy elements
049     * throws an IllegalArgumentException.
050     *
051     * @param list the list of strings.
052     * @param name parameter name for the exception message.
053     * @return the given list.
054     */
055    public static <T> List<T> notNullElements(List<T> list, String name) {
056        notNull(list, name);
057        for (int i = 0; i < list.size(); i++) {
058            notNull(list.get(i), XLog.format("list [{0}] element [{1}]", name, i));
059        }
060        return list;
061    }
062
063    /**
064     * Check that a string is not null and not empty. If null or emtpy throws an IllegalArgumentException.
065     *
066     * @param str value.
067     * @param name parameter name for the exception message.
068     * @return the given value.
069     */
070    public static String notEmpty(String str, String name) {
071        return notEmpty(str, name, null);
072    }
073
074    /**
075     * Check that a string is not null and not empty. If null or emtpy throws an IllegalArgumentException.
076     *
077     * @param str value.
078     * @param name parameter name for the exception message.
079     * @param info additional information to be printed with the exception message
080     * @return the given value.
081     */
082    public static String notEmpty(String str, String name, String info) {
083        if (str == null) {
084            throw new IllegalArgumentException(name + " cannot be null" + (info == null ? "" : ", " + info));
085        }
086        if (str.length() == 0) {
087            throw new IllegalArgumentException(name + " cannot be empty" + (info == null ? "" : ", " + info));
088        }
089        return str;
090    }
091
092    /**
093     * Check that a list is not null and that none of its elements is null. If null or if the list has emtpy elements
094     * throws an IllegalArgumentException.
095     *
096     * @param list the list of strings.
097     * @param name parameter name for the exception message.
098     * @return the given list.
099     */
100    public static List<String> notEmptyElements(List<String> list, String name) {
101        notNull(list, name);
102        for (int i = 0; i < list.size(); i++) {
103            notEmpty(list.get(i), XLog.format("list [{0}] element [{1}]", name, i));
104        }
105        return list;
106    }
107
108    private static final int MAX_NODE_NAME_LEN = 50;
109
110    /**
111     * Check that the given string is a valid action name [a-zA-Z_][0-9a-zA-Z_\-]* and not longer than 50 chars.
112     *
113     * @param actionName string to validate is a token.
114     * @return the given string.
115     */
116    public static String validateActionName(String actionName) {
117        ParamChecker.notEmpty(actionName, "action name");
118        if (actionName.length() > MAX_NODE_NAME_LEN) {
119            throw new IllegalArgumentException(XLog.format("name [{0}] must be {1} chars or less", actionName,
120                                                           MAX_NODE_NAME_LEN));
121        }
122
123        char c = actionName.charAt(0);
124        if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c == '_')) {
125            throw new IllegalArgumentException(XLog.format("name [{0}], must start with [A-Za-z_]", actionName));
126        }
127        for (int i = 1; i < actionName.length(); i++) {
128            c = actionName.charAt(i);
129            if (!(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z')
130                    && !(c == '_' || c == '-')) {
131                throw new IllegalArgumentException(XLog.format("name [{0}] must be [A-Za-z_][0-9A-Za-z_]*", actionName));
132            }
133        }
134        return actionName;
135    }
136
137    /**
138     * Return if the specified token is a valid Java identifier.
139     *
140     * @param token string to validate if it is a valid Java identifier.
141     * @return if the specified token is a valid Java identifier.
142     */
143    public static boolean isValidIdentifier(String token) {
144        ParamChecker.notEmpty(token, "identifier");
145        for (int i = 0; i < token.length(); i++) {
146            char c = token.charAt(i);
147            if (!(c >= '0' && c <= '9') && !(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c == '_')) {
148                return false;
149            }
150            if (i == 0 && (c >= '0' && c <= '9')) {
151                return false;
152            }
153        }
154        return true;
155    }
156
157    /**
158     * Check whether the value is greater than or equals 0.
159     *
160     * @param value : value to test
161     * @param name : Name of the parameter
162     * @return If the value is > 0, return the value. Otherwise throw IllegalArgumentException
163     */
164    public static int checkGTZero(int value, String name) {
165        if (value <= 0) {
166            throw new IllegalArgumentException(XLog.format("parameter [{0}] = [{1}] must be greater than zero", name,
167                                                           value));
168        }
169        return value;
170    }
171
172    /**
173     * Check whether the value is greater than or equals to 0.
174     *
175     * @param value : value to test
176     * @param name : Name of the parameter
177     * @return If the value is >= 0, return the value. Otherwise throw IllegalArgumentException
178     */
179    public static int checkGEZero(int value, String name) {
180        if (value < 0) {
181            throw new IllegalArgumentException(XLog.format(
182                    "parameter [{0}] = [{1}] must be greater than or equals zero", name, value));
183        }
184        return value;
185    }
186
187    /**
188     * Check whether the value is less than or equal to 0.
189     *
190     * @param value : value to test
191     * @param name : Name of the parameter
192     * @return If the value is <= 0, return the value. Otherwise throw IllegalArgumentException
193     */
194    public static int checkLEZero(int value, String name) {
195        if (value > 0) {
196            throw new IllegalArgumentException(XLog.format(
197                    "parameter [{0}] = [{1}] must be less than or equal to zero", name, value));
198        }
199        return value;
200    }
201
202    /**
203     * Check whether the value is Integer.
204     *
205     * @param value : value to test
206     * @param name : Name of the parameter
207     * @return If the value is integer, return the value. Otherwise throw IllegalArgumentException
208     */
209    public static int checkInteger(String val, String name) {
210        int ret;
211        try {
212            ret = Integer.parseInt(val);
213        }
214        catch (NumberFormatException nex) {
215            throw new IllegalArgumentException(XLog.format(
216                    "parameter [{0}] = [{1}]  must be an integer. Parsing error {2}", name, val, nex.getMessage(), nex));
217        }
218        return ret;
219    }
220
221    /**
222     * Check whether a value is a valid coordinator frequency.
223     *
224     * @param value : value to test
225     * @return If the value is a valid frequency, return the frequency, Otherwise throw IllegalArgumentException
226     */
227    public static String checkFrequency(String val) {
228        try {
229            Integer.parseInt(val);
230        }
231        catch (NumberFormatException ex) {
232            try {
233                // this part is necessary since Quartz
234                // doesn't support for specifying both a day-of-week
235                // and a day-of-month parameter, nor does it support
236                // using "?" in both fields, but we don't want to
237                // expose it to users.
238                String[] cronArray1 = val.split(" ");
239                String[] cronArray2 = val.split(" ");
240
241                if (cronArray1.length != 5) {
242                    throw new IllegalArgumentException(XLog.format(
243                            "parameter [{0}] = [{1}]  must have 5 bit fields. Parsing error {2}", "frequency",
244                            val, ex.getMessage(), ex));
245                }
246
247                if (!cronArray1[4].trim().equals("?")) {
248                    cronArray1[2] = "?";
249                }
250
251                if (!cronArray2[2].trim().equals("?")) {
252                    cronArray2[4] = "?";
253                }
254
255                new CronExpression("0 " + StringUtils.join(cronArray1, " "));
256                new CronExpression("0 " + StringUtils.join(cronArray2, " "));
257            }
258            catch (ParseException pex) {
259                throw new IllegalArgumentException(XLog.format(
260                        "parameter [{0}] = [{1}]  must be an integer or a cron syntax. Parsing error {2}", "frequency",
261                        val, ex.getMessage(), ex));
262            }
263        }
264        return val;
265    }
266    /**
267     * Check whether the value is Oozie processing timezone data format.
268     *
269     * @param value : value to test
270     * @param name : Name of the parameter
271     * @return If the value is in Oozie processing timezone date format, return the value.
272     * Otherwise throw IllegalArgumentException
273     */
274    public static Date checkDateOozieTZ(String date, String name) {
275        Date ret;
276        try {
277            ret = DateUtils.parseDateOozieTZ(date);
278        }
279        catch (Exception ex) {
280            throw new IllegalArgumentException(XLog.format(
281                    "parameter [{0}] = [{1}] must be Date in {2} format ({3})."
282                            + " Parsing error {4}", name, date, DateUtils.getOozieProcessingTimeZone().getID(),
283                    DateUtils.getOozieTimeMask(), ex));
284        }
285        return ret;
286    }
287
288    /**
289     * Check whether the value mention correct Timezone.
290     *
291     * @param value : value to test
292     * @param name : Name of the parameter
293     * @return If the value is correct TZ return the value. Otherwise throw IllegalArgumentException
294     */
295    public static TimeZone checkTimeZone(String tzStr, String name) {
296        TimeZone tz;
297        try {
298            tz = DateUtils.getTimeZone(tzStr);
299        }
300        catch (Exception ex) {
301            throw new IllegalArgumentException(XLog.format("parameter [{0}] = [{1}] must be a valid TZ."
302                    + " Parsing error {2}", name, tzStr, ex.getMessage(), ex));
303        }
304        return tz;
305    }
306
307    /**
308     * Check whether an item is a member of an array of string
309     *
310     * @param item : item to test
311     * @param members : List of items in string
312     * @param name : Name of the parameter
313     * @return If the item is in the member return true. Otherwise throw IllegalArgumentException
314     */
315    public static boolean isMember(String item, String[] members, String name) {
316        for (int i = 0; i < members.length; i++) {
317            if (members[i].equals(item)) {
318                return true;
319            }
320        }
321        // Error case
322        StringBuilder buff = new StringBuilder();
323        for (int i = 0; i < members.length; i++) {
324            buff.append(members[i]).append(", ");
325        }
326        throw new IllegalArgumentException(XLog.format("parameter [{0}] = [{1}] " + "must be in the list {2}", name,
327                                                       item, buff.toString()));
328    }
329}