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.XAttrHelper;
025import org.apache.hadoop.hdfs.protocol.*;
026import org.apache.hadoop.ipc.RemoteException;
027import org.apache.hadoop.security.token.Token;
028import org.apache.hadoop.security.token.TokenIdentifier;
029import org.apache.hadoop.util.StringUtils;
030import org.codehaus.jackson.map.ObjectMapper;
031
032import com.google.common.collect.Lists;
033
034import java.io.IOException;
035import java.util.*;
036
037/** JSON Utilities */
038public class JsonUtil {
039  private static final Object[] EMPTY_OBJECT_ARRAY = {};
040
041  // Reuse ObjectMapper instance for improving performance.
042  // ObjectMapper is thread safe as long as we always configure instance
043  // before use. We don't have a re-entrant call pattern in WebHDFS,
044  // so we just need to worry about thread-safety.
045  private static final ObjectMapper MAPPER = new ObjectMapper();
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 an exception object to a Json string. */
065  public static String toJsonString(final Exception e) {
066    final Map<String, Object> m = new TreeMap<String, Object>();
067    m.put("exception", e.getClass().getSimpleName());
068    m.put("message", e.getMessage());
069    m.put("javaClassName", e.getClass().getName());
070    return toJsonString(RemoteException.class, m);
071  }
072
073  private static String toJsonString(final Class<?> clazz, final Object value) {
074    return toJsonString(clazz.getSimpleName(), value);
075  }
076
077  /** Convert a key-value pair to a Json string. */
078  public static String toJsonString(final String key, final Object value) {
079    final Map<String, Object> m = new TreeMap<String, Object>();
080    m.put(key, value);
081    try {
082      return MAPPER.writeValueAsString(m);
083    } catch (IOException ignored) {
084    }
085    return null;
086  }
087
088  /** Convert a FsPermission object to a string. */
089  private static String toString(final FsPermission permission) {
090    return String.format("%o", permission.toShort());
091  }
092
093  /** Convert a HdfsFileStatus object to a Json string. */
094  public static String toJsonString(final HdfsFileStatus status,
095      boolean includeType) {
096    if (status == null) {
097      return null;
098    }
099    final Map<String, Object> m = new TreeMap<String, Object>();
100    m.put("pathSuffix", status.getLocalName());
101    m.put("type", WebHdfsConstants.PathType.valueOf(status));
102    if (status.isSymlink()) {
103      m.put("symlink", status.getSymlink());
104    }
105
106    m.put("length", status.getLen());
107    m.put("owner", status.getOwner());
108    m.put("group", status.getGroup());
109    FsPermission perm = status.getPermission();
110    m.put("permission", toString(perm));
111    if (perm.getAclBit()) {
112      m.put("aclBit", true);
113    }
114    if (perm.getEncryptedBit()) {
115      m.put("encBit", true);
116    }
117    m.put("accessTime", status.getAccessTime());
118    m.put("modificationTime", status.getModificationTime());
119    m.put("blockSize", status.getBlockSize());
120    m.put("replication", status.getReplication());
121    m.put("fileId", status.getFileId());
122    m.put("childrenNum", status.getChildrenNum());
123    m.put("storagePolicy", status.getStoragePolicy());
124    try {
125      return includeType ?
126          toJsonString(FileStatus.class, m) : MAPPER.writeValueAsString(m);
127    } catch (IOException ignored) {
128    }
129    return null;
130  }
131
132  /** Convert an ExtendedBlock to a Json map. */
133  private static Map<String, Object> toJsonMap(final ExtendedBlock extendedblock) {
134    if (extendedblock == null) {
135      return null;
136    }
137
138    final Map<String, Object> m = new TreeMap<String, Object>();
139    m.put("blockPoolId", extendedblock.getBlockPoolId());
140    m.put("blockId", extendedblock.getBlockId());
141    m.put("numBytes", extendedblock.getNumBytes());
142    m.put("generationStamp", extendedblock.getGenerationStamp());
143    return m;
144  }
145
146  /** Convert a DatanodeInfo to a Json map. */
147  static Map<String, Object> toJsonMap(final DatanodeInfo datanodeinfo) {
148    if (datanodeinfo == null) {
149      return null;
150    }
151
152    // TODO: Fix storageID
153    final Map<String, Object> m = new TreeMap<String, Object>();
154    m.put("ipAddr", datanodeinfo.getIpAddr());
155    // 'name' is equivalent to ipAddr:xferPort. Older clients (1.x, 0.23.x) 
156    // expects this instead of the two fields.
157    m.put("name", datanodeinfo.getXferAddr());
158    m.put("hostName", datanodeinfo.getHostName());
159    m.put("storageID", datanodeinfo.getDatanodeUuid());
160    m.put("xferPort", datanodeinfo.getXferPort());
161    m.put("infoPort", datanodeinfo.getInfoPort());
162    m.put("infoSecurePort", datanodeinfo.getInfoSecurePort());
163    m.put("ipcPort", datanodeinfo.getIpcPort());
164
165    m.put("capacity", datanodeinfo.getCapacity());
166    m.put("dfsUsed", datanodeinfo.getDfsUsed());
167    m.put("remaining", datanodeinfo.getRemaining());
168    m.put("blockPoolUsed", datanodeinfo.getBlockPoolUsed());
169    m.put("cacheCapacity", datanodeinfo.getCacheCapacity());
170    m.put("cacheUsed", datanodeinfo.getCacheUsed());
171    m.put("lastUpdate", datanodeinfo.getLastUpdate());
172    m.put("lastUpdateMonotonic", datanodeinfo.getLastUpdateMonotonic());
173    m.put("xceiverCount", datanodeinfo.getXceiverCount());
174    m.put("networkLocation", datanodeinfo.getNetworkLocation());
175    m.put("adminState", datanodeinfo.getAdminState().name());
176    if (datanodeinfo.getUpgradeDomain() != null) {
177      m.put("upgradeDomain", datanodeinfo.getUpgradeDomain());
178    }
179    return m;
180  }
181
182  /** Convert a DatanodeInfo[] to a Json array. */
183  private static Object[] toJsonArray(final DatanodeInfo[] array) {
184    if (array == null) {
185      return null;
186    } else if (array.length == 0) {
187      return EMPTY_OBJECT_ARRAY;
188    } else {
189      final Object[] a = new Object[array.length];
190      for(int i = 0; i < array.length; i++) {
191        a[i] = toJsonMap(array[i]);
192      }
193      return a;
194    }
195  }
196
197  /** Convert a StorageType[] to a Json array. */
198  private static Object[] toJsonArray(final StorageType[] array) {
199    if (array == null) {
200      return null;
201    } else if (array.length == 0) {
202      return EMPTY_OBJECT_ARRAY;
203    } else {
204      final Object[] a = new Object[array.length];
205      for(int i = 0; i < array.length; i++) {
206        a[i] = array[i];
207      }
208      return a;
209    }
210  }
211
212  /** Convert a LocatedBlock to a Json map. */
213  private static Map<String, Object> toJsonMap(final LocatedBlock locatedblock
214      ) throws IOException {
215    if (locatedblock == null) {
216      return null;
217    }
218 
219    final Map<String, Object> m = new TreeMap<String, Object>();
220    m.put("blockToken", toJsonMap(locatedblock.getBlockToken()));
221    m.put("isCorrupt", locatedblock.isCorrupt());
222    m.put("startOffset", locatedblock.getStartOffset());
223    m.put("block", toJsonMap(locatedblock.getBlock()));
224    m.put("storageTypes", toJsonArray(locatedblock.getStorageTypes()));
225    m.put("locations", toJsonArray(locatedblock.getLocations()));
226    m.put("cachedLocations", toJsonArray(locatedblock.getCachedLocations()));
227    return m;
228  }
229
230  /** Convert a LocatedBlock[] to a Json array. */
231  private static Object[] toJsonArray(final List<LocatedBlock> array
232      ) throws IOException {
233    if (array == null) {
234      return null;
235    } else if (array.size() == 0) {
236      return EMPTY_OBJECT_ARRAY;
237    } else {
238      final Object[] a = new Object[array.size()];
239      for(int i = 0; i < array.size(); i++) {
240        a[i] = toJsonMap(array.get(i));
241      }
242      return a;
243    }
244  }
245
246  /** Convert LocatedBlocks to a Json string. */
247  public static String toJsonString(final LocatedBlocks locatedblocks
248      ) throws IOException {
249    if (locatedblocks == null) {
250      return null;
251    }
252
253    final Map<String, Object> m = new TreeMap<String, Object>();
254    m.put("fileLength", locatedblocks.getFileLength());
255    m.put("isUnderConstruction", locatedblocks.isUnderConstruction());
256
257    m.put("locatedBlocks", toJsonArray(locatedblocks.getLocatedBlocks()));
258    m.put("lastLocatedBlock", toJsonMap(locatedblocks.getLastLocatedBlock()));
259    m.put("isLastBlockComplete", locatedblocks.isLastBlockComplete());
260    return toJsonString(LocatedBlocks.class, m);
261  }
262
263  /** Convert a ContentSummary to a Json string. */
264  public static String toJsonString(final ContentSummary contentsummary) {
265    if (contentsummary == null) {
266      return null;
267    }
268
269    final Map<String, Object> m = new TreeMap<String, Object>();
270    m.put("length", contentsummary.getLength());
271    m.put("fileCount", contentsummary.getFileCount());
272    m.put("directoryCount", contentsummary.getDirectoryCount());
273    m.put("quota", contentsummary.getQuota());
274    m.put("spaceConsumed", contentsummary.getSpaceConsumed());
275    m.put("spaceQuota", contentsummary.getSpaceQuota());
276    final Map<String, Map<String, Long>> typeQuota =
277        new TreeMap<String, Map<String, Long>>();
278    for (StorageType t : StorageType.getTypesSupportingQuota()) {
279      long tQuota = contentsummary.getTypeQuota(t);
280      if (tQuota != HdfsConstants.QUOTA_RESET) {
281        Map<String, Long> type = typeQuota.get(t.toString());
282        if (type == null) {
283          type = new TreeMap<String, Long>();
284          typeQuota.put(t.toString(), type);
285        }
286        type.put("quota", contentsummary.getTypeQuota(t));
287        type.put("consumed", contentsummary.getTypeConsumed(t));
288      }
289    }
290    m.put("typeQuota", typeQuota);
291    return toJsonString(ContentSummary.class, m);
292  }
293
294  /** Convert a MD5MD5CRC32FileChecksum to a Json string. */
295  public static String toJsonString(final MD5MD5CRC32FileChecksum checksum) {
296    if (checksum == null) {
297      return null;
298    }
299
300    final Map<String, Object> m = new TreeMap<String, Object>();
301    m.put("algorithm", checksum.getAlgorithmName());
302    m.put("length", checksum.getLength());
303    m.put("bytes", StringUtils.byteToHexString(checksum.getBytes()));
304    return toJsonString(FileChecksum.class, m);
305  }
306
307  /** Convert a AclStatus object to a Json string. */
308  public static String toJsonString(final AclStatus status) {
309    if (status == null) {
310      return null;
311    }
312
313    final Map<String, Object> m = new TreeMap<String, Object>();
314    m.put("owner", status.getOwner());
315    m.put("group", status.getGroup());
316    m.put("stickyBit", status.isStickyBit());
317
318    final List<String> stringEntries = new ArrayList<>();
319    for (AclEntry entry : status.getEntries()) {
320      stringEntries.add(entry.toStringStable());
321    }
322    m.put("entries", stringEntries);
323
324    FsPermission perm = status.getPermission();
325    if (perm != null) {
326      m.put("permission", toString(perm));
327      if (perm.getAclBit()) {
328        m.put("aclBit", true);
329      }
330      if (perm.getEncryptedBit()) {
331        m.put("encBit", true);
332      }
333    }
334    final Map<String, Map<String, Object>> finalMap =
335        new TreeMap<String, Map<String, Object>>();
336    finalMap.put(AclStatus.class.getSimpleName(), m);
337
338    try {
339      return MAPPER.writeValueAsString(finalMap);
340    } catch (IOException ignored) {
341    }
342    return null;
343  }
344
345  private static Map<String, Object> toJsonMap(final XAttr xAttr,
346      final XAttrCodec encoding) throws IOException {
347    if (xAttr == null) {
348      return null;
349    }
350 
351    final Map<String, Object> m = new TreeMap<String, Object>();
352    m.put("name", XAttrHelper.getPrefixedName(xAttr));
353    m.put("value", xAttr.getValue() != null ?
354        XAttrCodec.encodeValue(xAttr.getValue(), encoding) : null);
355    return m;
356  }
357  
358  private static Object[] toJsonArray(final List<XAttr> array,
359      final XAttrCodec encoding) throws IOException {
360    if (array == null) {
361      return null;
362    } else if (array.size() == 0) {
363      return EMPTY_OBJECT_ARRAY;
364    } else {
365      final Object[] a = new Object[array.size()];
366      for(int i = 0; i < array.size(); i++) {
367        a[i] = toJsonMap(array.get(i), encoding);
368      }
369      return a;
370    }
371  }
372  
373  public static String toJsonString(final List<XAttr> xAttrs, 
374      final XAttrCodec encoding) throws IOException {
375    final Map<String, Object> finalMap = new TreeMap<String, Object>();
376    finalMap.put("XAttrs", toJsonArray(xAttrs, encoding));
377    return MAPPER.writeValueAsString(finalMap);
378  }
379  
380  public static String toJsonString(final List<XAttr> xAttrs)
381      throws IOException {
382    final List<String> names = Lists.newArrayListWithCapacity(xAttrs.size());
383    for (XAttr xAttr : xAttrs) {
384      names.add(XAttrHelper.getPrefixedName(xAttr));
385    }
386    String ret = MAPPER.writeValueAsString(names);
387    final Map<String, Object> finalMap = new TreeMap<String, Object>();
388    finalMap.put("XAttrNames", ret);
389    return MAPPER.writeValueAsString(finalMap);
390  }
391
392  public static String toJsonString(Object obj) throws IOException {
393    return MAPPER.writeValueAsString(obj);
394  }
395
396  public static String toJsonString(BlockStoragePolicy[] storagePolicies) {
397    final Map<String, Object> blockStoragePolicies = new TreeMap<>();
398    Object[] a = null;
399    if (storagePolicies != null && storagePolicies.length > 0) {
400      a = new Object[storagePolicies.length];
401      for (int i = 0; i < storagePolicies.length; i++) {
402        a[i] = toJsonMap(storagePolicies[i]);
403      }
404    }
405    blockStoragePolicies.put("BlockStoragePolicy", a);
406    return toJsonString("BlockStoragePolicies", blockStoragePolicies);
407  }
408
409  private static Object toJsonMap(BlockStoragePolicy blockStoragePolicy) {
410    final Map<String, Object> m = new TreeMap<String, Object>();
411    m.put("id", blockStoragePolicy.getId());
412    m.put("name", blockStoragePolicy.getName());
413    m.put("storageTypes", blockStoragePolicy.getStorageTypes());
414    m.put("creationFallbacks", blockStoragePolicy.getCreationFallbacks());
415    m.put("replicationFallbacks", blockStoragePolicy.getReplicationFallbacks());
416    m.put("copyOnCreateFile", blockStoragePolicy.isCopyOnCreateFile());
417    return m;
418  }
419
420  public static String toJsonString(BlockStoragePolicy storagePolicy) {
421    return toJsonString(BlockStoragePolicy.class, toJsonMap(storagePolicy));
422  }
423}