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