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 org.apache.hadoop.conf.Configuration;
022import org.jdom.Element;
023import org.json.simple.JSONValue;
024
025import java.text.SimpleDateFormat;
026import java.util.Map;
027import java.util.Properties;
028import java.util.TimeZone;
029import java.util.Date;
030import java.net.URLEncoder;
031import java.io.UnsupportedEncodingException;
032
033/**
034 * Base EL constants and functions.
035 */
036public class ELConstantsFunctions {
037
038    /**
039     * KiloByte constant (1024). Defined for EL as 'KB'.
040     */
041    public static final long KB = 1024;
042
043    /**
044     * MegaByte constant (1024 KB). Defined for EL as 'MB'.
045     */
046    public static final long MB = KB * 1024;
047
048    /**
049     * GigaByte constant (1024 MB). Defined for EL as 'GB'.
050     */
051    public static final long GB = MB * 1024;
052
053    /**
054     * TeraByte constant (1024 GB). Defined for EL as 'TB'.
055     */
056    public static final long TB = GB * 1024;
057
058    /**
059     * PetaByte constant (1024 TB). Defined for EL as 'PB'.
060     */
061    public static final long PB = TB * 1024;
062
063    public static final int SUBMIT_MINUTES = 1;
064    public static final int SUBMIT_HOURS = 60;
065    public static final int SUBMIT_DAYS = 24 * 60;
066
067    /**
068     * Return the first not <code>null</code> value, or <code>null</code> if both are <code>null</code>. Defined for EL
069     * as 'Object firstNotNull(Object, Object)'.
070     *
071     * @param o1 first value.
072     * @param o2 second value.
073     * @return the first not <code>null</code> value, or or <code>null</code> if both are <code>null</code>
074     */
075    public static Object firstNotNull(Object o1, Object o2) {
076        return (o1 != null) ? o1 : o2;
077    }
078
079    /**
080     * Return the concatenation of 2 strings. <p> A string with <code>null</code> value is considered as an empty
081     * string.
082     *
083     * @param s1 first string.
084     * @param s2 second string.
085     * @return the concatenation of <code>s1</code> and <code>s2</code>.
086     */
087    public static String concat(String s1, String s2) {
088        StringBuilder sb = new StringBuilder();
089        if (s1 != null) {
090            sb.append(s1);
091        }
092        if (s2 != null) {
093            sb.append(s2);
094        }
095        return sb.toString();
096    }
097
098    /**
099     * Replace each occurrence of regular expression match in the first string
100     * with the <code>replacement</code> string. This EL function utilizes the
101     * java String class replaceAll method. For more details please see
102     *
103     * <code>http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#replaceAll(java.lang.String,%20java.lang.String)</code>
104     *
105     * @param src source string.
106     * @param regex the regular expression to which this string is to be
107     *        matched. null means no replacement.
108     * @param replacement - the string to be substituted for each match. If
109     *        null, it will considered as ""
110     * @return the replaced string.
111     */
112    public static String replaceAll(String src, String regex, String replacement) {
113        if (src != null && regex != null) {
114            if (replacement == null) {
115                replacement = "";
116            }
117            return src.replaceAll(regex, replacement);
118        }
119        return src;
120    }
121
122    /**
123     * Add the <code>append</code> string into each splitted sub-strings of the
124     * first string ('src'). The split is performed into <code>src</code> string
125     * using the <code>delimiter</code>. E.g.
126     * <code>appendAll("/a/b/,/c/b/,/c/d/", "ADD", ",")</code> will return
127     * <code>"/a/b/ADD,/c/b/ADD,/c/d/ADD"</code>
128     *
129     * @param src source string.
130     * @param append - the string to be appended for each match. If null, it
131     *        will considered as ""
132     * @param delimeter the string that is used to split the 'src' into
133     *        substring before the append. null means no append.
134     * @return the appended string.
135     */
136    public static String appendAll(String src, String append, String delimeter) {
137        if (src != null && delimeter != null) {
138            if (append == null) {
139                append = "";
140            }
141            String[] ret = src.split(delimeter);
142            StringBuilder result = new StringBuilder();
143            for (int i = 0; i < ret.length; i++) {
144                result.append(ret[i]).append(append);
145                if (i < (ret.length - 1)) { // Don't append to the last item
146                    result.append(delimeter);
147                }
148            }
149            // Java 8 skips a leading match if it's empty; to remain consistent with Java6,7, we check this case
150            if (src.startsWith(delimeter) && ret.length > 0 && !ret[0].equals("")) {
151                result.insert(0, append);
152            }
153            return result.toString();
154        }
155        return src;
156    }
157
158    /**
159     *
160     * @param input string to be trimmed
161     * @return the trimmed version of the given string or the empty string if the given string was <code>null</code>
162     */
163    public static String trim(String input) {
164        return (input == null) ? "" : input.trim();
165    }
166
167    /**
168     * Return the current datetime in ISO8601 using Oozie processing timezone, yyyy-MM-ddTHH:mmZ. i.e.:
169     * 1997-07-16T19:20Z
170     *
171     * @return the formatted time string.
172     */
173    public static String timestamp() {
174        return DateUtils.formatDateOozieTZ(new Date());
175    }
176
177    /**
178     * Translates a string into <code>application/x-www-form-urlencoded</code> format using UTF-8 encoding scheme. Bytes
179     * for unsafe characters are also obtained using UTF-8 scheme.
180     *
181     * @param input string to be encoded
182     * @return the encoded <code>String</code>
183     */
184    public static String urlEncode(String input) {
185        try {
186            return (input == null) ? "" : URLEncoder.encode(input, "UTF-8");
187        }
188        catch (UnsupportedEncodingException uee) {
189            throw new RuntimeException("It should never happen");
190        }
191    }
192
193    public static String toJsonStr(Map<String, String> map) {
194        String json = JSONValue.toJSONString(map);
195        return XmlUtils.escapeCharsForXML(json);
196    }
197
198    public static String toPropertiesStr(Map<String, String> map) {
199        Properties props = new Properties();
200        for (Map.Entry<String, String> entry: map.entrySet()) {
201            props.setProperty(entry.getKey(), entry.getValue());
202        }
203        return XmlUtils.escapeCharsForXML(PropertiesUtils.propertiesToString(props));
204    }
205
206    public static String toConfigurationStr(Map<String, String> map) {
207        Configuration conf = new Configuration(false);
208        for (Map.Entry<String, String> entry: map.entrySet()) {
209            conf.set(entry.getKey(), entry.getValue());
210        }
211        return XmlUtils.escapeCharsForXML(XmlUtils.prettyPrint(conf).toString());
212    }
213
214}