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.parser.JSONParser;
023    import org.json.simple.parser.ParseException;
024    
025    import java.io.IOException;
026    import java.io.InputStreamReader;
027    import java.net.HttpURLConnection;
028    import java.net.URI;
029    import java.net.URL;
030    import java.net.URLEncoder;
031    import java.text.MessageFormat;
032    import java.util.List;
033    import java.util.Map;
034    
035    /**
036     * Utility methods used by HttpFS classes.
037     */
038    @InterfaceAudience.Private
039    public 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    }