Java tutorial
/* * Copyright 2014 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap.gateway.util; import co.cask.cdap.api.common.Bytes; import com.google.common.base.Charsets; import org.apache.commons.codec.binary.Base64; import org.apache.http.HttpResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; /** * Utility class containing helpers. */ public class Util { private static final Logger LOG = LoggerFactory.getLogger(Util.class); /** * Read the contents of an Http response. * * @param response The Http response * @return the contents as a byte array */ public static byte[] readHttpResponse(HttpResponse response) { byte[] binary; try { if (response.getEntity() == null) { LOG.error("Cannot read from HTTP response because it has no content."); return null; } int length = (int) response.getEntity().getContentLength(); InputStream content = response.getEntity().getContent(); binary = new byte[length]; int offset = 0; while (length > 0) { // must iterate because input stream does not always return all at once int bytesRead = content.read(binary, offset, length); offset += bytesRead; length -= bytesRead; } return binary; } catch (IOException e) { System.err.println("Cannot read from HTTP response: " + e.getMessage()); return null; } } /** * Read the contents of a binary file into a byte array. * * @param filename The name of the file * @return the content of the file if successful, otherwise null */ public static byte[] readBinaryFile(String filename) { File file = new File(filename); if (!file.isFile()) { System.err.println("'" + filename + "' is not a regular file."); return null; } int bytesToRead = (int) file.length(); byte[] bytes = new byte[bytesToRead]; int offset = 0; try { FileInputStream input = new FileInputStream(filename); while (bytesToRead > 0) { int bytesRead = input.read(bytes, offset, bytesToRead); bytesToRead -= bytesRead; offset += bytesRead; } input.close(); return bytes; } catch (FileNotFoundException e) { LOG.error("File '" + filename + "' cannot be opened: " + e.getMessage()); } catch (IOException e) { LOG.error("Error reading from file '" + filename + "': " + e.getMessage()); } return bytes; } /** * Convert a hexadecimal string into a byte array. * * @param hex The string to convert * @return the byte array value of the String * @throws NumberFormatException if the string is ill-formed */ public static byte[] hexValue(String hex) { // verify the length of the string if (hex.length() % 2 != 0) { throw new NumberFormatException("Hex string must have even length."); } byte[] bytes = new byte[hex.length() / 2]; for (int i = 0; i < bytes.length; ++i) { byte hi = hexValue(hex.charAt(2 * i)); byte lo = hexValue(hex.charAt(2 * i + 1)); bytes[i] = (byte) (((hi << 4) & 0xF0) | lo); } return bytes; } /** * Convert a hexadecimal character into a byte. * * @param hex The character to convert * @return the byte value of the character * @throws NumberFormatException if the character is not hexadecimal */ public static byte hexValue(char hex) { if (hex >= '0' && hex <= '9') { return (byte) (hex - '0'); } else if (hex >= 'a' && hex <= 'f') { return (byte) (hex - 'a' + 10); } else if (hex >= 'A' && hex <= 'F') { return (byte) (hex - 'A' + 10); } else { throw new NumberFormatException("'" + hex + "' is not a hexadecimal character."); } } /** * Convert a byte array into its hex string representation. * * @param bytes the byte array to convert * @return A hex string representing the bytes */ public static String toHex(byte[] bytes) { StringBuilder builder = new StringBuilder(bytes.length * 2); for (byte b : bytes) { try { builder.append(Character.forDigit(b >> 4 & 0x0F, 16)); builder.append(Character.forDigit(b & 0x0F, 16)); } catch (IllegalArgumentException e) { // odd, that should never happen e.printStackTrace(); } } return builder.toString(); } /** * decode an URL-encoded string into bytes. */ public static byte[] urlDecode(String str) { try { // we use a base encoding that accepts all byte values return URLDecoder.decode(str, "ISO8859_1").getBytes("ISO8859_1"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; // cant' happen with ISO8859_1 = Latin1 } } /** * URL-encode a binary string. */ public static String urlEncode(byte[] binary) { if (binary == null) { return null; } try { // we use a base encoding that accepts all byte values return URLEncoder.encode(new String(binary, "ISO8859_1"), "ISO8859_1"); } catch (UnsupportedEncodingException e) { // this cannot happen with ISO8859_1 = Latin1 e.printStackTrace(); return null; } } /** * Encode a byte string as a String. * * @param bytes the byte string to encode * @param encoding the encoding to use - "url" for URL-encoded, "hex" for * hexadecimal, or any other valid encoding name * @return the encoded String */ public static String encode(byte[] bytes, String encoding) { if (bytes == null) { return null; } else if (encoding == null) { return urlEncode(bytes); } else if ("url".equals(encoding)) { return urlEncode(bytes); } else if ("hex".equals(encoding)) { return toHex(bytes); } else { try { return new String(bytes, encoding); } catch (UnsupportedEncodingException e) { return urlEncode(bytes); } } } /** * Convert a long value into a big-endian byte array. * * @param value the value to convert * @return the bytes of the value */ public static byte[] longToBytes(long value) { byte[] bytes = new byte[8]; for (int i = 7; i >= 0; i--) { bytes[i] = (byte) (value & 0xff); value = value >> 8; } return bytes; } /** * Convert a big-endian byte-array into a long. This is intended to be used * with 8-byte arrays, but it does not check the length of the array. For * arrays of less than 8 bytes, this will produce the same value as if the * array was left-padded with zeros. For arrays longer than 8 bytes, only * the last 8 bytes are used. * * @param bytes the byte array to convert * @return the long value of the byte array */ public static long bytesToLong(byte[] bytes) { long value = 0; for (byte aByte : bytes) { value = (value << 8) | (((long) aByte) & 0xff); } return value; } //----------------------------------------------------------------------------------- // the following 3 belong together: encodeBinary, decodeBinary, supportedEncoding //----------------------------------------------------------------------------------- public static String encodeBinary(byte[] binary, String encoding) { return encodeBinary(binary, encoding, false); } public static String encodeBinary(byte[] binary, String encoding, boolean counter) { if (counter && Bytes.SIZEOF_LONG == binary.length) { return Long.toString(Bytes.toLong(binary)); } if (encoding == null) { return new String(binary, Charsets.US_ASCII); } if ("hex".equals(encoding)) { return toHex(binary); } if ("base64".equals(encoding)) { return Base64.encodeBase64URLSafeString(binary); } if ("url".equals(encoding)) { try { // URLEncoder does not take byte[], so we convert it into a String with the same code points using // ISO-8859-1. This string only contains code points 0-255. Then we URL-encode that string using // ISO-8859-1 again. This way, every byte ends up as the exact same, %-escaped byte, in the URL string return URLEncoder.encode(new String(binary, Charsets.ISO_8859_1), Charsets.UTF_8.name()); } catch (UnsupportedEncodingException e) { // can't happen throw new RuntimeException("Charsets.ISO_8859_1 is unsupported? Something is wrong with the JVM", e); } } // this can never happen because we only call it with null, hex, base64, or url throw new IllegalArgumentException( "Unexpected encoding: " + encoding + " Only hex, base64 and url are supported."); } public static byte[] decodeBinary(String str, String encoding) throws NumberFormatException { return decodeBinary(str, encoding, false); } public static byte[] decodeBinary(String str, String encoding, boolean counter) throws NumberFormatException { if (counter) { return Bytes.toBytes(Long.parseLong(str)); } if (encoding == null) { return str.getBytes(Charsets.US_ASCII); } if ("hex".equals(encoding)) { return hexValue(str); } if ("base64".equals(encoding)) { return Base64.decodeBase64(str); } if ("url".equals(encoding)) { try { // URLDecoder does not produce byte[], so we decode to a String using ISO-8859. Note that the URL // decoder produces chars between 0-255, and thus each %-escaped byet in the URL results in // exactly one char in the string. That can be safely converted 1:1 into byte[] using getBuytes(). return URLDecoder.decode(str, Charsets.ISO_8859_1.name()).getBytes(Charsets.ISO_8859_1); } catch (UnsupportedEncodingException e) { // can't happen throw new RuntimeException("Charsets.ISO_8859_1 is unsupported? Something is wrong with the JVM", e); } } // this can never happen because we only call it with null, hex, base64, or url throw new IllegalArgumentException( "Unexpected encoding: " + encoding + " Only hex, base64 and url are supported."); } public static boolean supportedEncoding(String enc) { return "hex".equals(enc) || "url".equals(enc) || "base64".equals(enc); } }