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 */
018package org.apache.hadoop.fs.http.client;
019
020import org.apache.hadoop.classification.InterfaceAudience;
021import org.apache.hadoop.fs.Path;
022import org.json.simple.parser.JSONParser;
023import org.json.simple.parser.ParseException;
024
025import java.io.IOException;
026import java.io.InputStreamReader;
027import java.net.HttpURLConnection;
028import java.net.URI;
029import java.net.URL;
030import java.net.URLEncoder;
031import java.text.MessageFormat;
032import java.util.List;
033import java.util.Map;
034
035/**
036 * Utility methods used by HttpFS classes.
037 */
038@InterfaceAudience.Private
039public class HttpFSUtils {
040
041  public static final String SERVICE_NAME = "/webhdfs";
042
043  public static final String SERVICE_VERSION = "/v1";
044
045  private static final String SERVICE_PATH = SERVICE_NAME + SERVICE_VERSION;
046
047  /**
048   * Convenience method that creates an HTTP <code>URL</code> for the
049   * HttpFSServer file system operations.
050   * <p/>
051   *
052   * @param path the file path.
053   * @param params the query string parameters.
054   *
055   * @return a <code>URL</code> for the HttpFSServer server,
056   *
057   * @throws IOException thrown if an IO error occurs.
058   */
059  static URL createURL(Path path, Map<String, String> params)
060    throws IOException {
061    return createURL(path, params, null);
062  }
063
064  /**
065   * Convenience method that creates an HTTP <code>URL</code> for the
066   * HttpFSServer file system operations.
067   * <p/>
068   *
069   * @param path the file path.
070   * @param params the query string parameters.
071   * @param multiValuedParams multi valued parameters of the query string
072   *
073   * @return URL a <code>URL</code> for the HttpFSServer server,
074   *
075   * @throws IOException thrown if an IO error occurs.
076   */
077  static URL createURL(Path path, Map<String, String> params, Map<String, 
078      List<String>> multiValuedParams) throws IOException {
079    URI uri = path.toUri();
080    String realScheme;
081    if (uri.getScheme().equalsIgnoreCase(HttpFSFileSystem.SCHEME)) {
082      realScheme = "http";
083    } else if (uri.getScheme().equalsIgnoreCase(HttpsFSFileSystem.SCHEME)) {
084      realScheme = "https";
085
086    } else {
087      throw new IllegalArgumentException(MessageFormat.format(
088        "Invalid scheme [{0}] it should be '" + HttpFSFileSystem.SCHEME + "' " +
089            "or '" + HttpsFSFileSystem.SCHEME + "'", uri));
090    }
091    StringBuilder sb = new StringBuilder();
092    sb.append(realScheme).append("://").append(uri.getAuthority()).
093      append(SERVICE_PATH).append(uri.getPath());
094
095    String separator = "?";
096    for (Map.Entry<String, String> entry : params.entrySet()) {
097      sb.append(separator).append(entry.getKey()).append("=").
098        append(URLEncoder.encode(entry.getValue(), "UTF8"));
099      separator = "&";
100    }
101    if (multiValuedParams != null) {
102      for (Map.Entry<String, List<String>> multiValuedEntry : 
103        multiValuedParams.entrySet()) {
104        String name = URLEncoder.encode(multiValuedEntry.getKey(), "UTF8");
105        List<String> values = multiValuedEntry.getValue();
106        for (String value : values) {
107          sb.append(separator).append(name).append("=").
108            append(URLEncoder.encode(value, "UTF8"));
109          separator = "&";
110        }
111      }
112    }
113    return new URL(sb.toString());
114  }
115
116  /**
117   * Convenience method that JSON Parses the <code>InputStream</code> of a
118   * <code>HttpURLConnection</code>.
119   *
120   * @param conn the <code>HttpURLConnection</code>.
121   *
122   * @return the parsed JSON object.
123   *
124   * @throws IOException thrown if the <code>InputStream</code> could not be
125   * JSON parsed.
126   */
127  static Object jsonParse(HttpURLConnection conn) throws IOException {
128    try {
129      JSONParser parser = new JSONParser();
130      return parser.parse(new InputStreamReader(conn.getInputStream()));
131    } catch (ParseException ex) {
132      throw new IOException("JSON parser error, " + ex.getMessage(), ex);
133    }
134  }
135}