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  public static final byte[] EMPTY_BYTES = {};
046
047  private static final String SERVICE_PATH = SERVICE_NAME + SERVICE_VERSION;
048
049  /**
050   * Convenience method that creates an HTTP <code>URL</code> for the
051   * HttpFSServer file system operations.
052   * <p/>
053   *
054   * @param path the file path.
055   * @param params the query string parameters.
056   *
057   * @return a <code>URL</code> for the HttpFSServer server,
058   *
059   * @throws IOException thrown if an IO error occurs.
060   */
061  static URL createURL(Path path, Map<String, String> params)
062    throws IOException {
063    return createURL(path, params, null);
064  }
065
066  /**
067   * Convenience method that creates an HTTP <code>URL</code> for the
068   * HttpFSServer file system operations.
069   * <p/>
070   *
071   * @param path the file path.
072   * @param params the query string parameters.
073   * @param multiValuedParams multi valued parameters of the query string
074   *
075   * @return URL a <code>URL</code> for the HttpFSServer server,
076   *
077   * @throws IOException thrown if an IO error occurs.
078   */
079  static URL createURL(Path path, Map<String, String> params, Map<String, 
080      List<String>> multiValuedParams) throws IOException {
081    URI uri = path.toUri();
082    String realScheme;
083    if (uri.getScheme().equalsIgnoreCase(HttpFSFileSystem.SCHEME)) {
084      realScheme = "http";
085    } else if (uri.getScheme().equalsIgnoreCase(HttpsFSFileSystem.SCHEME)) {
086      realScheme = "https";
087
088    } else {
089      throw new IllegalArgumentException(MessageFormat.format(
090        "Invalid scheme [{0}] it should be '" + HttpFSFileSystem.SCHEME + "' " +
091            "or '" + HttpsFSFileSystem.SCHEME + "'", uri));
092    }
093    StringBuilder sb = new StringBuilder();
094    sb.append(realScheme).append("://").append(uri.getAuthority()).
095      append(SERVICE_PATH).append(uri.getPath());
096
097    String separator = "?";
098    for (Map.Entry<String, String> entry : params.entrySet()) {
099      sb.append(separator).append(entry.getKey()).append("=").
100        append(URLEncoder.encode(entry.getValue(), "UTF8"));
101      separator = "&";
102    }
103    if (multiValuedParams != null) {
104      for (Map.Entry<String, List<String>> multiValuedEntry : 
105        multiValuedParams.entrySet()) {
106        String name = URLEncoder.encode(multiValuedEntry.getKey(), "UTF8");
107        List<String> values = multiValuedEntry.getValue();
108        for (String value : values) {
109          sb.append(separator).append(name).append("=").
110            append(URLEncoder.encode(value, "UTF8"));
111          separator = "&";
112        }
113      }
114    }
115    return new URL(sb.toString());
116  }
117
118  /**
119   * Convenience method that JSON Parses the <code>InputStream</code> of a
120   * <code>HttpURLConnection</code>.
121   *
122   * @param conn the <code>HttpURLConnection</code>.
123   *
124   * @return the parsed JSON object.
125   *
126   * @throws IOException thrown if the <code>InputStream</code> could not be
127   * JSON parsed.
128   */
129  static Object jsonParse(HttpURLConnection conn) throws IOException {
130    try {
131      JSONParser parser = new JSONParser();
132      return parser.parse(new InputStreamReader(conn.getInputStream()));
133    } catch (ParseException ex) {
134      throw new IOException("JSON parser error, " + ex.getMessage(), ex);
135    }
136  }
137}