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 java.util.ArrayList;
021import java.util.EnumSet;
022import java.util.List;
023import org.apache.hadoop.classification.InterfaceAudience;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.fs.ContentSummary;
026import org.apache.hadoop.fs.DelegationTokenRenewer;
027import org.apache.hadoop.fs.FSDataInputStream;
028import org.apache.hadoop.fs.FSDataOutputStream;
029import org.apache.hadoop.fs.FileChecksum;
030import org.apache.hadoop.fs.FileStatus;
031import org.apache.hadoop.fs.FileSystem;
032import org.apache.hadoop.fs.Path;
033import org.apache.hadoop.fs.PositionedReadable;
034import org.apache.hadoop.fs.Seekable;
035import org.apache.hadoop.fs.XAttrCodec;
036import org.apache.hadoop.fs.XAttrSetFlag;
037import org.apache.hadoop.fs.permission.AclEntry;
038import org.apache.hadoop.fs.permission.AclStatus;
039import org.apache.hadoop.fs.permission.FsPermission;
040import org.apache.hadoop.hdfs.DFSConfigKeys;
041import org.apache.hadoop.lib.wsrs.EnumSetParam;
042import org.apache.hadoop.security.UserGroupInformation;
043import org.apache.hadoop.security.token.Token;
044import org.apache.hadoop.security.token.TokenIdentifier;
045import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticatedURL;
046import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticator;
047import org.apache.hadoop.security.token.delegation.web.KerberosDelegationTokenAuthenticator;
048import org.apache.hadoop.util.HttpExceptionUtils;
049import org.apache.hadoop.util.Progressable;
050import org.apache.hadoop.util.ReflectionUtils;
051import org.apache.hadoop.util.StringUtils;
052import org.json.simple.JSONArray;
053import org.json.simple.JSONObject;
054import org.json.simple.parser.JSONParser;
055import org.json.simple.parser.ParseException;
056
057import com.google.common.base.Preconditions;
058import com.google.common.collect.Lists;
059import com.google.common.collect.Maps;
060
061import java.io.BufferedInputStream;
062import java.io.BufferedOutputStream;
063import java.io.DataInput;
064import java.io.DataOutput;
065import java.io.FileNotFoundException;
066import java.io.FilterInputStream;
067import java.io.IOException;
068import java.io.InputStream;
069import java.io.OutputStream;
070import java.net.HttpURLConnection;
071import java.net.URI;
072import java.net.URISyntaxException;
073import java.net.URL;
074import java.security.PrivilegedExceptionAction;
075import java.text.MessageFormat;
076import java.util.HashMap;
077import java.util.Map;
078
079/**
080 * HttpFSServer implementation of the FileSystemAccess FileSystem.
081 * <p/>
082 * This implementation allows a user to access HDFS over HTTP via a HttpFSServer server.
083 */
084@InterfaceAudience.Private
085public class HttpFSFileSystem extends FileSystem
086  implements DelegationTokenRenewer.Renewable {
087
088  public static final String SERVICE_NAME = HttpFSUtils.SERVICE_NAME;
089
090  public static final String SERVICE_VERSION = HttpFSUtils.SERVICE_VERSION;
091
092  public static final String SCHEME = "webhdfs";
093
094  public static final String OP_PARAM = "op";
095  public static final String DO_AS_PARAM = "doas";
096  public static final String OVERWRITE_PARAM = "overwrite";
097  public static final String REPLICATION_PARAM = "replication";
098  public static final String BLOCKSIZE_PARAM = "blocksize";
099  public static final String PERMISSION_PARAM = "permission";
100  public static final String ACLSPEC_PARAM = "aclspec";
101  public static final String DESTINATION_PARAM = "destination";
102  public static final String RECURSIVE_PARAM = "recursive";
103  public static final String SOURCES_PARAM = "sources";
104  public static final String OWNER_PARAM = "owner";
105  public static final String GROUP_PARAM = "group";
106  public static final String MODIFICATION_TIME_PARAM = "modificationtime";
107  public static final String ACCESS_TIME_PARAM = "accesstime";
108  public static final String XATTR_NAME_PARAM = "xattr.name";
109  public static final String XATTR_VALUE_PARAM = "xattr.value";
110  public static final String XATTR_SET_FLAG_PARAM = "flag";
111  public static final String XATTR_ENCODING_PARAM = "encoding";
112
113  public static final Short DEFAULT_PERMISSION = 0755;
114  public static final String ACLSPEC_DEFAULT = "";
115
116  public static final String RENAME_JSON = "boolean";
117
118  public static final String DELETE_JSON = "boolean";
119
120  public static final String MKDIRS_JSON = "boolean";
121
122  public static final String HOME_DIR_JSON = "Path";
123
124  public static final String SET_REPLICATION_JSON = "boolean";
125
126  public static final String UPLOAD_CONTENT_TYPE= "application/octet-stream";
127
128  public static enum FILE_TYPE {
129    FILE, DIRECTORY, SYMLINK;
130
131    public static FILE_TYPE getType(FileStatus fileStatus) {
132      if (fileStatus.isFile()) {
133        return FILE;
134      }
135      if (fileStatus.isDirectory()) {
136        return DIRECTORY;
137      }
138      if (fileStatus.isSymlink()) {
139        return SYMLINK;
140      }
141      throw new IllegalArgumentException("Could not determine filetype for: " +
142                                         fileStatus.getPath());
143    }
144  }
145
146  public static final String FILE_STATUSES_JSON = "FileStatuses";
147  public static final String FILE_STATUS_JSON = "FileStatus";
148  public static final String PATH_SUFFIX_JSON = "pathSuffix";
149  public static final String TYPE_JSON = "type";
150  public static final String LENGTH_JSON = "length";
151  public static final String OWNER_JSON = "owner";
152  public static final String GROUP_JSON = "group";
153  public static final String PERMISSION_JSON = "permission";
154  public static final String ACCESS_TIME_JSON = "accessTime";
155  public static final String MODIFICATION_TIME_JSON = "modificationTime";
156  public static final String BLOCK_SIZE_JSON = "blockSize";
157  public static final String REPLICATION_JSON = "replication";
158  public static final String XATTRS_JSON = "XAttrs";
159  public static final String XATTR_NAME_JSON = "name";
160  public static final String XATTR_VALUE_JSON = "value";
161  public static final String XATTRNAMES_JSON = "XAttrNames";
162
163  public static final String FILE_CHECKSUM_JSON = "FileChecksum";
164  public static final String CHECKSUM_ALGORITHM_JSON = "algorithm";
165  public static final String CHECKSUM_BYTES_JSON = "bytes";
166  public static final String CHECKSUM_LENGTH_JSON = "length";
167
168  public static final String CONTENT_SUMMARY_JSON = "ContentSummary";
169  public static final String CONTENT_SUMMARY_DIRECTORY_COUNT_JSON = "directoryCount";
170  public static final String CONTENT_SUMMARY_FILE_COUNT_JSON = "fileCount";
171  public static final String CONTENT_SUMMARY_LENGTH_JSON = "length";
172  public static final String CONTENT_SUMMARY_QUOTA_JSON = "quota";
173  public static final String CONTENT_SUMMARY_SPACE_CONSUMED_JSON = "spaceConsumed";
174  public static final String CONTENT_SUMMARY_SPACE_QUOTA_JSON = "spaceQuota";
175
176  public static final String ACL_STATUS_JSON = "AclStatus";
177  public static final String ACL_STICKY_BIT_JSON = "stickyBit";
178  public static final String ACL_ENTRIES_JSON = "entries";
179  public static final String ACL_BIT_JSON = "aclBit";
180
181  public static final int HTTP_TEMPORARY_REDIRECT = 307;
182
183  private static final String HTTP_GET = "GET";
184  private static final String HTTP_PUT = "PUT";
185  private static final String HTTP_POST = "POST";
186  private static final String HTTP_DELETE = "DELETE";
187
188  @InterfaceAudience.Private
189  public static enum Operation {
190    OPEN(HTTP_GET), GETFILESTATUS(HTTP_GET), LISTSTATUS(HTTP_GET),
191    GETHOMEDIRECTORY(HTTP_GET), GETCONTENTSUMMARY(HTTP_GET),
192    GETFILECHECKSUM(HTTP_GET),  GETFILEBLOCKLOCATIONS(HTTP_GET),
193    INSTRUMENTATION(HTTP_GET), GETACLSTATUS(HTTP_GET),
194    APPEND(HTTP_POST), CONCAT(HTTP_POST),
195    CREATE(HTTP_PUT), MKDIRS(HTTP_PUT), RENAME(HTTP_PUT), SETOWNER(HTTP_PUT),
196    SETPERMISSION(HTTP_PUT), SETREPLICATION(HTTP_PUT), SETTIMES(HTTP_PUT),
197    MODIFYACLENTRIES(HTTP_PUT), REMOVEACLENTRIES(HTTP_PUT),
198    REMOVEDEFAULTACL(HTTP_PUT), REMOVEACL(HTTP_PUT), SETACL(HTTP_PUT),
199    DELETE(HTTP_DELETE), SETXATTR(HTTP_PUT), GETXATTRS(HTTP_GET),
200    REMOVEXATTR(HTTP_PUT), LISTXATTRS(HTTP_GET);
201
202    private String httpMethod;
203
204    Operation(String httpMethod) {
205      this.httpMethod = httpMethod;
206    }
207
208    public String getMethod() {
209      return httpMethod;
210    }
211
212  }
213
214  private DelegationTokenAuthenticatedURL authURL;
215  private DelegationTokenAuthenticatedURL.Token authToken =
216      new DelegationTokenAuthenticatedURL.Token();
217  private URI uri;
218  private Path workingDir;
219  private UserGroupInformation realUser;
220
221
222
223  /**
224   * Convenience method that creates a <code>HttpURLConnection</code> for the
225   * HttpFSServer file system operations.
226   * <p/>
227   * This methods performs and injects any needed authentication credentials
228   * via the {@link #getConnection(URL, String)} method
229   *
230   * @param method the HTTP method.
231   * @param params the query string parameters.
232   * @param path the file path
233   * @param makeQualified if the path should be 'makeQualified'
234   *
235   * @return a <code>HttpURLConnection</code> for the HttpFSServer server,
236   *         authenticated and ready to use for the specified path and file system operation.
237   *
238   * @throws IOException thrown if an IO error occurrs.
239   */
240  private HttpURLConnection getConnection(final String method,
241      Map<String, String> params, Path path, boolean makeQualified)
242      throws IOException {
243    return getConnection(method, params, null, path, makeQualified);
244  }
245
246  /**
247   * Convenience method that creates a <code>HttpURLConnection</code> for the
248   * HttpFSServer file system operations.
249   * <p/>
250   * This methods performs and injects any needed authentication credentials
251   * via the {@link #getConnection(URL, String)} method
252   *
253   * @param method the HTTP method.
254   * @param params the query string parameters.
255   * @param multiValuedParams multi valued parameters of the query string
256   * @param path the file path
257   * @param makeQualified if the path should be 'makeQualified'
258   *
259   * @return HttpURLConnection a <code>HttpURLConnection</code> for the
260   *         HttpFSServer server, authenticated and ready to use for the
261   *         specified path and file system operation.
262   *
263   * @throws IOException thrown if an IO error occurrs.
264   */
265  private HttpURLConnection getConnection(final String method,
266      Map<String, String> params, Map<String, List<String>> multiValuedParams,
267      Path path, boolean makeQualified) throws IOException {
268    if (makeQualified) {
269      path = makeQualified(path);
270    }
271    final URL url = HttpFSUtils.createURL(path, params, multiValuedParams);
272    try {
273      return UserGroupInformation.getCurrentUser().doAs(
274          new PrivilegedExceptionAction<HttpURLConnection>() {
275            @Override
276            public HttpURLConnection run() throws Exception {
277              return getConnection(url, method);
278            }
279          }
280      );
281    } catch (Exception ex) {
282      if (ex instanceof IOException) {
283        throw (IOException) ex;
284      } else {
285        throw new IOException(ex);
286      }
287    }
288  }
289
290  /**
291   * Convenience method that creates a <code>HttpURLConnection</code> for the specified URL.
292   * <p/>
293   * This methods performs and injects any needed authentication credentials.
294   *
295   * @param url url to connect to.
296   * @param method the HTTP method.
297   *
298   * @return a <code>HttpURLConnection</code> for the HttpFSServer server, authenticated and ready to use for
299   *         the specified path and file system operation.
300   *
301   * @throws IOException thrown if an IO error occurrs.
302   */
303  private HttpURLConnection getConnection(URL url, String method) throws IOException {
304    try {
305      HttpURLConnection conn = authURL.openConnection(url, authToken);
306      conn.setRequestMethod(method);
307      if (method.equals(HTTP_POST) || method.equals(HTTP_PUT)) {
308        conn.setDoOutput(true);
309      }
310      return conn;
311    } catch (Exception ex) {
312      throw new IOException(ex);
313    }
314  }
315
316  /**
317   * Called after a new FileSystem instance is constructed.
318   *
319   * @param name a uri whose authority section names the host, port, etc. for this FileSystem
320   * @param conf the configuration
321   */
322  @Override
323  public void initialize(URI name, Configuration conf) throws IOException {
324    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
325
326    //the real use is the one that has the Kerberos credentials needed for
327    //SPNEGO to work
328    realUser = ugi.getRealUser();
329    if (realUser == null) {
330      realUser = UserGroupInformation.getLoginUser();
331    }
332    super.initialize(name, conf);
333    try {
334      uri = new URI(name.getScheme() + "://" + name.getAuthority());
335    } catch (URISyntaxException ex) {
336      throw new IOException(ex);
337    }
338
339    Class<? extends DelegationTokenAuthenticator> klass =
340        getConf().getClass("httpfs.authenticator.class",
341            KerberosDelegationTokenAuthenticator.class,
342            DelegationTokenAuthenticator.class);
343    DelegationTokenAuthenticator authenticator =
344        ReflectionUtils.newInstance(klass, getConf());
345    authURL = new DelegationTokenAuthenticatedURL(authenticator);
346  }
347
348  @Override
349  public String getScheme() {
350    return SCHEME;
351  }
352
353  /**
354   * Returns a URI whose scheme and authority identify this FileSystem.
355   *
356   * @return the URI whose scheme and authority identify this FileSystem.
357   */
358  @Override
359  public URI getUri() {
360    return uri;
361  }
362
363  /**
364   * Get the default port for this file system.
365   * @return the default port or 0 if there isn't one
366   */
367  @Override
368  protected int getDefaultPort() {
369    return getConf().getInt(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_KEY,
370        DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT);
371  }
372
373  /**
374   * HttpFSServer subclass of the <code>FSDataInputStream</code>.
375   * <p/>
376   * This implementation does not support the
377   * <code>PositionReadable</code> and <code>Seekable</code> methods.
378   */
379  private static class HttpFSDataInputStream extends FilterInputStream implements Seekable, PositionedReadable {
380
381    protected HttpFSDataInputStream(InputStream in, int bufferSize) {
382      super(new BufferedInputStream(in, bufferSize));
383    }
384
385    @Override
386    public int read(long position, byte[] buffer, int offset, int length) throws IOException {
387      throw new UnsupportedOperationException();
388    }
389
390    @Override
391    public void readFully(long position, byte[] buffer, int offset, int length) throws IOException {
392      throw new UnsupportedOperationException();
393    }
394
395    @Override
396    public void readFully(long position, byte[] buffer) throws IOException {
397      throw new UnsupportedOperationException();
398    }
399
400    @Override
401    public void seek(long pos) throws IOException {
402      throw new UnsupportedOperationException();
403    }
404
405    @Override
406    public long getPos() throws IOException {
407      throw new UnsupportedOperationException();
408    }
409
410    @Override
411    public boolean seekToNewSource(long targetPos) throws IOException {
412      throw new UnsupportedOperationException();
413    }
414  }
415
416  /**
417   * Opens an FSDataInputStream at the indicated Path.
418   * </p>
419   * IMPORTANT: the returned <code><FSDataInputStream/code> does not support the
420   * <code>PositionReadable</code> and <code>Seekable</code> methods.
421   *
422   * @param f the file name to open
423   * @param bufferSize the size of the buffer to be used.
424   */
425  @Override
426  public FSDataInputStream open(Path f, int bufferSize) throws IOException {
427    Map<String, String> params = new HashMap<String, String>();
428    params.put(OP_PARAM, Operation.OPEN.toString());
429    HttpURLConnection conn = getConnection(Operation.OPEN.getMethod(), params,
430                                           f, true);
431    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
432    return new FSDataInputStream(
433      new HttpFSDataInputStream(conn.getInputStream(), bufferSize));
434  }
435
436  /**
437   * HttpFSServer subclass of the <code>FSDataOutputStream</code>.
438   * <p/>
439   * This implementation closes the underlying HTTP connection validating the Http connection status
440   * at closing time.
441   */
442  private static class HttpFSDataOutputStream extends FSDataOutputStream {
443    private HttpURLConnection conn;
444    private int closeStatus;
445
446    public HttpFSDataOutputStream(HttpURLConnection conn, OutputStream out, int closeStatus, Statistics stats)
447      throws IOException {
448      super(out, stats);
449      this.conn = conn;
450      this.closeStatus = closeStatus;
451    }
452
453    @Override
454    public void close() throws IOException {
455      try {
456        super.close();
457      } finally {
458        HttpExceptionUtils.validateResponse(conn, closeStatus);
459      }
460    }
461
462  }
463
464  /**
465   * Converts a <code>FsPermission</code> to a Unix octal representation.
466   *
467   * @param p the permission.
468   *
469   * @return the Unix string symbolic reprentation.
470   */
471  public static String permissionToString(FsPermission p) {
472    return  Integer.toString((p == null) ? DEFAULT_PERMISSION : p.toShort(), 8);
473  }
474
475  /*
476   * Common handling for uploading data for create and append operations.
477   */
478  private FSDataOutputStream uploadData(String method, Path f, Map<String, String> params,
479                                        int bufferSize, int expectedStatus) throws IOException {
480    HttpURLConnection conn = getConnection(method, params, f, true);
481    conn.setInstanceFollowRedirects(false);
482    boolean exceptionAlreadyHandled = false;
483    try {
484      if (conn.getResponseCode() == HTTP_TEMPORARY_REDIRECT) {
485        exceptionAlreadyHandled = true;
486        String location = conn.getHeaderField("Location");
487        if (location != null) {
488          conn = getConnection(new URL(location), method);
489          conn.setRequestProperty("Content-Type", UPLOAD_CONTENT_TYPE);
490          try {
491            OutputStream os = new BufferedOutputStream(conn.getOutputStream(), bufferSize);
492            return new HttpFSDataOutputStream(conn, os, expectedStatus, statistics);
493          } catch (IOException ex) {
494            HttpExceptionUtils.validateResponse(conn, expectedStatus);
495            throw ex;
496          }
497        } else {
498          HttpExceptionUtils.validateResponse(conn, HTTP_TEMPORARY_REDIRECT);
499          throw new IOException("Missing HTTP 'Location' header for [" + conn.getURL() + "]");
500        }
501      } else {
502        throw new IOException(
503          MessageFormat.format("Expected HTTP status was [307], received [{0}]",
504                               conn.getResponseCode()));
505      }
506    } catch (IOException ex) {
507      if (exceptionAlreadyHandled) {
508        throw ex;
509      } else {
510        HttpExceptionUtils.validateResponse(conn, HTTP_TEMPORARY_REDIRECT);
511        throw ex;
512      }
513    }
514  }
515
516
517  /**
518   * Opens an FSDataOutputStream at the indicated Path with write-progress
519   * reporting.
520   * <p/>
521   * IMPORTANT: The <code>Progressable</code> parameter is not used.
522   *
523   * @param f the file name to open.
524   * @param permission file permission.
525   * @param overwrite if a file with this name already exists, then if true,
526   * the file will be overwritten, and if false an error will be thrown.
527   * @param bufferSize the size of the buffer to be used.
528   * @param replication required block replication for the file.
529   * @param blockSize block size.
530   * @param progress progressable.
531   *
532   * @throws IOException
533   * @see #setPermission(Path, FsPermission)
534   */
535  @Override
536  public FSDataOutputStream create(Path f, FsPermission permission,
537                                   boolean overwrite, int bufferSize,
538                                   short replication, long blockSize,
539                                   Progressable progress) throws IOException {
540    Map<String, String> params = new HashMap<String, String>();
541    params.put(OP_PARAM, Operation.CREATE.toString());
542    params.put(OVERWRITE_PARAM, Boolean.toString(overwrite));
543    params.put(REPLICATION_PARAM, Short.toString(replication));
544    params.put(BLOCKSIZE_PARAM, Long.toString(blockSize));
545    params.put(PERMISSION_PARAM, permissionToString(permission));
546    return uploadData(Operation.CREATE.getMethod(), f, params, bufferSize,
547                      HttpURLConnection.HTTP_CREATED);
548  }
549
550
551  /**
552   * Append to an existing file (optional operation).
553   * <p/>
554   * IMPORTANT: The <code>Progressable</code> parameter is not used.
555   *
556   * @param f the existing file to be appended.
557   * @param bufferSize the size of the buffer to be used.
558   * @param progress for reporting progress if it is not null.
559   *
560   * @throws IOException
561   */
562  @Override
563  public FSDataOutputStream append(Path f, int bufferSize,
564                                   Progressable progress) throws IOException {
565    Map<String, String> params = new HashMap<String, String>();
566    params.put(OP_PARAM, Operation.APPEND.toString());
567    return uploadData(Operation.APPEND.getMethod(), f, params, bufferSize,
568                      HttpURLConnection.HTTP_OK);
569  }
570
571  /**
572   * Concat existing files together.
573   * @param f the path to the target destination.
574   * @param psrcs the paths to the sources to use for the concatenation.
575   *
576   * @throws IOException
577   */
578  @Override
579  public void concat(Path f, Path[] psrcs) throws IOException {
580    List<String> strPaths = new ArrayList<String>(psrcs.length);
581    for(Path psrc : psrcs) {
582      strPaths.add(psrc.toUri().getPath());
583    }
584    String srcs = StringUtils.join(",", strPaths);
585
586    Map<String, String> params = new HashMap<String, String>();
587    params.put(OP_PARAM, Operation.CONCAT.toString());
588    params.put(SOURCES_PARAM, srcs);
589    HttpURLConnection conn = getConnection(Operation.CONCAT.getMethod(),
590        params, f, true);
591    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
592  }
593
594  /**
595   * Renames Path src to Path dst.  Can take place on local fs
596   * or remote DFS.
597   */
598  @Override
599  public boolean rename(Path src, Path dst) throws IOException {
600    Map<String, String> params = new HashMap<String, String>();
601    params.put(OP_PARAM, Operation.RENAME.toString());
602    params.put(DESTINATION_PARAM, dst.toString());
603    HttpURLConnection conn = getConnection(Operation.RENAME.getMethod(),
604                                           params, src, true);
605    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
606    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
607    return (Boolean) json.get(RENAME_JSON);
608  }
609
610  /**
611   * Delete a file.
612   *
613   * @deprecated Use delete(Path, boolean) instead
614   */
615  @Deprecated
616  @Override
617  public boolean delete(Path f) throws IOException {
618    return delete(f, false);
619  }
620
621  /**
622   * Delete a file.
623   *
624   * @param f the path to delete.
625   * @param recursive if path is a directory and set to
626   * true, the directory is deleted else throws an exception. In
627   * case of a file the recursive can be set to either true or false.
628   *
629   * @return true if delete is successful else false.
630   *
631   * @throws IOException
632   */
633  @Override
634  public boolean delete(Path f, boolean recursive) throws IOException {
635    Map<String, String> params = new HashMap<String, String>();
636    params.put(OP_PARAM, Operation.DELETE.toString());
637    params.put(RECURSIVE_PARAM, Boolean.toString(recursive));
638    HttpURLConnection conn = getConnection(Operation.DELETE.getMethod(),
639                                           params, f, true);
640    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
641    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
642    return (Boolean) json.get(DELETE_JSON);
643  }
644
645  /**
646   * List the statuses of the files/directories in the given path if the path is
647   * a directory.
648   *
649   * @param f given path
650   *
651   * @return the statuses of the files/directories in the given patch
652   *
653   * @throws IOException
654   */
655  @Override
656  public FileStatus[] listStatus(Path f) throws IOException {
657    Map<String, String> params = new HashMap<String, String>();
658    params.put(OP_PARAM, Operation.LISTSTATUS.toString());
659    HttpURLConnection conn = getConnection(Operation.LISTSTATUS.getMethod(),
660                                           params, f, true);
661    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
662    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
663    json = (JSONObject) json.get(FILE_STATUSES_JSON);
664    JSONArray jsonArray = (JSONArray) json.get(FILE_STATUS_JSON);
665    FileStatus[] array = new FileStatus[jsonArray.size()];
666    f = makeQualified(f);
667    for (int i = 0; i < jsonArray.size(); i++) {
668      array[i] = createFileStatus(f, (JSONObject) jsonArray.get(i));
669    }
670    return array;
671  }
672
673  /**
674   * Set the current working directory for the given file system. All relative
675   * paths will be resolved relative to it.
676   *
677   * @param newDir new directory.
678   */
679  @Override
680  public void setWorkingDirectory(Path newDir) {
681    workingDir = newDir;
682  }
683
684  /**
685   * Get the current working directory for the given file system
686   *
687   * @return the directory pathname
688   */
689  @Override
690  public Path getWorkingDirectory() {
691    if (workingDir == null) {
692      workingDir = getHomeDirectory();
693    }
694    return workingDir;
695  }
696
697  /**
698   * Make the given file and all non-existent parents into
699   * directories. Has the semantics of Unix 'mkdir -p'.
700   * Existence of the directory hierarchy is not an error.
701   */
702  @Override
703  public boolean mkdirs(Path f, FsPermission permission) throws IOException {
704    Map<String, String> params = new HashMap<String, String>();
705    params.put(OP_PARAM, Operation.MKDIRS.toString());
706    params.put(PERMISSION_PARAM, permissionToString(permission));
707    HttpURLConnection conn = getConnection(Operation.MKDIRS.getMethod(),
708                                           params, f, true);
709    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
710    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
711    return (Boolean) json.get(MKDIRS_JSON);
712  }
713
714  /**
715   * Return a file status object that represents the path.
716   *
717   * @param f The path we want information from
718   *
719   * @return a FileStatus object
720   *
721   * @throws FileNotFoundException when the path does not exist;
722   * IOException see specific implementation
723   */
724  @Override
725  public FileStatus getFileStatus(Path f) throws IOException {
726    Map<String, String> params = new HashMap<String, String>();
727    params.put(OP_PARAM, Operation.GETFILESTATUS.toString());
728    HttpURLConnection conn = getConnection(Operation.GETFILESTATUS.getMethod(),
729                                           params, f, true);
730    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
731    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
732    json = (JSONObject) json.get(FILE_STATUS_JSON);
733    f = makeQualified(f);
734    return createFileStatus(f, json);
735  }
736
737  /**
738   * Return the current user's home directory in this filesystem.
739   * The default implementation returns "/user/$USER/".
740   */
741  @Override
742  public Path getHomeDirectory() {
743    Map<String, String> params = new HashMap<String, String>();
744    params.put(OP_PARAM, Operation.GETHOMEDIRECTORY.toString());
745    try {
746      HttpURLConnection conn =
747        getConnection(Operation.GETHOMEDIRECTORY.getMethod(), params,
748                      new Path(getUri().toString(), "/"), false);
749      HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
750      JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
751      return new Path((String) json.get(HOME_DIR_JSON));
752    } catch (IOException ex) {
753      throw new RuntimeException(ex);
754    }
755  }
756
757  /**
758   * Set owner of a path (i.e. a file or a directory).
759   * The parameters username and groupname cannot both be null.
760   *
761   * @param p The path
762   * @param username If it is null, the original username remains unchanged.
763   * @param groupname If it is null, the original groupname remains unchanged.
764   */
765  @Override
766  public void setOwner(Path p, String username, String groupname)
767    throws IOException {
768    Map<String, String> params = new HashMap<String, String>();
769    params.put(OP_PARAM, Operation.SETOWNER.toString());
770    params.put(OWNER_PARAM, username);
771    params.put(GROUP_PARAM, groupname);
772    HttpURLConnection conn = getConnection(Operation.SETOWNER.getMethod(),
773                                           params, p, true);
774    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
775  }
776
777  /**
778   * Set permission of a path.
779   *
780   * @param p path.
781   * @param permission permission.
782   */
783  @Override
784  public void setPermission(Path p, FsPermission permission) throws IOException {
785    Map<String, String> params = new HashMap<String, String>();
786    params.put(OP_PARAM, Operation.SETPERMISSION.toString());
787    params.put(PERMISSION_PARAM, permissionToString(permission));
788    HttpURLConnection conn = getConnection(Operation.SETPERMISSION.getMethod(), params, p, true);
789    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
790  }
791
792  /**
793   * Set access time of a file
794   *
795   * @param p The path
796   * @param mtime Set the modification time of this file.
797   * The number of milliseconds since Jan 1, 1970.
798   * A value of -1 means that this call should not set modification time.
799   * @param atime Set the access time of this file.
800   * The number of milliseconds since Jan 1, 1970.
801   * A value of -1 means that this call should not set access time.
802   */
803  @Override
804  public void setTimes(Path p, long mtime, long atime) throws IOException {
805    Map<String, String> params = new HashMap<String, String>();
806    params.put(OP_PARAM, Operation.SETTIMES.toString());
807    params.put(MODIFICATION_TIME_PARAM, Long.toString(mtime));
808    params.put(ACCESS_TIME_PARAM, Long.toString(atime));
809    HttpURLConnection conn = getConnection(Operation.SETTIMES.getMethod(),
810                                           params, p, true);
811    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
812  }
813
814  /**
815   * Set replication for an existing file.
816   *
817   * @param src file name
818   * @param replication new replication
819   *
820   * @return true if successful;
821   *         false if file does not exist or is a directory
822   *
823   * @throws IOException
824   */
825  @Override
826  public boolean setReplication(Path src, short replication)
827    throws IOException {
828    Map<String, String> params = new HashMap<String, String>();
829    params.put(OP_PARAM, Operation.SETREPLICATION.toString());
830    params.put(REPLICATION_PARAM, Short.toString(replication));
831    HttpURLConnection conn =
832      getConnection(Operation.SETREPLICATION.getMethod(), params, src, true);
833    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
834    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
835    return (Boolean) json.get(SET_REPLICATION_JSON);
836  }
837
838  /**
839   * Modify the ACL entries for a file.
840   *
841   * @param path Path to modify
842   * @param aclSpec List<AclEntry> describing modifications
843   * @throws IOException
844   */
845  @Override
846  public void modifyAclEntries(Path path, List<AclEntry> aclSpec)
847          throws IOException {
848    Map<String, String> params = new HashMap<String, String>();
849    params.put(OP_PARAM, Operation.MODIFYACLENTRIES.toString());
850    params.put(ACLSPEC_PARAM, AclEntry.aclSpecToString(aclSpec));
851    HttpURLConnection conn = getConnection(
852            Operation.MODIFYACLENTRIES.getMethod(), params, path, true);
853    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
854  }
855
856  /**
857   * Remove the specified ACL entries from a file
858   * @param path Path to modify
859   * @param aclSpec List<AclEntry> describing entries to remove
860   * @throws IOException
861   */
862  @Override
863  public void removeAclEntries(Path path, List<AclEntry> aclSpec)
864          throws IOException {
865    Map<String, String> params = new HashMap<String, String>();
866    params.put(OP_PARAM, Operation.REMOVEACLENTRIES.toString());
867    params.put(ACLSPEC_PARAM, AclEntry.aclSpecToString(aclSpec));
868    HttpURLConnection conn = getConnection(
869            Operation.REMOVEACLENTRIES.getMethod(), params, path, true);
870    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
871  }
872
873  /**
874   * Removes the default ACL for the given file
875   * @param path Path from which to remove the default ACL.
876   * @throws IOException
877   */
878  @Override
879  public void removeDefaultAcl(Path path) throws IOException {
880    Map<String, String> params = new HashMap<String, String>();
881    params.put(OP_PARAM, Operation.REMOVEDEFAULTACL.toString());
882    HttpURLConnection conn = getConnection(
883            Operation.REMOVEDEFAULTACL.getMethod(), params, path, true);
884    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
885  }
886
887  /**
888   * Remove all ACLs from a file
889   * @param path Path from which to remove all ACLs
890   * @throws IOException
891   */
892  @Override
893  public void removeAcl(Path path) throws IOException {
894    Map<String, String> params = new HashMap<String, String>();
895    params.put(OP_PARAM, Operation.REMOVEACL.toString());
896    HttpURLConnection conn = getConnection(Operation.REMOVEACL.getMethod(),
897            params, path, true);
898    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
899  }
900
901  /**
902   * Set the ACLs for the given file
903   * @param path Path to modify
904   * @param aclSpec List<AclEntry> describing modifications, must include
905   *                entries for user, group, and others for compatibility
906   *                with permission bits.
907   * @throws IOException
908   */
909  @Override
910  public void setAcl(Path path, List<AclEntry> aclSpec) throws IOException {
911    Map<String, String> params = new HashMap<String, String>();
912    params.put(OP_PARAM, Operation.SETACL.toString());
913    params.put(ACLSPEC_PARAM, AclEntry.aclSpecToString(aclSpec));
914    HttpURLConnection conn = getConnection(Operation.SETACL.getMethod(),
915                                           params, path, true);
916    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
917  }
918
919  /**
920   * Get the ACL information for a given file
921   * @param path Path to acquire ACL info for
922   * @return the ACL information in JSON format
923   * @throws IOException
924   */
925  @Override
926  public AclStatus getAclStatus(Path path) throws IOException {
927    Map<String, String> params = new HashMap<String, String>();
928    params.put(OP_PARAM, Operation.GETACLSTATUS.toString());
929    HttpURLConnection conn = getConnection(Operation.GETACLSTATUS.getMethod(),
930            params, path, true);
931    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
932    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
933    json = (JSONObject) json.get(ACL_STATUS_JSON);
934    return createAclStatus(json);
935  }
936
937  private FileStatus createFileStatus(Path parent, JSONObject json) {
938    String pathSuffix = (String) json.get(PATH_SUFFIX_JSON);
939    Path path = (pathSuffix.equals("")) ? parent : new Path(parent, pathSuffix);
940    FILE_TYPE type = FILE_TYPE.valueOf((String) json.get(TYPE_JSON));
941    long len = (Long) json.get(LENGTH_JSON);
942    String owner = (String) json.get(OWNER_JSON);
943    String group = (String) json.get(GROUP_JSON);
944    FsPermission permission =
945      new FsPermission(Short.parseShort((String) json.get(PERMISSION_JSON), 8));
946    long aTime = (Long) json.get(ACCESS_TIME_JSON);
947    long mTime = (Long) json.get(MODIFICATION_TIME_JSON);
948    long blockSize = (Long) json.get(BLOCK_SIZE_JSON);
949    short replication = ((Long) json.get(REPLICATION_JSON)).shortValue();
950    FileStatus fileStatus = null;
951
952    switch (type) {
953      case FILE:
954      case DIRECTORY:
955        fileStatus = new FileStatus(len, (type == FILE_TYPE.DIRECTORY),
956                                    replication, blockSize, mTime, aTime,
957                                    permission, owner, group, path);
958        break;
959      case SYMLINK:
960        Path symLink = null;
961        fileStatus = new FileStatus(len, false,
962                                    replication, blockSize, mTime, aTime,
963                                    permission, owner, group, symLink,
964                                    path);
965    }
966    return fileStatus;
967  }
968
969  /**
970   * Convert the given JSON object into an AclStatus
971   * @param json Input JSON representing the ACLs
972   * @return Resulting AclStatus
973   */
974  private AclStatus createAclStatus(JSONObject json) {
975    AclStatus.Builder aclStatusBuilder = new AclStatus.Builder()
976            .owner((String) json.get(OWNER_JSON))
977            .group((String) json.get(GROUP_JSON))
978            .stickyBit((Boolean) json.get(ACL_STICKY_BIT_JSON));
979    JSONArray entries = (JSONArray) json.get(ACL_ENTRIES_JSON);
980    for ( Object e : entries ) {
981      aclStatusBuilder.addEntry(AclEntry.parseAclEntry(e.toString(), true));
982    }
983    return aclStatusBuilder.build();
984  }
985
986  @Override
987  public ContentSummary getContentSummary(Path f) throws IOException {
988    Map<String, String> params = new HashMap<String, String>();
989    params.put(OP_PARAM, Operation.GETCONTENTSUMMARY.toString());
990    HttpURLConnection conn =
991      getConnection(Operation.GETCONTENTSUMMARY.getMethod(), params, f, true);
992    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
993    JSONObject json = (JSONObject) ((JSONObject)
994      HttpFSUtils.jsonParse(conn)).get(CONTENT_SUMMARY_JSON);
995    return new ContentSummary((Long) json.get(CONTENT_SUMMARY_LENGTH_JSON),
996                              (Long) json.get(CONTENT_SUMMARY_FILE_COUNT_JSON),
997                              (Long) json.get(CONTENT_SUMMARY_DIRECTORY_COUNT_JSON),
998                              (Long) json.get(CONTENT_SUMMARY_QUOTA_JSON),
999                              (Long) json.get(CONTENT_SUMMARY_SPACE_CONSUMED_JSON),
1000                              (Long) json.get(CONTENT_SUMMARY_SPACE_QUOTA_JSON)
1001    );
1002  }
1003
1004  @Override
1005  public FileChecksum getFileChecksum(Path f) throws IOException {
1006    Map<String, String> params = new HashMap<String, String>();
1007    params.put(OP_PARAM, Operation.GETFILECHECKSUM.toString());
1008    HttpURLConnection conn =
1009      getConnection(Operation.GETFILECHECKSUM.getMethod(), params, f, true);
1010    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1011    final JSONObject json = (JSONObject) ((JSONObject)
1012      HttpFSUtils.jsonParse(conn)).get(FILE_CHECKSUM_JSON);
1013    return new FileChecksum() {
1014      @Override
1015      public String getAlgorithmName() {
1016        return (String) json.get(CHECKSUM_ALGORITHM_JSON);
1017      }
1018
1019      @Override
1020      public int getLength() {
1021        return ((Long) json.get(CHECKSUM_LENGTH_JSON)).intValue();
1022      }
1023
1024      @Override
1025      public byte[] getBytes() {
1026        return StringUtils.hexStringToByte((String) json.get(CHECKSUM_BYTES_JSON));
1027      }
1028
1029      @Override
1030      public void write(DataOutput out) throws IOException {
1031        throw new UnsupportedOperationException();
1032      }
1033
1034      @Override
1035      public void readFields(DataInput in) throws IOException {
1036        throw new UnsupportedOperationException();
1037      }
1038    };
1039  }
1040
1041
1042  @Override
1043  public Token<?> getDelegationToken(final String renewer)
1044    throws IOException {
1045    try {
1046      return UserGroupInformation.getCurrentUser().doAs(
1047          new PrivilegedExceptionAction<Token<?>>() {
1048            @Override
1049            public Token<?> run() throws Exception {
1050              return authURL.getDelegationToken(uri.toURL(), authToken,
1051                  renewer);
1052            }
1053          }
1054      );
1055    } catch (Exception ex) {
1056      if (ex instanceof IOException) {
1057        throw (IOException) ex;
1058      } else {
1059        throw new IOException(ex);
1060      }
1061    }
1062  }
1063
1064  public long renewDelegationToken(final Token<?> token) throws IOException {
1065    try {
1066      return UserGroupInformation.getCurrentUser().doAs(
1067          new PrivilegedExceptionAction<Long>() {
1068            @Override
1069            public Long run() throws Exception {
1070              return authURL.renewDelegationToken(uri.toURL(), authToken);
1071            }
1072          }
1073      );
1074    } catch (Exception ex) {
1075      if (ex instanceof IOException) {
1076        throw (IOException) ex;
1077      } else {
1078        throw new IOException(ex);
1079      }
1080    }
1081  }
1082
1083  public void cancelDelegationToken(final Token<?> token) throws IOException {
1084    authURL.cancelDelegationToken(uri.toURL(), authToken);
1085  }
1086
1087  @Override
1088  public Token<?> getRenewToken() {
1089    return null; //TODO : for renewer
1090  }
1091
1092  @Override
1093  @SuppressWarnings("unchecked")
1094  public <T extends TokenIdentifier> void setDelegationToken(Token<T> token) {
1095    //TODO : for renewer
1096  }
1097
1098  @Override
1099  public void setXAttr(Path f, String name, byte[] value,
1100      EnumSet<XAttrSetFlag> flag) throws IOException {
1101    Map<String, String> params = new HashMap<String, String>();
1102    params.put(OP_PARAM, Operation.SETXATTR.toString());
1103    params.put(XATTR_NAME_PARAM, name);
1104    if (value != null) {
1105      params.put(XATTR_VALUE_PARAM, 
1106          XAttrCodec.encodeValue(value, XAttrCodec.HEX));
1107    }
1108    params.put(XATTR_SET_FLAG_PARAM, EnumSetParam.toString(flag));
1109    HttpURLConnection conn = getConnection(Operation.SETXATTR.getMethod(),
1110        params, f, true);
1111    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1112  }
1113
1114  @Override
1115  public byte[] getXAttr(Path f, String name) throws IOException {
1116    Map<String, String> params = new HashMap<String, String>();
1117    params.put(OP_PARAM, Operation.GETXATTRS.toString());
1118    params.put(XATTR_NAME_PARAM, name);
1119    HttpURLConnection conn = getConnection(Operation.GETXATTRS.getMethod(),
1120        params, f, true);
1121    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1122    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
1123    Map<String, byte[]> xAttrs = createXAttrMap(
1124        (JSONArray) json.get(XATTRS_JSON));
1125    return xAttrs != null ? xAttrs.get(name) : null;
1126  }
1127
1128  /** Convert xAttrs json to xAttrs map */
1129  private Map<String, byte[]> createXAttrMap(JSONArray jsonArray) 
1130      throws IOException {
1131    Map<String, byte[]> xAttrs = Maps.newHashMap();
1132    for (Object obj : jsonArray) {
1133      JSONObject jsonObj = (JSONObject) obj;
1134      final String name = (String)jsonObj.get(XATTR_NAME_JSON);
1135      final byte[] value = XAttrCodec.decodeValue(
1136          (String)jsonObj.get(XATTR_VALUE_JSON));
1137      xAttrs.put(name, value);
1138    }
1139
1140    return xAttrs;
1141  }
1142
1143  /** Convert xAttr names json to names list */
1144  private List<String> createXAttrNames(String xattrNamesStr) throws IOException {
1145    JSONParser parser = new JSONParser();
1146    JSONArray jsonArray;
1147    try {
1148      jsonArray = (JSONArray)parser.parse(xattrNamesStr);
1149      List<String> names = Lists.newArrayListWithCapacity(jsonArray.size());
1150      for (Object name : jsonArray) {
1151        names.add((String) name);
1152      }
1153      return names;
1154    } catch (ParseException e) {
1155      throw new IOException("JSON parser error, " + e.getMessage(), e);
1156    }
1157  }
1158
1159  @Override
1160  public Map<String, byte[]> getXAttrs(Path f) throws IOException {
1161    Map<String, String> params = new HashMap<String, String>();
1162    params.put(OP_PARAM, Operation.GETXATTRS.toString());
1163    HttpURLConnection conn = getConnection(Operation.GETXATTRS.getMethod(),
1164        params, f, true);
1165    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1166    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
1167    return createXAttrMap((JSONArray) json.get(XATTRS_JSON));
1168  }
1169
1170  @Override
1171  public Map<String, byte[]> getXAttrs(Path f, List<String> names)
1172      throws IOException {
1173    Preconditions.checkArgument(names != null && !names.isEmpty(), 
1174        "XAttr names cannot be null or empty.");
1175    Map<String, String> params = new HashMap<String, String>();
1176    params.put(OP_PARAM, Operation.GETXATTRS.toString());
1177    Map<String, List<String>> multiValuedParams = Maps.newHashMap();
1178    multiValuedParams.put(XATTR_NAME_PARAM, names);
1179    HttpURLConnection conn = getConnection(Operation.GETXATTRS.getMethod(),
1180        params, multiValuedParams, f, true);
1181    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1182    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
1183    return createXAttrMap((JSONArray) json.get(XATTRS_JSON));
1184  }
1185
1186  @Override
1187  public List<String> listXAttrs(Path f) throws IOException {
1188    Map<String, String> params = new HashMap<String, String>();
1189    params.put(OP_PARAM, Operation.LISTXATTRS.toString());
1190    HttpURLConnection conn = getConnection(Operation.LISTXATTRS.getMethod(),
1191        params, f, true);
1192    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1193    JSONObject json = (JSONObject) HttpFSUtils.jsonParse(conn);
1194    return createXAttrNames((String) json.get(XATTRNAMES_JSON));
1195  }
1196
1197  @Override
1198  public void removeXAttr(Path f, String name) throws IOException {
1199    Map<String, String> params = new HashMap<String, String>();
1200    params.put(OP_PARAM, Operation.REMOVEXATTR.toString());
1201    params.put(XATTR_NAME_PARAM, name);
1202    HttpURLConnection conn = getConnection(Operation.REMOVEXATTR.getMethod(),
1203        params, f, true);
1204    HttpExceptionUtils.validateResponse(conn, HttpURLConnection.HTTP_OK);
1205  }
1206}