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