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 package org.apache.hadoop.fs.http.client; 019 020 import org.apache.hadoop.classification.InterfaceAudience; 021 import org.apache.hadoop.fs.Path; 022 import org.json.simple.JSONObject; 023 import org.json.simple.parser.JSONParser; 024 import org.json.simple.parser.ParseException; 025 026 import java.io.IOException; 027 import java.io.InputStreamReader; 028 import java.lang.reflect.Constructor; 029 import java.net.HttpURLConnection; 030 import java.net.URI; 031 import java.net.URL; 032 import java.net.URLEncoder; 033 import java.text.MessageFormat; 034 import java.util.Map; 035 036 /** 037 * Utility methods used by HttpFS classes. 038 */ 039 @InterfaceAudience.Private 040 public class HttpFSUtils { 041 042 public static final String SERVICE_NAME = "/webhdfs"; 043 044 public static final String SERVICE_VERSION = "/v1"; 045 046 private static final String SERVICE_PATH = SERVICE_NAME + SERVICE_VERSION; 047 048 /** 049 * Convenience method that creates an HTTP <code>URL</code> for the 050 * HttpFSServer file system operations. 051 * <p/> 052 * 053 * @param path the file path. 054 * @param params the query string parameters. 055 * 056 * @return a <code>URL</code> for the HttpFSServer server, 057 * 058 * @throws IOException thrown if an IO error occurrs. 059 */ 060 static URL createHttpURL(Path path, Map<String, String> params) 061 throws IOException { 062 URI uri = path.toUri(); 063 String realScheme; 064 if (uri.getScheme().equalsIgnoreCase(HttpFSFileSystem.SCHEME)) { 065 realScheme = "http"; 066 } else { 067 throw new IllegalArgumentException(MessageFormat.format( 068 "Invalid scheme [{0}] it should be 'webhdfs'", uri)); 069 } 070 StringBuilder sb = new StringBuilder(); 071 sb.append(realScheme).append("://").append(uri.getAuthority()). 072 append(SERVICE_PATH).append(uri.getPath()); 073 074 String separator = "?"; 075 for (Map.Entry<String, String> entry : params.entrySet()) { 076 sb.append(separator).append(entry.getKey()).append("="). 077 append(URLEncoder.encode(entry.getValue(), "UTF8")); 078 separator = "&"; 079 } 080 return new URL(sb.toString()); 081 } 082 083 /** 084 * Validates the status of an <code>HttpURLConnection</code> against an 085 * expected HTTP status code. If the current status code is not the expected 086 * one it throws an exception with a detail message using Server side error 087 * messages if available. 088 * 089 * @param conn the <code>HttpURLConnection</code>. 090 * @param expected the expected HTTP status code. 091 * 092 * @throws IOException thrown if the current status code does not match the 093 * expected one. 094 */ 095 @SuppressWarnings({"unchecked", "deprecation"}) 096 static void validateResponse(HttpURLConnection conn, int expected) 097 throws IOException { 098 int status = conn.getResponseCode(); 099 if (status != expected) { 100 try { 101 JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn); 102 json = (JSONObject) json.get(HttpFSFileSystem.ERROR_JSON); 103 String message = (String) json.get(HttpFSFileSystem.ERROR_MESSAGE_JSON); 104 String exception = (String) 105 json.get(HttpFSFileSystem.ERROR_EXCEPTION_JSON); 106 String className = (String) 107 json.get(HttpFSFileSystem.ERROR_CLASSNAME_JSON); 108 109 try { 110 ClassLoader cl = HttpFSFileSystem.class.getClassLoader(); 111 Class klass = cl.loadClass(className); 112 Constructor constr = klass.getConstructor(String.class); 113 throw (IOException) constr.newInstance(message); 114 } catch (IOException ex) { 115 throw ex; 116 } catch (Exception ex) { 117 throw new IOException(MessageFormat.format("{0} - {1}", exception, 118 message)); 119 } 120 } catch (IOException ex) { 121 if (ex.getCause() instanceof IOException) { 122 throw (IOException) ex.getCause(); 123 } 124 throw new IOException( 125 MessageFormat.format("HTTP status [{0}], {1}", 126 status, conn.getResponseMessage())); 127 } 128 } 129 } 130 131 /** 132 * Convenience method that JSON Parses the <code>InputStream</code> of a 133 * <code>HttpURLConnection</code>. 134 * 135 * @param conn the <code>HttpURLConnection</code>. 136 * 137 * @return the parsed JSON object. 138 * 139 * @throws IOException thrown if the <code>InputStream</code> could not be 140 * JSON parsed. 141 */ 142 static Object jsonParse(HttpURLConnection conn) throws IOException { 143 try { 144 JSONParser parser = new JSONParser(); 145 return parser.parse(new InputStreamReader(conn.getInputStream())); 146 } catch (ParseException ex) { 147 throw new IOException("JSON parser error, " + ex.getMessage(), ex); 148 } 149 } 150 }