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.tools.offlineImageViewer; 019 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import org.jboss.netty.channel.ChannelFutureListener; 028import org.jboss.netty.channel.ChannelHandlerContext; 029import org.jboss.netty.channel.MessageEvent; 030import org.jboss.netty.channel.SimpleChannelUpstreamHandler; 031import org.jboss.netty.handler.codec.http.DefaultHttpResponse; 032import org.jboss.netty.handler.codec.http.HttpHeaders; 033import org.jboss.netty.handler.codec.http.HttpMethod; 034import org.jboss.netty.handler.codec.http.HttpRequest; 035import org.jboss.netty.handler.codec.http.HttpResponse; 036import org.jboss.netty.handler.codec.http.HttpResponseStatus; 037import org.jboss.netty.handler.codec.http.HttpVersion; 038import org.jboss.netty.handler.codec.http.QueryStringDecoder; 039 040/** 041 * Implement the read-only WebHDFS API for fsimage. 042 */ 043public class FSImageHandler extends SimpleChannelUpstreamHandler { 044 public static final Log LOG = LogFactory.getLog(FSImageHandler.class); 045 private final FSImageLoader loader; 046 047 public FSImageHandler(FSImageLoader loader) throws IOException { 048 this.loader = loader; 049 } 050 051 @Override 052 public void messageReceived( 053 ChannelHandlerContext ctx, MessageEvent e) throws Exception { 054 String op = getOp(e); 055 try { 056 String path = getPath(e); 057 handleOperation(op, path, e); 058 } catch (Exception ex) { 059 notFoundResponse(e); 060 LOG.warn(ex.getMessage()); 061 } finally { 062 e.getFuture().addListener(ChannelFutureListener.CLOSE); 063 } 064 } 065 066 /** return the op parameter in upper case */ 067 private String getOp(MessageEvent e) { 068 Map<String, List<String>> parameters = getDecoder(e).getParameters(); 069 if (parameters.containsKey("op")) { 070 return parameters.get("op").get(0).toUpperCase(); 071 } else { 072 // return "" to avoid NPE 073 return ""; 074 } 075 } 076 077 private String getPath(MessageEvent e) throws FileNotFoundException { 078 String path = getDecoder(e).getPath(); 079 // trim "/webhdfs/v1" to keep compatibility with WebHDFS API 080 if (path.startsWith("/webhdfs/v1/")) { 081 return path.replaceFirst("/webhdfs/v1", ""); 082 } else { 083 throw new FileNotFoundException("Path: " + path + " should " + 084 "start with \"/webhdfs/v1/\""); 085 } 086 } 087 088 private QueryStringDecoder getDecoder(MessageEvent e) { 089 HttpRequest request = (HttpRequest) e.getMessage(); 090 return new QueryStringDecoder(request.getUri()); 091 } 092 093 private void handleOperation(String op, String path, MessageEvent e) 094 throws IOException { 095 HttpRequest request = (HttpRequest) e.getMessage(); 096 HttpResponse response = new DefaultHttpResponse( 097 HttpVersion.HTTP_1_1, HttpResponseStatus.OK); 098 response.setHeader(HttpHeaders.Names.CONTENT_TYPE, 099 "application/json"); 100 String content = null; 101 102 if (request.getMethod() == HttpMethod.GET){ 103 if (op.equals("GETFILESTATUS")) { 104 content = loader.getFileStatus(path); 105 } else if (op.equals("LISTSTATUS")) { 106 content = loader.listStatus(path); 107 } else if (op.equals("GETACLSTATUS")) { 108 content = loader.getAclStatus(path); 109 } else { 110 response.setStatus(HttpResponseStatus.BAD_REQUEST); 111 } 112 } else { 113 // only HTTP GET is allowed since fsimage is read-only. 114 response.setStatus(HttpResponseStatus.METHOD_NOT_ALLOWED); 115 } 116 117 if (content != null) { 118 HttpHeaders.setContentLength(response, content.length()); 119 } 120 e.getChannel().write(response); 121 122 if (content != null) { 123 e.getChannel().write(content); 124 } 125 126 LOG.info(response.getStatus().getCode() + " method=" 127 + request.getMethod().getName() + " op=" + op + " target=" + path); 128 } 129 130 private void notFoundResponse(MessageEvent e) { 131 HttpResponse response = new DefaultHttpResponse( 132 HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND); 133 e.getChannel().write(response); 134 } 135}