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