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 java.io.ByteArrayInputStream; 021 import java.io.DataInputStream; 022 import java.io.IOException; 023 import java.util.ArrayList; 024 import java.util.Collections; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.TreeMap; 028 029 import org.apache.hadoop.fs.ContentSummary; 030 import org.apache.hadoop.fs.FileChecksum; 031 import org.apache.hadoop.fs.FileStatus; 032 import org.apache.hadoop.fs.MD5MD5CRC32FileChecksum; 033 import org.apache.hadoop.fs.permission.FsPermission; 034 import org.apache.hadoop.hdfs.DFSUtil; 035 import org.apache.hadoop.hdfs.protocol.DatanodeInfo; 036 import org.apache.hadoop.hdfs.protocol.DatanodeInfo.AdminStates; 037 import org.apache.hadoop.hdfs.protocol.ExtendedBlock; 038 import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 039 import org.apache.hadoop.hdfs.protocol.LocatedBlock; 040 import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 041 import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier; 042 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 043 import org.apache.hadoop.io.MD5Hash; 044 import org.apache.hadoop.ipc.RemoteException; 045 import org.apache.hadoop.security.token.Token; 046 import org.apache.hadoop.security.token.TokenIdentifier; 047 import org.apache.hadoop.util.StringUtils; 048 import org.mortbay.util.ajax.JSON; 049 050 /** JSON Utilities */ 051 public class JsonUtil { 052 private static final Object[] EMPTY_OBJECT_ARRAY = {}; 053 private static final DatanodeInfo[] EMPTY_DATANODE_INFO_ARRAY = {}; 054 055 /** Convert a token object to a Json string. */ 056 public static String toJsonString(final Token<? extends TokenIdentifier> token 057 ) throws IOException { 058 return toJsonString(Token.class, toJsonMap(token)); 059 } 060 061 private static Map<String, Object> toJsonMap( 062 final Token<? extends TokenIdentifier> token) throws IOException { 063 if (token == null) { 064 return null; 065 } 066 067 final Map<String, Object> m = new TreeMap<String, Object>(); 068 m.put("urlString", token.encodeToUrlString()); 069 return m; 070 } 071 072 /** Convert a Json map to a Token. */ 073 public static Token<? extends TokenIdentifier> toToken( 074 final Map<?, ?> m) throws IOException { 075 if (m == null) { 076 return null; 077 } 078 079 final Token<DelegationTokenIdentifier> token 080 = new Token<DelegationTokenIdentifier>(); 081 token.decodeFromUrlString((String)m.get("urlString")); 082 return token; 083 } 084 085 /** Convert a Json map to a Token of DelegationTokenIdentifier. */ 086 @SuppressWarnings("unchecked") 087 public static Token<DelegationTokenIdentifier> toDelegationToken( 088 final Map<?, ?> json) throws IOException { 089 final Map<?, ?> m = (Map<?, ?>)json.get(Token.class.getSimpleName()); 090 return (Token<DelegationTokenIdentifier>)toToken(m); 091 } 092 093 /** Convert a Json map to a Token of BlockTokenIdentifier. */ 094 @SuppressWarnings("unchecked") 095 private static Token<BlockTokenIdentifier> toBlockToken( 096 final Map<?, ?> m) throws IOException { 097 return (Token<BlockTokenIdentifier>)toToken(m); 098 } 099 100 /** Convert a Token[] to a JSON array. */ 101 private static Object[] toJsonArray(final Token<? extends TokenIdentifier>[] array 102 ) throws IOException { 103 if (array == null) { 104 return null; 105 } else if (array.length == 0) { 106 return EMPTY_OBJECT_ARRAY; 107 } else { 108 final Object[] a = new Object[array.length]; 109 for(int i = 0; i < array.length; i++) { 110 a[i] = toJsonMap(array[i]); 111 } 112 return a; 113 } 114 } 115 116 /** Convert a token object to a JSON string. */ 117 public static String toJsonString(final Token<? extends TokenIdentifier>[] tokens 118 ) throws IOException { 119 if (tokens == null) { 120 return null; 121 } 122 123 final Map<String, Object> m = new TreeMap<String, Object>(); 124 m.put(Token.class.getSimpleName(), toJsonArray(tokens)); 125 return toJsonString(Token.class.getSimpleName() + "s", m); 126 } 127 128 /** Convert an Object[] to a List<Token<?>>. */ 129 private static List<Token<?>> toTokenList(final Object[] objects) throws IOException { 130 if (objects == null) { 131 return null; 132 } else if (objects.length == 0) { 133 return Collections.emptyList(); 134 } else { 135 final List<Token<?>> list = new ArrayList<Token<?>>(objects.length); 136 for(int i = 0; i < objects.length; i++) { 137 list.add(toToken((Map<?, ?>)objects[i])); 138 } 139 return list; 140 } 141 } 142 143 /** Convert a JSON map to a List<Token<?>>. */ 144 public static List<Token<?>> toTokenList(final Map<?, ?> json) throws IOException { 145 if (json == null) { 146 return null; 147 } 148 149 final Map<?, ?> m = (Map<?, ?>)json.get(Token.class.getSimpleName() + "s"); 150 return toTokenList((Object[])m.get(Token.class.getSimpleName())); 151 } 152 153 /** Convert an exception object to a Json string. */ 154 public static String toJsonString(final Exception e) { 155 final Map<String, Object> m = new TreeMap<String, Object>(); 156 m.put("exception", e.getClass().getSimpleName()); 157 m.put("message", e.getMessage()); 158 m.put("javaClassName", e.getClass().getName()); 159 return toJsonString(RemoteException.class, m); 160 } 161 162 /** Convert a Json map to a RemoteException. */ 163 public static RemoteException toRemoteException(final Map<?, ?> json) { 164 final Map<?, ?> m = (Map<?, ?>)json.get(RemoteException.class.getSimpleName()); 165 final String message = (String)m.get("message"); 166 final String javaClassName = (String)m.get("javaClassName"); 167 return new RemoteException(javaClassName, message); 168 } 169 170 private static String toJsonString(final Class<?> clazz, final Object value) { 171 return toJsonString(clazz.getSimpleName(), value); 172 } 173 174 /** Convert a key-value pair to a Json string. */ 175 public static String toJsonString(final String key, final Object value) { 176 final Map<String, Object> m = new TreeMap<String, Object>(); 177 m.put(key, value); 178 return JSON.toString(m); 179 } 180 181 /** Convert a FsPermission object to a string. */ 182 private static String toString(final FsPermission permission) { 183 return String.format("%o", permission.toShort()); 184 } 185 186 /** Convert a string to a FsPermission object. */ 187 private static FsPermission toFsPermission(final String s) { 188 return new FsPermission(Short.parseShort(s, 8)); 189 } 190 191 static enum PathType { 192 FILE, DIRECTORY, SYMLINK; 193 194 static PathType valueOf(HdfsFileStatus status) { 195 return status.isDir()? DIRECTORY: status.isSymlink()? SYMLINK: FILE; 196 } 197 } 198 199 /** Convert a HdfsFileStatus object to a Json string. */ 200 public static String toJsonString(final HdfsFileStatus status, 201 boolean includeType) { 202 if (status == null) { 203 return null; 204 } 205 final Map<String, Object> m = new TreeMap<String, Object>(); 206 m.put("pathSuffix", status.getLocalName()); 207 m.put("type", PathType.valueOf(status)); 208 if (status.isSymlink()) { 209 m.put("symlink", status.getSymlink()); 210 } 211 212 m.put("length", status.getLen()); 213 m.put("owner", status.getOwner()); 214 m.put("group", status.getGroup()); 215 m.put("permission", toString(status.getPermission())); 216 m.put("accessTime", status.getAccessTime()); 217 m.put("modificationTime", status.getModificationTime()); 218 m.put("blockSize", status.getBlockSize()); 219 m.put("replication", status.getReplication()); 220 return includeType ? toJsonString(FileStatus.class, m): JSON.toString(m); 221 } 222 223 /** Convert a Json map to a HdfsFileStatus object. */ 224 public static HdfsFileStatus toFileStatus(final Map<?, ?> json, boolean includesType) { 225 if (json == null) { 226 return null; 227 } 228 229 final Map<?, ?> m = includesType ? 230 (Map<?, ?>)json.get(FileStatus.class.getSimpleName()) : json; 231 final String localName = (String) m.get("pathSuffix"); 232 final PathType type = PathType.valueOf((String) m.get("type")); 233 final byte[] symlink = type != PathType.SYMLINK? null 234 : DFSUtil.string2Bytes((String)m.get("symlink")); 235 236 final long len = (Long) m.get("length"); 237 final String owner = (String) m.get("owner"); 238 final String group = (String) m.get("group"); 239 final FsPermission permission = toFsPermission((String) m.get("permission")); 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 return new HdfsFileStatus(len, type == PathType.DIRECTORY, replication, 245 blockSize, mTime, aTime, permission, owner, group, 246 symlink, DFSUtil.string2Bytes(localName)); 247 } 248 249 /** Convert an ExtendedBlock to a Json map. */ 250 private static Map<String, Object> toJsonMap(final ExtendedBlock extendedblock) { 251 if (extendedblock == null) { 252 return null; 253 } 254 255 final Map<String, Object> m = new TreeMap<String, Object>(); 256 m.put("blockPoolId", extendedblock.getBlockPoolId()); 257 m.put("blockId", extendedblock.getBlockId()); 258 m.put("numBytes", extendedblock.getNumBytes()); 259 m.put("generationStamp", extendedblock.getGenerationStamp()); 260 return m; 261 } 262 263 /** Convert a Json map to an ExtendedBlock object. */ 264 private static ExtendedBlock toExtendedBlock(final Map<?, ?> m) { 265 if (m == null) { 266 return null; 267 } 268 269 final String blockPoolId = (String)m.get("blockPoolId"); 270 final long blockId = (Long)m.get("blockId"); 271 final long numBytes = (Long)m.get("numBytes"); 272 final long generationStamp = (Long)m.get("generationStamp"); 273 return new ExtendedBlock(blockPoolId, blockId, numBytes, generationStamp); 274 } 275 276 /** Convert a DatanodeInfo to a Json map. */ 277 private static Map<String, Object> toJsonMap(final DatanodeInfo datanodeinfo) { 278 if (datanodeinfo == null) { 279 return null; 280 } 281 282 final Map<String, Object> m = new TreeMap<String, Object>(); 283 m.put("ipAddr", datanodeinfo.getIpAddr()); 284 m.put("hostName", datanodeinfo.getHostName()); 285 m.put("storageID", datanodeinfo.getStorageID()); 286 m.put("xferPort", datanodeinfo.getXferPort()); 287 m.put("infoPort", datanodeinfo.getInfoPort()); 288 m.put("ipcPort", datanodeinfo.getIpcPort()); 289 290 m.put("capacity", datanodeinfo.getCapacity()); 291 m.put("dfsUsed", datanodeinfo.getDfsUsed()); 292 m.put("remaining", datanodeinfo.getRemaining()); 293 m.put("blockPoolUsed", datanodeinfo.getBlockPoolUsed()); 294 m.put("lastUpdate", datanodeinfo.getLastUpdate()); 295 m.put("xceiverCount", datanodeinfo.getXceiverCount()); 296 m.put("networkLocation", datanodeinfo.getNetworkLocation()); 297 m.put("adminState", datanodeinfo.getAdminState().name()); 298 return m; 299 } 300 301 /** Convert a Json map to an DatanodeInfo object. */ 302 private static DatanodeInfo toDatanodeInfo(final Map<?, ?> m) { 303 if (m == null) { 304 return null; 305 } 306 307 return new DatanodeInfo( 308 (String)m.get("ipAddr"), 309 (String)m.get("hostName"), 310 (String)m.get("storageID"), 311 (int)(long)(Long)m.get("xferPort"), 312 (int)(long)(Long)m.get("infoPort"), 313 (int)(long)(Long)m.get("ipcPort"), 314 315 (Long)m.get("capacity"), 316 (Long)m.get("dfsUsed"), 317 (Long)m.get("remaining"), 318 (Long)m.get("blockPoolUsed"), 319 (Long)m.get("lastUpdate"), 320 (int)(long)(Long)m.get("xceiverCount"), 321 (String)m.get("networkLocation"), 322 AdminStates.valueOf((String)m.get("adminState"))); 323 } 324 325 /** Convert a DatanodeInfo[] to a Json array. */ 326 private static Object[] toJsonArray(final DatanodeInfo[] array) { 327 if (array == null) { 328 return null; 329 } else if (array.length == 0) { 330 return EMPTY_OBJECT_ARRAY; 331 } else { 332 final Object[] a = new Object[array.length]; 333 for(int i = 0; i < array.length; i++) { 334 a[i] = toJsonMap(array[i]); 335 } 336 return a; 337 } 338 } 339 340 /** Convert an Object[] to a DatanodeInfo[]. */ 341 private static DatanodeInfo[] toDatanodeInfoArray(final Object[] objects) { 342 if (objects == null) { 343 return null; 344 } else if (objects.length == 0) { 345 return EMPTY_DATANODE_INFO_ARRAY; 346 } else { 347 final DatanodeInfo[] array = new DatanodeInfo[objects.length]; 348 for(int i = 0; i < array.length; i++) { 349 array[i] = toDatanodeInfo((Map<?, ?>) objects[i]); 350 } 351 return array; 352 } 353 } 354 355 /** Convert a LocatedBlock to a Json map. */ 356 private static Map<String, Object> toJsonMap(final LocatedBlock locatedblock 357 ) throws IOException { 358 if (locatedblock == null) { 359 return null; 360 } 361 362 final Map<String, Object> m = new TreeMap<String, Object>(); 363 m.put("blockToken", toJsonMap(locatedblock.getBlockToken())); 364 m.put("isCorrupt", locatedblock.isCorrupt()); 365 m.put("startOffset", locatedblock.getStartOffset()); 366 m.put("block", toJsonMap(locatedblock.getBlock())); 367 m.put("locations", toJsonArray(locatedblock.getLocations())); 368 return m; 369 } 370 371 /** Convert a Json map to LocatedBlock. */ 372 private static LocatedBlock toLocatedBlock(final Map<?, ?> m) throws IOException { 373 if (m == null) { 374 return null; 375 } 376 377 final ExtendedBlock b = toExtendedBlock((Map<?, ?>)m.get("block")); 378 final DatanodeInfo[] locations = toDatanodeInfoArray( 379 (Object[])m.get("locations")); 380 final long startOffset = (Long)m.get("startOffset"); 381 final boolean isCorrupt = (Boolean)m.get("isCorrupt"); 382 383 final LocatedBlock locatedblock = new LocatedBlock(b, locations, startOffset, isCorrupt); 384 locatedblock.setBlockToken(toBlockToken((Map<?, ?>)m.get("blockToken"))); 385 return locatedblock; 386 } 387 388 /** Convert a LocatedBlock[] to a Json array. */ 389 private static Object[] toJsonArray(final List<LocatedBlock> array 390 ) throws IOException { 391 if (array == null) { 392 return null; 393 } else if (array.size() == 0) { 394 return EMPTY_OBJECT_ARRAY; 395 } else { 396 final Object[] a = new Object[array.size()]; 397 for(int i = 0; i < array.size(); i++) { 398 a[i] = toJsonMap(array.get(i)); 399 } 400 return a; 401 } 402 } 403 404 /** Convert an Object[] to a List of LocatedBlock. */ 405 private static List<LocatedBlock> toLocatedBlockList(final Object[] objects 406 ) throws IOException { 407 if (objects == null) { 408 return null; 409 } else if (objects.length == 0) { 410 return Collections.emptyList(); 411 } else { 412 final List<LocatedBlock> list = new ArrayList<LocatedBlock>(objects.length); 413 for(int i = 0; i < objects.length; i++) { 414 list.add(toLocatedBlock((Map<?, ?>)objects[i])); 415 } 416 return list; 417 } 418 } 419 420 /** Convert LocatedBlocks to a Json string. */ 421 public static String toJsonString(final LocatedBlocks locatedblocks 422 ) throws IOException { 423 if (locatedblocks == null) { 424 return null; 425 } 426 427 final Map<String, Object> m = new TreeMap<String, Object>(); 428 m.put("fileLength", locatedblocks.getFileLength()); 429 m.put("isUnderConstruction", locatedblocks.isUnderConstruction()); 430 431 m.put("locatedBlocks", toJsonArray(locatedblocks.getLocatedBlocks())); 432 m.put("lastLocatedBlock", toJsonMap(locatedblocks.getLastLocatedBlock())); 433 m.put("isLastBlockComplete", locatedblocks.isLastBlockComplete()); 434 return toJsonString(LocatedBlocks.class, m); 435 } 436 437 /** Convert a Json map to LocatedBlock. */ 438 public static LocatedBlocks toLocatedBlocks(final Map<?, ?> json 439 ) throws IOException { 440 if (json == null) { 441 return null; 442 } 443 444 final Map<?, ?> m = (Map<?, ?>)json.get(LocatedBlocks.class.getSimpleName()); 445 final long fileLength = (Long)m.get("fileLength"); 446 final boolean isUnderConstruction = (Boolean)m.get("isUnderConstruction"); 447 final List<LocatedBlock> locatedBlocks = toLocatedBlockList( 448 (Object[])m.get("locatedBlocks")); 449 final LocatedBlock lastLocatedBlock = toLocatedBlock( 450 (Map<?, ?>)m.get("lastLocatedBlock")); 451 final boolean isLastBlockComplete = (Boolean)m.get("isLastBlockComplete"); 452 return new LocatedBlocks(fileLength, isUnderConstruction, locatedBlocks, 453 lastLocatedBlock, isLastBlockComplete); 454 } 455 456 /** Convert a ContentSummary to a Json string. */ 457 public static String toJsonString(final ContentSummary contentsummary) { 458 if (contentsummary == null) { 459 return null; 460 } 461 462 final Map<String, Object> m = new TreeMap<String, Object>(); 463 m.put("length", contentsummary.getLength()); 464 m.put("fileCount", contentsummary.getFileCount()); 465 m.put("directoryCount", contentsummary.getDirectoryCount()); 466 m.put("quota", contentsummary.getQuota()); 467 m.put("spaceConsumed", contentsummary.getSpaceConsumed()); 468 m.put("spaceQuota", contentsummary.getSpaceQuota()); 469 return toJsonString(ContentSummary.class, m); 470 } 471 472 /** Convert a Json map to a ContentSummary. */ 473 public static ContentSummary toContentSummary(final Map<?, ?> json) { 474 if (json == null) { 475 return null; 476 } 477 478 final Map<?, ?> m = (Map<?, ?>)json.get(ContentSummary.class.getSimpleName()); 479 final long length = (Long)m.get("length"); 480 final long fileCount = (Long)m.get("fileCount"); 481 final long directoryCount = (Long)m.get("directoryCount"); 482 final long quota = (Long)m.get("quota"); 483 final long spaceConsumed = (Long)m.get("spaceConsumed"); 484 final long spaceQuota = (Long)m.get("spaceQuota"); 485 486 return new ContentSummary(length, fileCount, directoryCount, 487 quota, spaceConsumed, spaceQuota); 488 } 489 490 /** Convert a MD5MD5CRC32FileChecksum to a Json string. */ 491 public static String toJsonString(final MD5MD5CRC32FileChecksum checksum) { 492 if (checksum == null) { 493 return null; 494 } 495 496 final Map<String, Object> m = new TreeMap<String, Object>(); 497 m.put("algorithm", checksum.getAlgorithmName()); 498 m.put("length", checksum.getLength()); 499 m.put("bytes", StringUtils.byteToHexString(checksum.getBytes())); 500 return toJsonString(FileChecksum.class, m); 501 } 502 503 /** Convert a Json map to a MD5MD5CRC32FileChecksum. */ 504 public static MD5MD5CRC32FileChecksum toMD5MD5CRC32FileChecksum( 505 final Map<?, ?> json) throws IOException { 506 if (json == null) { 507 return null; 508 } 509 510 final Map<?, ?> m = (Map<?, ?>)json.get(FileChecksum.class.getSimpleName()); 511 final String algorithm = (String)m.get("algorithm"); 512 final int length = (int)(long)(Long)m.get("length"); 513 final byte[] bytes = StringUtils.hexStringToByte((String)m.get("bytes")); 514 515 final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes)); 516 final MD5MD5CRC32FileChecksum checksum = new MD5MD5CRC32FileChecksum(); 517 checksum.readFields(in); 518 519 //check algorithm name 520 if (!checksum.getAlgorithmName().equals(algorithm)) { 521 throw new IOException("Algorithm not matched. Expected " + algorithm 522 + ", Received " + checksum.getAlgorithmName()); 523 } 524 //check length 525 if (length != checksum.getLength()) { 526 throw new IOException("Length not matched: length=" + length 527 + ", checksum.getLength()=" + checksum.getLength()); 528 } 529 530 return checksum; 531 } 532 }