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