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.hdfs.web;
019
020import org.apache.hadoop.fs.*;
021import org.apache.hadoop.fs.permission.AclEntry;
022import org.apache.hadoop.fs.permission.AclStatus;
023import org.apache.hadoop.fs.permission.FsPermission;
024import org.apache.hadoop.hdfs.DFSUtil;
025import org.apache.hadoop.hdfs.protocol.*;
026import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates;
027import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
028import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
029import org.apache.hadoop.hdfs.server.namenode.INodeId;
030import org.apache.hadoop.ipc.RemoteException;
031import org.apache.hadoop.security.token.Token;
032import org.apache.hadoop.security.token.TokenIdentifier;
033import org.apache.hadoop.util.DataChecksum;
034import org.apache.hadoop.util.StringUtils;
035import org.mortbay.util.ajax.JSON;
036
037import java.io.ByteArrayInputStream;
038import java.io.DataInputStream;
039import java.io.IOException;
040import java.util.*;
041
042/** JSON Utilities */
043public class JsonUtil {
044  private static final Object[] EMPTY_OBJECT_ARRAY = {};
045  private static final DatanodeInfo[] EMPTY_DATANODE_INFO_ARRAY = {};
046
047  /** Convert a token object to a Json string. */
048  public static String toJsonString(final Token<? extends TokenIdentifier> token
049      ) throws IOException {
050    return toJsonString(Token.class, toJsonMap(token));
051  }
052
053  private static Map<String, Object> toJsonMap(
054      final Token<? extends TokenIdentifier> token) throws IOException {
055    if (token == null) {
056      return null;
057    }
058
059    final Map<String, Object> m = new TreeMap<String, Object>();
060    m.put("urlString", token.encodeToUrlString());
061    return m;
062  }
063
064  /** Convert a Json map to a Token. */
065  public static Token<? extends TokenIdentifier> toToken(
066      final Map<?, ?> m) throws IOException {
067    if (m == null) {
068      return null;
069    }
070
071    final Token<DelegationTokenIdentifier> token
072        = new Token<DelegationTokenIdentifier>();
073    token.decodeFromUrlString((String)m.get("urlString"));
074    return token;
075  }
076
077  /** Convert a Json map to a Token of DelegationTokenIdentifier. */
078  @SuppressWarnings("unchecked")
079  public static Token<DelegationTokenIdentifier> toDelegationToken(
080      final Map<?, ?> json) throws IOException {
081    final Map<?, ?> m = (Map<?, ?>)json.get(Token.class.getSimpleName());
082    return (Token<DelegationTokenIdentifier>)toToken(m);
083  }
084
085  /** Convert a Json map to a Token of BlockTokenIdentifier. */
086  @SuppressWarnings("unchecked")
087  private static Token<BlockTokenIdentifier> toBlockToken(
088      final Map<?, ?> m) throws IOException {
089    return (Token<BlockTokenIdentifier>)toToken(m);
090  }
091
092  /** Convert a Token[] to a JSON array. */
093  private static Object[] toJsonArray(final Token<? extends TokenIdentifier>[] array
094      ) throws IOException {
095    if (array == null) {
096      return null;
097    } else if (array.length == 0) {
098      return EMPTY_OBJECT_ARRAY;
099    } else {
100      final Object[] a = new Object[array.length];
101      for(int i = 0; i < array.length; i++) {
102        a[i] = toJsonMap(array[i]);
103      }
104      return a;
105    }
106  }
107
108  /** Convert a token object to a JSON string. */
109  public static String toJsonString(final Token<? extends TokenIdentifier>[] tokens
110      ) throws IOException {
111    if (tokens == null) {
112      return null;
113    }
114
115    final Map<String, Object> m = new TreeMap<String, Object>();
116    m.put(Token.class.getSimpleName(), toJsonArray(tokens));
117    return toJsonString(Token.class.getSimpleName() + "s", m);
118  }
119
120  /** Convert an Object[] to a List<Token<?>>.  */
121  private static List<Token<?>> toTokenList(final Object[] objects) throws IOException {
122    if (objects == null) {
123      return null;
124    } else if (objects.length == 0) {
125      return Collections.emptyList();
126    } else {
127      final List<Token<?>> list = new ArrayList<Token<?>>(objects.length);
128      for(int i = 0; i < objects.length; i++) {
129        list.add(toToken((Map<?, ?>)objects[i]));
130      }
131      return list;
132    }
133  }
134
135  /** Convert a JSON map to a List<Token<?>>. */
136  public static List<Token<?>> toTokenList(final Map<?, ?> json) throws IOException {
137    if (json == null) {
138      return null;
139    }
140
141    final Map<?, ?> m = (Map<?, ?>)json.get(Token.class.getSimpleName() + "s");
142    return toTokenList((Object[])m.get(Token.class.getSimpleName()));
143  }
144
145  /** Convert an exception object to a Json string. */
146  public static String toJsonString(final Exception e) {
147    final Map<String, Object> m = new TreeMap<String, Object>();
148    m.put("exception", e.getClass().getSimpleName());
149    m.put("message", e.getMessage());
150    m.put("javaClassName", e.getClass().getName());
151    return toJsonString(RemoteException.class, m);
152  }
153
154  /** Convert a Json map to a RemoteException. */
155  public static RemoteException toRemoteException(final Map<?, ?> json) {
156    final Map<?, ?> m = (Map<?, ?>)json.get(RemoteException.class.getSimpleName());
157    final String message = (String)m.get("message");
158    final String javaClassName = (String)m.get("javaClassName");
159    return new RemoteException(javaClassName, message);
160  }
161
162  private static String toJsonString(final Class<?> clazz, final Object value) {
163    return toJsonString(clazz.getSimpleName(), value);
164  }
165
166  /** Convert a key-value pair to a Json string. */
167  public static String toJsonString(final String key, final Object value) {
168    final Map<String, Object> m = new TreeMap<String, Object>();
169    m.put(key, value);
170    return JSON.toString(m);
171  }
172
173  /** Convert a FsPermission object to a string. */
174  private static String toString(final FsPermission permission) {
175    return String.format("%o", permission.toShort());
176  }
177
178  /** Convert a string to a FsPermission object. */
179  private static FsPermission toFsPermission(final String s, Boolean aclBit) {
180    FsPermission perm = new FsPermission(Short.parseShort(s, 8));
181    return (aclBit != null && aclBit) ? new FsAclPermission(perm) : perm;
182  }
183
184  static enum PathType {
185    FILE, DIRECTORY, SYMLINK;
186    
187    static PathType valueOf(HdfsFileStatus status) {
188      return status.isDir()? DIRECTORY: status.isSymlink()? SYMLINK: FILE;
189    }
190  }
191
192  /** Convert a HdfsFileStatus object to a Json string. */
193  public static String toJsonString(final HdfsFileStatus status,
194      boolean includeType) {
195    if (status == null) {
196      return null;
197    }
198    final Map<String, Object> m = new TreeMap<String, Object>();
199    m.put("pathSuffix", status.getLocalName());
200    m.put("type", PathType.valueOf(status));
201    if (status.isSymlink()) {
202      m.put("symlink", status.getSymlink());
203    }
204
205    m.put("length", status.getLen());
206    m.put("owner", status.getOwner());
207    m.put("group", status.getGroup());
208    FsPermission perm = status.getPermission();
209    m.put("permission", toString(perm));
210    if (perm.getAclBit()) {
211      m.put("aclBit", true);
212    }
213    m.put("accessTime", status.getAccessTime());
214    m.put("modificationTime", status.getModificationTime());
215    m.put("blockSize", status.getBlockSize());
216    m.put("replication", status.getReplication());
217    m.put("fileId", status.getFileId());
218    m.put("childrenNum", status.getChildrenNum());
219    return includeType ? toJsonString(FileStatus.class, m): JSON.toString(m);
220  }
221
222  /** Convert a Json map to a HdfsFileStatus object. */
223  public static HdfsFileStatus toFileStatus(final Map<?, ?> json, boolean includesType) {
224    if (json == null) {
225      return null;
226    }
227
228    final Map<?, ?> m = includesType ? 
229        (Map<?, ?>)json.get(FileStatus.class.getSimpleName()) : json;
230    final String localName = (String) m.get("pathSuffix");
231    final PathType type = PathType.valueOf((String) m.get("type"));
232    final byte[] symlink = type != PathType.SYMLINK? null
233        : DFSUtil.string2Bytes((String)m.get("symlink"));
234
235    final long len = (Long) m.get("length");
236    final String owner = (String) m.get("owner");
237    final String group = (String) m.get("group");
238    final FsPermission permission = toFsPermission((String) m.get("permission"),
239      (Boolean)m.get("aclBit"));
240    final long aTime = (Long) m.get("accessTime");
241    final long mTime = (Long) m.get("modificationTime");
242    final long blockSize = (Long) m.get("blockSize");
243    final short replication = (short) (long) (Long) m.get("replication");
244    final long fileId = m.containsKey("fileId") ? (Long) m.get("fileId")
245        : INodeId.GRANDFATHER_INODE_ID;
246    Long childrenNumLong = (Long) m.get("childrenNum");
247    final int childrenNum = (childrenNumLong == null) ? -1
248            : childrenNumLong.intValue();
249    return new HdfsFileStatus(len, type == PathType.DIRECTORY, replication,
250        blockSize, mTime, aTime, permission, owner, group,
251        symlink, DFSUtil.string2Bytes(localName), fileId, childrenNum);
252  }
253
254  /** Convert an ExtendedBlock to a Json map. */
255  private static Map<String, Object> toJsonMap(final ExtendedBlock extendedblock) {
256    if (extendedblock == null) {
257      return null;
258    }
259
260    final Map<String, Object> m = new TreeMap<String, Object>();
261    m.put("blockPoolId", extendedblock.getBlockPoolId());
262    m.put("blockId", extendedblock.getBlockId());
263    m.put("numBytes", extendedblock.getNumBytes());
264    m.put("generationStamp", extendedblock.getGenerationStamp());
265    return m;
266  }
267
268  /** Convert a Json map to an ExtendedBlock object. */
269  private static ExtendedBlock toExtendedBlock(final Map<?, ?> m) {
270    if (m == null) {
271      return null;
272    }
273    
274    final String blockPoolId = (String)m.get("blockPoolId");
275    final long blockId = (Long)m.get("blockId");
276    final long numBytes = (Long)m.get("numBytes");
277    final long generationStamp = (Long)m.get("generationStamp");
278    return new ExtendedBlock(blockPoolId, blockId, numBytes, generationStamp);
279  }
280  
281  /** Convert a DatanodeInfo to a Json map. */
282  static Map<String, Object> toJsonMap(final DatanodeInfo datanodeinfo) {
283    if (datanodeinfo == null) {
284      return null;
285    }
286
287    // TODO: Fix storageID
288    final Map<String, Object> m = new TreeMap<String, Object>();
289    m.put("ipAddr", datanodeinfo.getIpAddr());
290    // 'name' is equivalent to ipAddr:xferPort. Older clients (1.x, 0.23.x) 
291    // expects this instead of the two fields.
292    m.put("name", datanodeinfo.getXferAddr());
293    m.put("hostName", datanodeinfo.getHostName());
294    m.put("storageID", datanodeinfo.getDatanodeUuid());
295    m.put("xferPort", datanodeinfo.getXferPort());
296    m.put("infoPort", datanodeinfo.getInfoPort());
297    m.put("infoSecurePort", datanodeinfo.getInfoSecurePort());
298    m.put("ipcPort", datanodeinfo.getIpcPort());
299
300    m.put("capacity", datanodeinfo.getCapacity());
301    m.put("dfsUsed", datanodeinfo.getDfsUsed());
302    m.put("remaining", datanodeinfo.getRemaining());
303    m.put("blockPoolUsed", datanodeinfo.getBlockPoolUsed());
304    m.put("cacheCapacity", datanodeinfo.getCacheCapacity());
305    m.put("cacheUsed", datanodeinfo.getCacheUsed());
306    m.put("lastUpdate", datanodeinfo.getLastUpdate());
307    m.put("xceiverCount", datanodeinfo.getXceiverCount());
308    m.put("networkLocation", datanodeinfo.getNetworkLocation());
309    m.put("adminState", datanodeinfo.getAdminState().name());
310    return m;
311  }
312
313  private static int getInt(Map<?, ?> m, String key, final int defaultValue) {
314    Object value = m.get(key);
315    if (value == null) {
316      return defaultValue;
317    }
318    return (int) (long) (Long) value;
319  }
320
321  private static long getLong(Map<?, ?> m, String key, final long defaultValue) {
322    Object value = m.get(key);
323    if (value == null) {
324      return defaultValue;
325    }
326    return (long) (Long) value;
327  }
328
329  private static String getString(Map<?, ?> m, String key,
330      final String defaultValue) {
331    Object value = m.get(key);
332    if (value == null) {
333      return defaultValue;
334    }
335    return (String) value;
336  }
337
338  /** Convert a Json map to an DatanodeInfo object. */
339  static DatanodeInfo toDatanodeInfo(final Map<?, ?> m)
340      throws IOException {
341    if (m == null) {
342      return null;
343    }
344
345    // ipAddr and xferPort are the critical fields for accessing data.
346    // If any one of the two is missing, an exception needs to be thrown.
347
348    // Handle the case of old servers (1.x, 0.23.x) sending 'name' instead
349    // of ipAddr and xferPort.
350    Object tmpValue = m.get("ipAddr");
351    String ipAddr = (tmpValue == null) ? null : (String)tmpValue;
352    tmpValue = m.get("xferPort");
353    int xferPort = (tmpValue == null) ? -1 : (int)(long)(Long)tmpValue;
354    if (ipAddr == null) {
355      tmpValue = m.get("name");
356      if (tmpValue != null) {
357        String name = (String)tmpValue;
358        int colonIdx = name.indexOf(':');
359        if (colonIdx > 0) {
360          ipAddr = name.substring(0, colonIdx);
361          xferPort = Integer.parseInt(name.substring(colonIdx +1));
362        } else {
363          throw new IOException(
364              "Invalid value in server response: name=[" + name + "]");
365        }
366      } else {
367        throw new IOException(
368            "Missing both 'ipAddr' and 'name' in server response.");
369      }
370      // ipAddr is non-null & non-empty string at this point.
371    }
372
373    // Check the validity of xferPort.
374    if (xferPort == -1) {
375      throw new IOException(
376          "Invalid or missing 'xferPort' in server response.");
377    }
378
379    // TODO: Fix storageID
380    return new DatanodeInfo(
381        ipAddr,
382        (String)m.get("hostName"),
383        (String)m.get("storageID"),
384        xferPort,
385        (int)(long)(Long)m.get("infoPort"),
386        getInt(m, "infoSecurePort", 0),
387        (int)(long)(Long)m.get("ipcPort"),
388
389        getLong(m, "capacity", 0l),
390        getLong(m, "dfsUsed", 0l),
391        getLong(m, "remaining", 0l),
392        getLong(m, "blockPoolUsed", 0l),
393        getLong(m, "cacheCapacity", 0l),
394        getLong(m, "cacheUsed", 0l),
395        getLong(m, "lastUpdate", 0l),
396        getInt(m, "xceiverCount", 0),
397        getString(m, "networkLocation", ""),
398        AdminStates.valueOf(getString(m, "adminState", "NORMAL")));
399  }
400
401  /** Convert a DatanodeInfo[] to a Json array. */
402  private static Object[] toJsonArray(final DatanodeInfo[] array) {
403    if (array == null) {
404      return null;
405    } else if (array.length == 0) {
406      return EMPTY_OBJECT_ARRAY;
407    } else {
408      final Object[] a = new Object[array.length];
409      for(int i = 0; i < array.length; i++) {
410        a[i] = toJsonMap(array[i]);
411      }
412      return a;
413    }
414  }
415
416  /** Convert an Object[] to a DatanodeInfo[]. */
417  private static DatanodeInfo[] toDatanodeInfoArray(final Object[] objects) 
418      throws IOException {
419    if (objects == null) {
420      return null;
421    } else if (objects.length == 0) {
422      return EMPTY_DATANODE_INFO_ARRAY;
423    } else {
424      final DatanodeInfo[] array = new DatanodeInfo[objects.length];
425      for(int i = 0; i < array.length; i++) {
426        array[i] = toDatanodeInfo((Map<?, ?>) objects[i]);
427      }
428      return array;
429    }
430  }
431  
432  /** Convert a LocatedBlock to a Json map. */
433  private static Map<String, Object> toJsonMap(final LocatedBlock locatedblock
434      ) throws IOException {
435    if (locatedblock == null) {
436      return null;
437    }
438 
439    final Map<String, Object> m = new TreeMap<String, Object>();
440    m.put("blockToken", toJsonMap(locatedblock.getBlockToken()));
441    m.put("isCorrupt", locatedblock.isCorrupt());
442    m.put("startOffset", locatedblock.getStartOffset());
443    m.put("block", toJsonMap(locatedblock.getBlock()));
444    m.put("locations", toJsonArray(locatedblock.getLocations()));
445    m.put("cachedLocations", toJsonArray(locatedblock.getCachedLocations()));
446    return m;
447  }
448
449  /** Convert a Json map to LocatedBlock. */
450  private static LocatedBlock toLocatedBlock(final Map<?, ?> m) throws IOException {
451    if (m == null) {
452      return null;
453    }
454
455    final ExtendedBlock b = toExtendedBlock((Map<?, ?>)m.get("block"));
456    final DatanodeInfo[] locations = toDatanodeInfoArray(
457        (Object[])m.get("locations"));
458    final long startOffset = (Long)m.get("startOffset");
459    final boolean isCorrupt = (Boolean)m.get("isCorrupt");
460    final DatanodeInfo[] cachedLocations = toDatanodeInfoArray(
461        (Object[])m.get("cachedLocations"));
462
463    final LocatedBlock locatedblock = new LocatedBlock(b, locations,
464        null, null, startOffset, isCorrupt, cachedLocations);
465    locatedblock.setBlockToken(toBlockToken((Map<?, ?>)m.get("blockToken")));
466    return locatedblock;
467  }
468
469  /** Convert a LocatedBlock[] to a Json array. */
470  private static Object[] toJsonArray(final List<LocatedBlock> array
471      ) throws IOException {
472    if (array == null) {
473      return null;
474    } else if (array.size() == 0) {
475      return EMPTY_OBJECT_ARRAY;
476    } else {
477      final Object[] a = new Object[array.size()];
478      for(int i = 0; i < array.size(); i++) {
479        a[i] = toJsonMap(array.get(i));
480      }
481      return a;
482    }
483  }
484
485  /** Convert an Object[] to a List of LocatedBlock. */
486  private static List<LocatedBlock> toLocatedBlockList(final Object[] objects
487      ) throws IOException {
488    if (objects == null) {
489      return null;
490    } else if (objects.length == 0) {
491      return Collections.emptyList();
492    } else {
493      final List<LocatedBlock> list = new ArrayList<LocatedBlock>(objects.length);
494      for(int i = 0; i < objects.length; i++) {
495        list.add(toLocatedBlock((Map<?, ?>)objects[i]));
496      }
497      return list;
498    }
499  }
500
501  /** Convert LocatedBlocks to a Json string. */
502  public static String toJsonString(final LocatedBlocks locatedblocks
503      ) throws IOException {
504    if (locatedblocks == null) {
505      return null;
506    }
507
508    final Map<String, Object> m = new TreeMap<String, Object>();
509    m.put("fileLength", locatedblocks.getFileLength());
510    m.put("isUnderConstruction", locatedblocks.isUnderConstruction());
511
512    m.put("locatedBlocks", toJsonArray(locatedblocks.getLocatedBlocks()));
513    m.put("lastLocatedBlock", toJsonMap(locatedblocks.getLastLocatedBlock()));
514    m.put("isLastBlockComplete", locatedblocks.isLastBlockComplete());
515    return toJsonString(LocatedBlocks.class, m);
516  }
517
518  /** Convert a Json map to LocatedBlock. */
519  public static LocatedBlocks toLocatedBlocks(final Map<?, ?> json
520      ) throws IOException {
521    if (json == null) {
522      return null;
523    }
524
525    final Map<?, ?> m = (Map<?, ?>)json.get(LocatedBlocks.class.getSimpleName());
526    final long fileLength = (Long)m.get("fileLength");
527    final boolean isUnderConstruction = (Boolean)m.get("isUnderConstruction");
528    final List<LocatedBlock> locatedBlocks = toLocatedBlockList(
529        (Object[])m.get("locatedBlocks"));
530    final LocatedBlock lastLocatedBlock = toLocatedBlock(
531        (Map<?, ?>)m.get("lastLocatedBlock"));
532    final boolean isLastBlockComplete = (Boolean)m.get("isLastBlockComplete");
533    return new LocatedBlocks(fileLength, isUnderConstruction, locatedBlocks,
534        lastLocatedBlock, isLastBlockComplete);
535  }
536
537  /** Convert a ContentSummary to a Json string. */
538  public static String toJsonString(final ContentSummary contentsummary) {
539    if (contentsummary == null) {
540      return null;
541    }
542
543    final Map<String, Object> m = new TreeMap<String, Object>();
544    m.put("length", contentsummary.getLength());
545    m.put("fileCount", contentsummary.getFileCount());
546    m.put("directoryCount", contentsummary.getDirectoryCount());
547    m.put("quota", contentsummary.getQuota());
548    m.put("spaceConsumed", contentsummary.getSpaceConsumed());
549    m.put("spaceQuota", contentsummary.getSpaceQuota());
550    return toJsonString(ContentSummary.class, m);
551  }
552
553  /** Convert a Json map to a ContentSummary. */
554  public static ContentSummary toContentSummary(final Map<?, ?> json) {
555    if (json == null) {
556      return null;
557    }
558
559    final Map<?, ?> m = (Map<?, ?>)json.get(ContentSummary.class.getSimpleName());
560    final long length = (Long)m.get("length");
561    final long fileCount = (Long)m.get("fileCount");
562    final long directoryCount = (Long)m.get("directoryCount");
563    final long quota = (Long)m.get("quota");
564    final long spaceConsumed = (Long)m.get("spaceConsumed");
565    final long spaceQuota = (Long)m.get("spaceQuota");
566
567    return new ContentSummary(length, fileCount, directoryCount,
568        quota, spaceConsumed, spaceQuota);
569  }
570
571  /** Convert a MD5MD5CRC32FileChecksum to a Json string. */
572  public static String toJsonString(final MD5MD5CRC32FileChecksum checksum) {
573    if (checksum == null) {
574      return null;
575    }
576
577    final Map<String, Object> m = new TreeMap<String, Object>();
578    m.put("algorithm", checksum.getAlgorithmName());
579    m.put("length", checksum.getLength());
580    m.put("bytes", StringUtils.byteToHexString(checksum.getBytes()));
581    return toJsonString(FileChecksum.class, m);
582  }
583
584  /** Convert a Json map to a MD5MD5CRC32FileChecksum. */
585  public static MD5MD5CRC32FileChecksum toMD5MD5CRC32FileChecksum(
586      final Map<?, ?> json) throws IOException {
587    if (json == null) {
588      return null;
589    }
590
591    final Map<?, ?> m = (Map<?, ?>)json.get(FileChecksum.class.getSimpleName());
592    final String algorithm = (String)m.get("algorithm");
593    final int length = (int)(long)(Long)m.get("length");
594    final byte[] bytes = StringUtils.hexStringToByte((String)m.get("bytes"));
595
596    final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
597    final DataChecksum.Type crcType = 
598        MD5MD5CRC32FileChecksum.getCrcTypeFromAlgorithmName(algorithm);
599    final MD5MD5CRC32FileChecksum checksum;
600
601    // Recreate what DFSClient would have returned.
602    switch(crcType) {
603      case CRC32:
604        checksum = new MD5MD5CRC32GzipFileChecksum();
605        break;
606      case CRC32C:
607        checksum = new MD5MD5CRC32CastagnoliFileChecksum();
608        break;
609      default:
610        throw new IOException("Unknown algorithm: " + algorithm);
611    }
612    checksum.readFields(in);
613
614    //check algorithm name
615    if (!checksum.getAlgorithmName().equals(algorithm)) {
616      throw new IOException("Algorithm not matched. Expected " + algorithm
617          + ", Received " + checksum.getAlgorithmName());
618    }
619    //check length
620    if (length != checksum.getLength()) {
621      throw new IOException("Length not matched: length=" + length
622          + ", checksum.getLength()=" + checksum.getLength());
623    }
624
625    return checksum;
626  }
627  /** Convert a AclStatus object to a Json string. */
628  public static String toJsonString(final AclStatus status) {
629    if (status == null) {
630      return null;
631    }
632
633    final Map<String, Object> m = new TreeMap<String, Object>();
634    m.put("owner", status.getOwner());
635    m.put("group", status.getGroup());
636    m.put("stickyBit", status.isStickyBit());
637    m.put("entries", status.getEntries());
638    final Map<String, Map<String, Object>> finalMap =
639        new TreeMap<String, Map<String, Object>>();
640    finalMap.put(AclStatus.class.getSimpleName(), m);
641    return JSON.toString(finalMap);
642  }
643
644  /** Convert a Json map to a AclStatus object. */
645  public static AclStatus toAclStatus(final Map<?, ?> json) {
646    if (json == null) {
647      return null;
648    }
649
650    final Map<?, ?> m = (Map<?, ?>) json.get(AclStatus.class.getSimpleName());
651
652    AclStatus.Builder aclStatusBuilder = new AclStatus.Builder();
653    aclStatusBuilder.owner((String) m.get("owner"));
654    aclStatusBuilder.group((String) m.get("group"));
655    aclStatusBuilder.stickyBit((Boolean) m.get("stickyBit"));
656
657    final Object[] entries = (Object[]) m.get("entries");
658
659    List<AclEntry> aclEntryList = new ArrayList<AclEntry>();
660    for (int i = 0; i < entries.length; i++) {
661      AclEntry aclEntry = AclEntry.parseAclEntry((String) entries[i], true);
662      aclEntryList.add(aclEntry);
663    }
664    aclStatusBuilder.addEntries(aclEntryList);
665    return aclStatusBuilder.build();
666  }
667}