HDFS-5570. Addendum commit for r1584100.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1584174 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
fb1d7fb596
commit
5b3481a750
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
|
||||
/**
|
||||
* Cancel delegation tokens over http for use in hftp.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CancelDelegationTokenServlet extends DfsServlet {
|
||||
private static final Log LOG = LogFactory.getLog(CancelDelegationTokenServlet.class);
|
||||
public static final String PATH_SPEC = "/cancelDelegationToken";
|
||||
public static final String TOKEN = "token";
|
||||
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
final UserGroupInformation ugi;
|
||||
final ServletContext context = getServletContext();
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(context);
|
||||
try {
|
||||
ugi = getUGI(req, conf);
|
||||
} catch(IOException ioe) {
|
||||
LOG.info("Request for token received with no authentication from "
|
||||
+ req.getRemoteAddr(), ioe);
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Unable to identify or authenticate user");
|
||||
return;
|
||||
}
|
||||
final NameNode nn = NameNodeHttpServer.getNameNodeFromContext(
|
||||
context);
|
||||
String tokenString = req.getParameter(TOKEN);
|
||||
if (tokenString == null) {
|
||||
resp.sendError(HttpServletResponse.SC_MULTIPLE_CHOICES,
|
||||
"Token to renew not specified");
|
||||
}
|
||||
final Token<DelegationTokenIdentifier> token =
|
||||
new Token<DelegationTokenIdentifier>();
|
||||
token.decodeFromUrlString(tokenString);
|
||||
|
||||
try {
|
||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
nn.getRpcServer().cancelDelegationToken(token);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch(Exception e) {
|
||||
LOG.info("Exception while cancelling token. Re-throwing. ", e);
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.ContentSummary;
|
||||
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.znerd.xmlenc.XMLOutputter;
|
||||
|
||||
/** Servlets for file checksum */
|
||||
@InterfaceAudience.Private
|
||||
public class ContentSummaryServlet extends DfsServlet {
|
||||
/** For java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void doGet(final HttpServletRequest request,
|
||||
final HttpServletResponse response) throws ServletException, IOException {
|
||||
final Configuration conf =
|
||||
(Configuration) getServletContext().getAttribute(JspHelper.CURRENT_CONF);
|
||||
final UserGroupInformation ugi = getUGI(request, conf);
|
||||
try {
|
||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
final String path = ServletUtil.getDecodedPath(request, "/contentSummary");
|
||||
final PrintWriter out = response.getWriter();
|
||||
final XMLOutputter xml = new XMLOutputter(out, "UTF-8");
|
||||
xml.declaration();
|
||||
try {
|
||||
//get content summary
|
||||
final ClientProtocol nnproxy = createNameNodeProxy();
|
||||
final ContentSummary cs = nnproxy.getContentSummary(path);
|
||||
|
||||
//write xml
|
||||
xml.startTag(ContentSummary.class.getName());
|
||||
if (cs != null) {
|
||||
xml.attribute("length" , "" + cs.getLength());
|
||||
xml.attribute("fileCount" , "" + cs.getFileCount());
|
||||
xml.attribute("directoryCount", "" + cs.getDirectoryCount());
|
||||
xml.attribute("quota" , "" + cs.getQuota());
|
||||
xml.attribute("spaceConsumed" , "" + cs.getSpaceConsumed());
|
||||
xml.attribute("spaceQuota" , "" + cs.getSpaceQuota());
|
||||
}
|
||||
xml.endTag();
|
||||
} catch(IOException ioe) {
|
||||
writeXml(ioe, path, xml);
|
||||
}
|
||||
xml.endDocument();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.MD5MD5CRC32FileChecksum;
|
||||
import org.apache.hadoop.hdfs.DFSClient;
|
||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DatanodeJspHelper;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.znerd.xmlenc.XMLOutputter;
|
||||
|
||||
/** Servlets for file checksum */
|
||||
@InterfaceAudience.Private
|
||||
public class FileChecksumServlets {
|
||||
/** Redirect file checksum queries to an appropriate datanode. */
|
||||
@InterfaceAudience.Private
|
||||
public static class RedirectServlet extends DfsServlet {
|
||||
/** For java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Create a redirection URL */
|
||||
private URL createRedirectURL(UserGroupInformation ugi, DatanodeID host,
|
||||
HttpServletRequest request, NameNode nn)
|
||||
throws IOException {
|
||||
final String hostname = host instanceof DatanodeInfo
|
||||
? host.getHostName() : host.getIpAddr();
|
||||
final String scheme = request.getScheme();
|
||||
int port = host.getInfoPort();
|
||||
if ("https".equals(scheme)) {
|
||||
final Integer portObject = (Integer) getServletContext().getAttribute(
|
||||
DFSConfigKeys.DFS_DATANODE_HTTPS_PORT_KEY);
|
||||
if (portObject != null) {
|
||||
port = portObject;
|
||||
}
|
||||
}
|
||||
final String encodedPath = ServletUtil.getRawPath(request, "/fileChecksum");
|
||||
|
||||
String dtParam = "";
|
||||
if (UserGroupInformation.isSecurityEnabled()) {
|
||||
String tokenString = ugi.getTokens().iterator().next().encodeToUrlString();
|
||||
dtParam = JspHelper.getDelegationTokenUrlParam(tokenString);
|
||||
}
|
||||
String addr = nn.getNameNodeAddressHostPortString();
|
||||
String addrParam = JspHelper.getUrlParam(JspHelper.NAMENODE_ADDRESS, addr);
|
||||
|
||||
return new URL(scheme, hostname, port,
|
||||
"/getFileChecksum" + encodedPath + '?' +
|
||||
"ugi=" + ServletUtil.encodeQueryValue(ugi.getShortUserName()) +
|
||||
dtParam + addrParam);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response
|
||||
) throws ServletException, IOException {
|
||||
final ServletContext context = getServletContext();
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(context);
|
||||
final UserGroupInformation ugi = getUGI(request, conf);
|
||||
final NameNode namenode = NameNodeHttpServer.getNameNodeFromContext(
|
||||
context);
|
||||
final DatanodeID datanode = NamenodeJspHelper.getRandomDatanode(namenode);
|
||||
try {
|
||||
response.sendRedirect(
|
||||
createRedirectURL(ugi, datanode, request, namenode).toString());
|
||||
} catch (IOException e) {
|
||||
response.sendError(400, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get FileChecksum */
|
||||
@InterfaceAudience.Private
|
||||
public static class GetServlet extends DfsServlet {
|
||||
/** For java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response
|
||||
) throws ServletException, IOException {
|
||||
final PrintWriter out = response.getWriter();
|
||||
final String path = ServletUtil.getDecodedPath(request, "/getFileChecksum");
|
||||
final XMLOutputter xml = new XMLOutputter(out, "UTF-8");
|
||||
xml.declaration();
|
||||
|
||||
final ServletContext context = getServletContext();
|
||||
final DataNode datanode = (DataNode) context.getAttribute("datanode");
|
||||
final Configuration conf =
|
||||
new HdfsConfiguration(datanode.getConf());
|
||||
|
||||
try {
|
||||
final DFSClient dfs = DatanodeJspHelper.getDFSClient(request,
|
||||
datanode, conf, getUGI(request, conf));
|
||||
final MD5MD5CRC32FileChecksum checksum = dfs.getFileChecksum(path);
|
||||
MD5MD5CRC32FileChecksum.write(xml, checksum);
|
||||
} catch(IOException ioe) {
|
||||
writeXml(ioe, path, xml);
|
||||
} catch (InterruptedException e) {
|
||||
writeXml(e, path, xml);
|
||||
}
|
||||
xml.endDocument();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeID;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
||||
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
|
||||
/** Redirect queries about the hosted filesystem to an appropriate datanode.
|
||||
* @see org.apache.hadoop.hdfs.web.HftpFileSystem
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class FileDataServlet extends DfsServlet {
|
||||
/** For java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Create a redirection URL */
|
||||
private URL createRedirectURL(String path, String encodedPath, HdfsFileStatus status,
|
||||
UserGroupInformation ugi, ClientProtocol nnproxy, HttpServletRequest request, String dt)
|
||||
throws IOException {
|
||||
String scheme = request.getScheme();
|
||||
final LocatedBlocks blks = nnproxy.getBlockLocations(
|
||||
status.getFullPath(new Path(path)).toUri().getPath(), 0, 1);
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(
|
||||
getServletContext());
|
||||
final DatanodeID host = pickSrcDatanode(blks, status, conf);
|
||||
final String hostname;
|
||||
if (host instanceof DatanodeInfo) {
|
||||
hostname = host.getHostName();
|
||||
} else {
|
||||
hostname = host.getIpAddr();
|
||||
}
|
||||
|
||||
int port = "https".equals(scheme) ? host.getInfoSecurePort() : host
|
||||
.getInfoPort();
|
||||
|
||||
String dtParam = "";
|
||||
if (dt != null) {
|
||||
dtParam = JspHelper.getDelegationTokenUrlParam(dt);
|
||||
}
|
||||
|
||||
// Add namenode address to the url params
|
||||
NameNode nn = NameNodeHttpServer.getNameNodeFromContext(
|
||||
getServletContext());
|
||||
String addr = nn.getNameNodeAddressHostPortString();
|
||||
String addrParam = JspHelper.getUrlParam(JspHelper.NAMENODE_ADDRESS, addr);
|
||||
|
||||
return new URL(scheme, hostname, port,
|
||||
"/streamFile" + encodedPath + '?' +
|
||||
"ugi=" + ServletUtil.encodeQueryValue(ugi.getShortUserName()) +
|
||||
dtParam + addrParam);
|
||||
}
|
||||
|
||||
/** Select a datanode to service this request.
|
||||
* Currently, this looks at no more than the first five blocks of a file,
|
||||
* selecting a datanode randomly from the most represented.
|
||||
* @param conf
|
||||
*/
|
||||
private DatanodeID pickSrcDatanode(LocatedBlocks blks, HdfsFileStatus i,
|
||||
Configuration conf) throws IOException {
|
||||
if (i.getLen() == 0 || blks.getLocatedBlocks().size() <= 0) {
|
||||
// pick a random datanode
|
||||
NameNode nn = NameNodeHttpServer.getNameNodeFromContext(
|
||||
getServletContext());
|
||||
return NamenodeJspHelper.getRandomDatanode(nn);
|
||||
}
|
||||
return JspHelper.bestNode(blks, conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Service a GET request as described below.
|
||||
* Request:
|
||||
* {@code
|
||||
* GET http://<nn>:<port>/data[/<path>] HTTP/1.1
|
||||
* }
|
||||
*/
|
||||
@Override
|
||||
public void doGet(final HttpServletRequest request,
|
||||
final HttpServletResponse response)
|
||||
throws IOException {
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(
|
||||
getServletContext());
|
||||
final UserGroupInformation ugi = getUGI(request, conf);
|
||||
|
||||
try {
|
||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws IOException {
|
||||
ClientProtocol nn = createNameNodeProxy();
|
||||
final String path = ServletUtil.getDecodedPath(request, "/data");
|
||||
final String encodedPath = ServletUtil.getRawPath(request, "/data");
|
||||
String delegationToken = request
|
||||
.getParameter(JspHelper.DELEGATION_PARAMETER_NAME);
|
||||
|
||||
HdfsFileStatus info = nn.getFileInfo(path);
|
||||
if (info != null && !info.isDir()) {
|
||||
response.sendRedirect(createRedirectURL(path, encodedPath,
|
||||
info, ugi, nn, request, delegationToken).toString());
|
||||
} else if (info == null) {
|
||||
response.sendError(400, "File not found " + path);
|
||||
} else {
|
||||
response.sendError(400, path + ": is a directory");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
response.sendError(400, e.getMessage());
|
||||
} catch (InterruptedException e) {
|
||||
response.sendError(400, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
|
||||
/**
|
||||
* Serve delegation tokens over http for use in hftp.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class GetDelegationTokenServlet extends DfsServlet {
|
||||
private static final Log LOG = LogFactory.getLog(GetDelegationTokenServlet.class);
|
||||
public static final String PATH_SPEC = "/getDelegationToken";
|
||||
public static final String RENEWER = "renewer";
|
||||
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
final UserGroupInformation ugi;
|
||||
final ServletContext context = getServletContext();
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(context);
|
||||
try {
|
||||
ugi = getUGI(req, conf);
|
||||
} catch(IOException ioe) {
|
||||
LOG.info("Request for token received with no authentication from "
|
||||
+ req.getRemoteAddr(), ioe);
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Unable to identify or authenticate user");
|
||||
return;
|
||||
}
|
||||
LOG.info("Sending token: {" + ugi.getUserName() + "," + req.getRemoteAddr() +"}");
|
||||
final NameNode nn = NameNodeHttpServer.getNameNodeFromContext(context);
|
||||
String renewer = req.getParameter(RENEWER);
|
||||
final String renewerFinal = (renewer == null) ?
|
||||
req.getUserPrincipal().getName() : renewer;
|
||||
|
||||
DataOutputStream dos = null;
|
||||
try {
|
||||
dos = new DataOutputStream(resp.getOutputStream());
|
||||
final DataOutputStream dosFinal = dos; // for doAs block
|
||||
ugi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws IOException {
|
||||
final Credentials ts = DelegationTokenSecretManager.createCredentials(
|
||||
nn, ugi, renewerFinal);
|
||||
ts.write(dosFinal);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
} catch(Exception e) {
|
||||
LOG.info("Exception while sending token. Re-throwing ", e);
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
if(dos != null) dos.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
|
||||
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
|
||||
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.hdfs.web.HftpFileSystem;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.apache.hadoop.util.VersionInfo;
|
||||
import org.znerd.xmlenc.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Obtain meta-information about a filesystem.
|
||||
* @see org.apache.hadoop.hdfs.web.HftpFileSystem
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
public class ListPathsServlet extends DfsServlet {
|
||||
/** For java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final ThreadLocal<SimpleDateFormat> df =
|
||||
new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return HftpFileSystem.getDateFormat();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Write a node to output.
|
||||
* Node information includes path, modification, permission, owner and group.
|
||||
* For files, it also includes size, replication and block-size.
|
||||
*/
|
||||
static void writeInfo(final Path fullpath, final HdfsFileStatus i,
|
||||
final XMLOutputter doc) throws IOException {
|
||||
final SimpleDateFormat ldf = df.get();
|
||||
doc.startTag(i.isDir() ? "directory" : "file");
|
||||
doc.attribute("path", fullpath.toUri().getPath());
|
||||
doc.attribute("modified", ldf.format(new Date(i.getModificationTime())));
|
||||
doc.attribute("accesstime", ldf.format(new Date(i.getAccessTime())));
|
||||
if (!i.isDir()) {
|
||||
doc.attribute("size", String.valueOf(i.getLen()));
|
||||
doc.attribute("replication", String.valueOf(i.getReplication()));
|
||||
doc.attribute("blocksize", String.valueOf(i.getBlockSize()));
|
||||
}
|
||||
doc.attribute("permission", (i.isDir()? "d": "-") + i.getPermission());
|
||||
doc.attribute("owner", i.getOwner());
|
||||
doc.attribute("group", i.getGroup());
|
||||
doc.endTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map from the query string, setting values and defaults.
|
||||
*/
|
||||
protected Map<String,String> buildRoot(HttpServletRequest request,
|
||||
XMLOutputter doc) {
|
||||
final String path = ServletUtil.getDecodedPath(request, "/listPaths");
|
||||
final String exclude = request.getParameter("exclude") != null
|
||||
? request.getParameter("exclude") : "";
|
||||
final String filter = request.getParameter("filter") != null
|
||||
? request.getParameter("filter") : ".*";
|
||||
final boolean recur = request.getParameter("recursive") != null
|
||||
&& "yes".equals(request.getParameter("recursive"));
|
||||
|
||||
Map<String, String> root = new HashMap<String, String>();
|
||||
root.put("path", path);
|
||||
root.put("recursive", recur ? "yes" : "no");
|
||||
root.put("filter", filter);
|
||||
root.put("exclude", exclude);
|
||||
root.put("time", df.get().format(new Date()));
|
||||
root.put("version", VersionInfo.getVersion());
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service a GET request as described below.
|
||||
* Request:
|
||||
* {@code
|
||||
* GET http://<nn>:<port>/listPaths[/<path>][<?option>[&option]*] HTTP/1.1
|
||||
* }
|
||||
*
|
||||
* Where <i>option</i> (default) in:
|
||||
* recursive ("no")
|
||||
* filter (".*")
|
||||
* exclude ("\..*\.crc")
|
||||
*
|
||||
* Response: A flat list of files/directories in the following format:
|
||||
* {@code
|
||||
* <listing path="..." recursive="(yes|no)" filter="..."
|
||||
* time="yyyy-MM-dd hh:mm:ss UTC" version="...">
|
||||
* <directory path="..." modified="yyyy-MM-dd hh:mm:ss"/>
|
||||
* <file path="..." modified="yyyy-MM-dd'T'hh:mm:ssZ" accesstime="yyyy-MM-dd'T'hh:mm:ssZ"
|
||||
* blocksize="..."
|
||||
* replication="..." size="..."/>
|
||||
* </listing>
|
||||
* }
|
||||
*/
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
final PrintWriter out = response.getWriter();
|
||||
final XMLOutputter doc = new XMLOutputter(out, "UTF-8");
|
||||
|
||||
final Map<String, String> root = buildRoot(request, doc);
|
||||
final String path = root.get("path");
|
||||
final String filePath = ServletUtil.getDecodedPath(request, "/listPaths");
|
||||
|
||||
try {
|
||||
final boolean recur = "yes".equals(root.get("recursive"));
|
||||
final Pattern filter = Pattern.compile(root.get("filter"));
|
||||
final Pattern exclude = Pattern.compile(root.get("exclude"));
|
||||
final Configuration conf =
|
||||
(Configuration) getServletContext().getAttribute(JspHelper.CURRENT_CONF);
|
||||
|
||||
getUGI(request, conf).doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws IOException {
|
||||
ClientProtocol nn = createNameNodeProxy();
|
||||
doc.declaration();
|
||||
doc.startTag("listing");
|
||||
for (Map.Entry<String, String> m : root.entrySet()) {
|
||||
doc.attribute(m.getKey(), m.getValue());
|
||||
}
|
||||
|
||||
HdfsFileStatus base = nn.getFileInfo(filePath);
|
||||
if ((base != null) && base.isDir()) {
|
||||
writeInfo(base.getFullPath(new Path(path)), base, doc);
|
||||
}
|
||||
|
||||
Stack<String> pathstack = new Stack<String>();
|
||||
pathstack.push(path);
|
||||
while (!pathstack.empty()) {
|
||||
String p = pathstack.pop();
|
||||
try {
|
||||
byte[] lastReturnedName = HdfsFileStatus.EMPTY_NAME;
|
||||
DirectoryListing thisListing;
|
||||
do {
|
||||
assert lastReturnedName != null;
|
||||
thisListing = nn.getListing(p, lastReturnedName, false);
|
||||
if (thisListing == null) {
|
||||
if (lastReturnedName.length == 0) {
|
||||
LOG
|
||||
.warn("ListPathsServlet - Path " + p
|
||||
+ " does not exist");
|
||||
}
|
||||
break;
|
||||
}
|
||||
HdfsFileStatus[] listing = thisListing.getPartialListing();
|
||||
for (HdfsFileStatus i : listing) {
|
||||
final Path fullpath = i.getFullPath(new Path(p));
|
||||
final String localName = fullpath.getName();
|
||||
if (exclude.matcher(localName).matches()
|
||||
|| !filter.matcher(localName).matches()) {
|
||||
continue;
|
||||
}
|
||||
if (recur && i.isDir()) {
|
||||
pathstack.push(new Path(p, localName).toUri().getPath());
|
||||
}
|
||||
writeInfo(fullpath, i, doc);
|
||||
}
|
||||
lastReturnedName = thisListing.getLastName();
|
||||
} while (thisListing.hasMore());
|
||||
} catch (IOException re) {
|
||||
writeXml(re, p, doc);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch(IOException ioe) {
|
||||
writeXml(ioe, path, doc);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warn("ListPathServlet encountered InterruptedException", e);
|
||||
response.sendError(400, e.getMessage());
|
||||
} finally {
|
||||
if (doc != null) {
|
||||
doc.endDocument();
|
||||
}
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with this
|
||||
* work for additional information regarding copyright ownership. The ASF
|
||||
* licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
/**
|
||||
* Renew delegation tokens over http for use in hftp.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class RenewDelegationTokenServlet extends DfsServlet {
|
||||
private static final Log LOG = LogFactory.getLog(RenewDelegationTokenServlet.class);
|
||||
public static final String PATH_SPEC = "/renewDelegationToken";
|
||||
public static final String TOKEN = "token";
|
||||
|
||||
@Override
|
||||
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
|
||||
throws ServletException, IOException {
|
||||
final UserGroupInformation ugi;
|
||||
final ServletContext context = getServletContext();
|
||||
final Configuration conf = NameNodeHttpServer.getConfFromContext(context);
|
||||
try {
|
||||
ugi = getUGI(req, conf);
|
||||
} catch(IOException ioe) {
|
||||
LOG.info("Request for token received with no authentication from "
|
||||
+ req.getRemoteAddr(), ioe);
|
||||
resp.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||
"Unable to identify or authenticate user");
|
||||
return;
|
||||
}
|
||||
final NameNode nn = NameNodeHttpServer.getNameNodeFromContext(context);
|
||||
String tokenString = req.getParameter(TOKEN);
|
||||
if (tokenString == null) {
|
||||
resp.sendError(HttpServletResponse.SC_MULTIPLE_CHOICES,
|
||||
"Token to renew not specified");
|
||||
}
|
||||
final Token<DelegationTokenIdentifier> token =
|
||||
new Token<DelegationTokenIdentifier>();
|
||||
token.decodeFromUrlString(tokenString);
|
||||
|
||||
try {
|
||||
long result = ugi.doAs(new PrivilegedExceptionAction<Long>() {
|
||||
@Override
|
||||
public Long run() throws Exception {
|
||||
return nn.getRpcServer().renewDelegationToken(token);
|
||||
}
|
||||
});
|
||||
final PrintWriter os = new PrintWriter(new OutputStreamWriter(
|
||||
resp.getOutputStream(), Charsets.UTF_8));
|
||||
os.println(result);
|
||||
os.close();
|
||||
} catch(Exception e) {
|
||||
// transfer exception over the http
|
||||
String exceptionClass = e.getClass().getName();
|
||||
String exceptionMsg = e.getLocalizedMessage();
|
||||
String strException = exceptionClass + ";" + exceptionMsg;
|
||||
LOG.info("Exception while renewing token. Re-throwing. s=" + strException, e);
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, strException);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,168 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.FSInputStream;
|
||||
import org.apache.hadoop.hdfs.DFSClient;
|
||||
import org.apache.hadoop.hdfs.DFSInputStream;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DatanodeJspHelper;
|
||||
import org.apache.hadoop.io.IOUtils;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.mortbay.jetty.InclusiveByteRange;
|
||||
|
||||
@InterfaceAudience.Private
|
||||
public class StreamFile extends DfsServlet {
|
||||
/** for java.io.Serializable */
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String CONTENT_LENGTH = "Content-Length";
|
||||
|
||||
/* Return a DFS client to use to make the given HTTP request */
|
||||
protected DFSClient getDFSClient(HttpServletRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
final Configuration conf =
|
||||
(Configuration) getServletContext().getAttribute(JspHelper.CURRENT_CONF);
|
||||
UserGroupInformation ugi = getUGI(request, conf);
|
||||
final ServletContext context = getServletContext();
|
||||
final DataNode datanode = (DataNode) context.getAttribute("datanode");
|
||||
return DatanodeJspHelper.getDFSClient(request, datanode, conf, ugi);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
final String path = ServletUtil.getDecodedPath(request, "/streamFile");
|
||||
final String rawPath = ServletUtil.getRawPath(request, "/streamFile");
|
||||
final String filename = JspHelper.validatePath(path);
|
||||
final String rawFilename = JspHelper.validatePath(rawPath);
|
||||
if (filename == null) {
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.print("Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
Enumeration<String> reqRanges = request.getHeaders("Range");
|
||||
if (reqRanges != null && !reqRanges.hasMoreElements()) {
|
||||
reqRanges = null;
|
||||
}
|
||||
|
||||
DFSClient dfs;
|
||||
try {
|
||||
dfs = getDFSClient(request);
|
||||
} catch (InterruptedException e) {
|
||||
response.sendError(400, e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
DFSInputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
in = dfs.open(filename);
|
||||
out = response.getOutputStream();
|
||||
final long fileLen = in.getFileLength();
|
||||
if (reqRanges != null) {
|
||||
List<InclusiveByteRange> ranges =
|
||||
InclusiveByteRange.satisfiableRanges(reqRanges, fileLen);
|
||||
StreamFile.sendPartialData(in, out, response, fileLen, ranges);
|
||||
} else {
|
||||
// No ranges, so send entire file
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" +
|
||||
rawFilename + "\"");
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader(CONTENT_LENGTH, "" + fileLen);
|
||||
StreamFile.copyFromOffset(in, out, 0L, fileLen);
|
||||
}
|
||||
in.close();
|
||||
in = null;
|
||||
out.close();
|
||||
out = null;
|
||||
dfs.close();
|
||||
dfs = null;
|
||||
} catch (IOException ioe) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("response.isCommitted()=" + response.isCommitted(), ioe);
|
||||
}
|
||||
throw ioe;
|
||||
} finally {
|
||||
IOUtils.cleanup(LOG, in);
|
||||
IOUtils.cleanup(LOG, out);
|
||||
IOUtils.cleanup(LOG, dfs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a partial content response with the given range. If there are
|
||||
* no satisfiable ranges, or if multiple ranges are requested, which
|
||||
* is unsupported, respond with range not satisfiable.
|
||||
*
|
||||
* @param in stream to read from
|
||||
* @param out stream to write to
|
||||
* @param response http response to use
|
||||
* @param contentLength for the response header
|
||||
* @param ranges to write to respond with
|
||||
* @throws IOException on error sending the response
|
||||
*/
|
||||
static void sendPartialData(FSInputStream in,
|
||||
OutputStream out,
|
||||
HttpServletResponse response,
|
||||
long contentLength,
|
||||
List<InclusiveByteRange> ranges)
|
||||
throws IOException {
|
||||
if (ranges == null || ranges.size() != 1) {
|
||||
response.setContentLength(0);
|
||||
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
response.setHeader("Content-Range",
|
||||
InclusiveByteRange.to416HeaderRangeString(contentLength));
|
||||
} else {
|
||||
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
|
||||
long singleLength = singleSatisfiableRange.getSize(contentLength);
|
||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
|
||||
response.setHeader("Content-Range",
|
||||
singleSatisfiableRange.toHeaderRangeString(contentLength));
|
||||
copyFromOffset(in, out,
|
||||
singleSatisfiableRange.getFirst(contentLength),
|
||||
singleLength);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy count bytes at the given offset from one stream to another */
|
||||
static void copyFromOffset(FSInputStream in, OutputStream out, long offset,
|
||||
long count) throws IOException {
|
||||
in.seek(offset);
|
||||
IOUtils.copyBytes(in, out, count, false);
|
||||
}
|
||||
}
|
|
@ -1,729 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hdfs.web;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.ContentSummary;
|
||||
import org.apache.hadoop.fs.DelegationTokenRenewer;
|
||||
import org.apache.hadoop.fs.FSDataInputStream;
|
||||
import org.apache.hadoop.fs.FSDataOutputStream;
|
||||
import org.apache.hadoop.fs.FileChecksum;
|
||||
import org.apache.hadoop.fs.FileStatus;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.MD5MD5CRC32FileChecksum;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.fs.permission.FsPermission;
|
||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||
import org.apache.hadoop.hdfs.DFSUtil;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.hdfs.tools.DelegationTokenFetcher;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.ipc.RemoteException;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.SecurityUtil;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||
import org.apache.hadoop.util.Progressable;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import org.xml.sax.helpers.XMLReaderFactory;
|
||||
|
||||
/**
|
||||
* An implementation of a protocol for accessing filesystems over HTTP.
|
||||
* The following implementation provides a limited, read-only interface
|
||||
* to a filesystem over HTTP.
|
||||
* @see org.apache.hadoop.hdfs.server.namenode.ListPathsServlet
|
||||
* @see org.apache.hadoop.hdfs.server.namenode.FileDataServlet
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
@InterfaceStability.Evolving
|
||||
public class HftpFileSystem extends FileSystem
|
||||
implements DelegationTokenRenewer.Renewable, TokenAspect.TokenManagementDelegator {
|
||||
public static final String SCHEME = "hftp";
|
||||
|
||||
static {
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
}
|
||||
|
||||
URLConnectionFactory connectionFactory;
|
||||
|
||||
public static final Text TOKEN_KIND = new Text("HFTP delegation");
|
||||
|
||||
protected UserGroupInformation ugi;
|
||||
private URI hftpURI;
|
||||
|
||||
protected URI nnUri;
|
||||
|
||||
public static final String HFTP_TIMEZONE = "UTC";
|
||||
public static final String HFTP_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
|
||||
|
||||
protected TokenAspect<? extends HftpFileSystem> tokenAspect;
|
||||
private Token<?> delegationToken;
|
||||
private Token<?> renewToken;
|
||||
protected Text tokenServiceName;
|
||||
|
||||
@Override
|
||||
public URI getCanonicalUri() {
|
||||
return super.getCanonicalUri();
|
||||
}
|
||||
|
||||
public static final SimpleDateFormat getDateFormat() {
|
||||
final SimpleDateFormat df = new SimpleDateFormat(HFTP_DATE_FORMAT);
|
||||
df.setTimeZone(TimeZone.getTimeZone(HFTP_TIMEZONE));
|
||||
return df;
|
||||
}
|
||||
|
||||
protected static final ThreadLocal<SimpleDateFormat> df =
|
||||
new ThreadLocal<SimpleDateFormat>() {
|
||||
@Override
|
||||
protected SimpleDateFormat initialValue() {
|
||||
return getDateFormat();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected int getDefaultPort() {
|
||||
return DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* We generate the address with one of the following ports, in
|
||||
* order of preference.
|
||||
* 1. Port from the hftp URI e.g. hftp://namenode:4000/ will return 4000.
|
||||
* 2. Port configured via DFS_NAMENODE_HTTP_PORT_KEY
|
||||
* 3. DFS_NAMENODE_HTTP_PORT_DEFAULT i.e. 50070.
|
||||
*
|
||||
* @param uri
|
||||
*/
|
||||
protected InetSocketAddress getNamenodeAddr(URI uri) {
|
||||
// use authority so user supplied uri can override port
|
||||
return NetUtils.createSocketAddr(uri.getAuthority(), getDefaultPort());
|
||||
}
|
||||
|
||||
protected URI getNamenodeUri(URI uri) {
|
||||
return DFSUtil.createUri(getUnderlyingProtocol(), getNamenodeAddr(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* See the documentation of {@Link #getNamenodeAddr(URI)} for the logic
|
||||
* behind selecting the canonical service name.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getCanonicalServiceName() {
|
||||
return SecurityUtil.buildTokenService(nnUri).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
return NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the protocol scheme for the FileSystem.
|
||||
* <p/>
|
||||
*
|
||||
* @return <code>hftp</code>
|
||||
*/
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return SCHEME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize connectionFactory and tokenAspect. This function is intended to
|
||||
* be overridden by HsFtpFileSystem.
|
||||
*/
|
||||
protected void initTokenAspect() {
|
||||
tokenAspect = new TokenAspect<HftpFileSystem>(this, tokenServiceName, TOKEN_KIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final URI name, final Configuration conf)
|
||||
throws IOException {
|
||||
super.initialize(name, conf);
|
||||
setConf(conf);
|
||||
this.connectionFactory = URLConnectionFactory
|
||||
.newDefaultURLConnectionFactory(conf);
|
||||
this.ugi = UserGroupInformation.getCurrentUser();
|
||||
this.nnUri = getNamenodeUri(name);
|
||||
this.tokenServiceName = SecurityUtil.buildTokenService(nnUri);
|
||||
|
||||
try {
|
||||
this.hftpURI = new URI(name.getScheme(), name.getAuthority(),
|
||||
null, null, null);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
initTokenAspect();
|
||||
if (UserGroupInformation.isSecurityEnabled()) {
|
||||
tokenAspect.initDelegationToken(ugi);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token<?> getRenewToken() {
|
||||
return renewToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying protocol that is used to talk to the namenode.
|
||||
*/
|
||||
protected String getUnderlyingProtocol() {
|
||||
return "http";
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized <T extends TokenIdentifier> void setDelegationToken(Token<T> token) {
|
||||
/**
|
||||
* XXX The kind of the token has been changed by DelegationTokenFetcher. We
|
||||
* use the token for renewal, since the reflection utilities needs the value
|
||||
* of the kind field to correctly renew the token.
|
||||
*
|
||||
* For other operations, however, the client has to send a
|
||||
* HDFS_DELEGATION_KIND token over the wire so that it can talk to Hadoop
|
||||
* 0.20.203 clusters. Later releases fix this problem. See HDFS-5440 for
|
||||
* more details.
|
||||
*/
|
||||
renewToken = token;
|
||||
delegationToken = new Token<T>(token);
|
||||
delegationToken.setKind(DelegationTokenIdentifier.HDFS_DELEGATION_KIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Token<?> getDelegationToken(final String renewer)
|
||||
throws IOException {
|
||||
try {
|
||||
// Renew TGT if needed
|
||||
UserGroupInformation connectUgi = ugi.getRealUser();
|
||||
final String proxyUser = connectUgi == null ? null : ugi
|
||||
.getShortUserName();
|
||||
if (connectUgi == null) {
|
||||
connectUgi = ugi;
|
||||
}
|
||||
return connectUgi.doAs(new PrivilegedExceptionAction<Token<?>>() {
|
||||
@Override
|
||||
public Token<?> run() throws IOException {
|
||||
Credentials c;
|
||||
try {
|
||||
c = DelegationTokenFetcher.getDTfromRemote(connectionFactory,
|
||||
nnUri, renewer, proxyUser);
|
||||
} catch (IOException e) {
|
||||
if (e.getCause() instanceof ConnectException) {
|
||||
LOG.warn("Couldn't connect to " + nnUri +
|
||||
", assuming security is disabled");
|
||||
return null;
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Exception getting delegation token", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
for (Token<? extends TokenIdentifier> t : c.getAllTokens()) {
|
||||
if(LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got dt for " + getUri() + ";t.service="
|
||||
+t.getService());
|
||||
}
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return hftpURI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a URL pointing to given path on the namenode.
|
||||
*
|
||||
* @param path to obtain the URL for
|
||||
* @param query string to append to the path
|
||||
* @return namenode URL referring to the given path
|
||||
* @throws IOException on error constructing the URL
|
||||
*/
|
||||
protected URL getNamenodeURL(String path, String query) throws IOException {
|
||||
final URL url = new URL(getUnderlyingProtocol(), nnUri.getHost(),
|
||||
nnUri.getPort(), path + '?' + query);
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("url=" + url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get encoded UGI parameter string for a URL.
|
||||
*
|
||||
* @return user_shortname,group1,group2...
|
||||
*/
|
||||
private String getEncodedUgiParameter() {
|
||||
StringBuilder ugiParameter = new StringBuilder(
|
||||
ServletUtil.encodeQueryValue(ugi.getShortUserName()));
|
||||
for(String g: ugi.getGroupNames()) {
|
||||
ugiParameter.append(",");
|
||||
ugiParameter.append(ServletUtil.encodeQueryValue(g));
|
||||
}
|
||||
return ugiParameter.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an HTTP connection to the namenode to read file data and metadata.
|
||||
* @param path The path component of the URL
|
||||
* @param query The query component of the URL
|
||||
*/
|
||||
protected HttpURLConnection openConnection(String path, String query)
|
||||
throws IOException {
|
||||
query = addDelegationTokenParam(query);
|
||||
final URL url = getNamenodeURL(path, query);
|
||||
final HttpURLConnection connection;
|
||||
connection = (HttpURLConnection)connectionFactory.openConnection(url);
|
||||
connection.setRequestMethod("GET");
|
||||
connection.connect();
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected String addDelegationTokenParam(String query) throws IOException {
|
||||
String tokenString = null;
|
||||
if (UserGroupInformation.isSecurityEnabled()) {
|
||||
synchronized (this) {
|
||||
tokenAspect.ensureTokenInitialized();
|
||||
if (delegationToken != null) {
|
||||
tokenString = delegationToken.encodeToUrlString();
|
||||
return (query + JspHelper.getDelegationTokenUrlParam(tokenString));
|
||||
}
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
static class RangeHeaderUrlOpener extends ByteRangeInputStream.URLOpener {
|
||||
private final URLConnectionFactory connFactory;
|
||||
|
||||
RangeHeaderUrlOpener(URLConnectionFactory connFactory, final URL url) {
|
||||
super(url);
|
||||
this.connFactory = connFactory;
|
||||
}
|
||||
|
||||
protected HttpURLConnection openConnection() throws IOException {
|
||||
return (HttpURLConnection)connFactory.openConnection(url);
|
||||
}
|
||||
|
||||
/** Use HTTP Range header for specifying offset. */
|
||||
@Override
|
||||
protected HttpURLConnection connect(final long offset,
|
||||
final boolean resolved) throws IOException {
|
||||
final HttpURLConnection conn = openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
if (offset != 0L) {
|
||||
conn.setRequestProperty("Range", "bytes=" + offset + "-");
|
||||
}
|
||||
conn.connect();
|
||||
|
||||
//Expects HTTP_OK or HTTP_PARTIAL response codes.
|
||||
final int code = conn.getResponseCode();
|
||||
if (offset != 0L && code != HttpURLConnection.HTTP_PARTIAL) {
|
||||
throw new IOException("HTTP_PARTIAL expected, received " + code);
|
||||
} else if (offset == 0L && code != HttpURLConnection.HTTP_OK) {
|
||||
throw new IOException("HTTP_OK expected, received " + code);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
static class RangeHeaderInputStream extends ByteRangeInputStream {
|
||||
RangeHeaderInputStream(RangeHeaderUrlOpener o, RangeHeaderUrlOpener r) {
|
||||
super(o, r);
|
||||
}
|
||||
|
||||
RangeHeaderInputStream(URLConnectionFactory connFactory, final URL url) {
|
||||
this(new RangeHeaderUrlOpener(connFactory, url),
|
||||
new RangeHeaderUrlOpener(connFactory, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL getResolvedUrl(final HttpURLConnection connection) {
|
||||
return connection.getURL();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSDataInputStream open(Path f, int buffersize) throws IOException {
|
||||
f = f.makeQualified(getUri(), getWorkingDirectory());
|
||||
String path = "/data" + ServletUtil.encodePath(f.toUri().getPath());
|
||||
String query = addDelegationTokenParam("ugi=" + getEncodedUgiParameter());
|
||||
URL u = getNamenodeURL(path, query);
|
||||
return new FSDataInputStream(new RangeHeaderInputStream(connectionFactory, u));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
tokenAspect.removeRenewAction();
|
||||
}
|
||||
|
||||
/** Class to parse and store a listing reply from the server. */
|
||||
class LsParser extends DefaultHandler {
|
||||
|
||||
final ArrayList<FileStatus> fslist = new ArrayList<FileStatus>();
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname,
|
||||
Attributes attrs) throws SAXException {
|
||||
if ("listing".equals(qname)) return;
|
||||
if (!"file".equals(qname) && !"directory".equals(qname)) {
|
||||
if (RemoteException.class.getSimpleName().equals(qname)) {
|
||||
throw new SAXException(RemoteException.valueOf(attrs));
|
||||
}
|
||||
throw new SAXException("Unrecognized entry: " + qname);
|
||||
}
|
||||
long modif;
|
||||
long atime = 0;
|
||||
try {
|
||||
final SimpleDateFormat ldf = df.get();
|
||||
modif = ldf.parse(attrs.getValue("modified")).getTime();
|
||||
String astr = attrs.getValue("accesstime");
|
||||
if (astr != null) {
|
||||
atime = ldf.parse(astr).getTime();
|
||||
}
|
||||
} catch (ParseException e) { throw new SAXException(e); }
|
||||
FileStatus fs = "file".equals(qname)
|
||||
? new FileStatus(
|
||||
Long.parseLong(attrs.getValue("size")), false,
|
||||
Short.valueOf(attrs.getValue("replication")).shortValue(),
|
||||
Long.parseLong(attrs.getValue("blocksize")),
|
||||
modif, atime, FsPermission.valueOf(attrs.getValue("permission")),
|
||||
attrs.getValue("owner"), attrs.getValue("group"),
|
||||
HftpFileSystem.this.makeQualified(
|
||||
new Path(getUri().toString(), attrs.getValue("path"))))
|
||||
: new FileStatus(0L, true, 0, 0L,
|
||||
modif, atime, FsPermission.valueOf(attrs.getValue("permission")),
|
||||
attrs.getValue("owner"), attrs.getValue("group"),
|
||||
HftpFileSystem.this.makeQualified(
|
||||
new Path(getUri().toString(), attrs.getValue("path"))));
|
||||
fslist.add(fs);
|
||||
}
|
||||
|
||||
private void fetchList(String path, boolean recur) throws IOException {
|
||||
try {
|
||||
XMLReader xr = XMLReaderFactory.createXMLReader();
|
||||
xr.setContentHandler(this);
|
||||
HttpURLConnection connection = openConnection(
|
||||
"/listPaths" + ServletUtil.encodePath(path),
|
||||
"ugi=" + getEncodedUgiParameter() + (recur ? "&recursive=yes" : ""));
|
||||
InputStream resp = connection.getInputStream();
|
||||
xr.parse(new InputSource(resp));
|
||||
} catch(SAXException e) {
|
||||
final Exception embedded = e.getException();
|
||||
if (embedded != null && embedded instanceof IOException) {
|
||||
throw (IOException)embedded;
|
||||
}
|
||||
throw new IOException("invalid xml directory content", e);
|
||||
}
|
||||
}
|
||||
|
||||
public FileStatus getFileStatus(Path f) throws IOException {
|
||||
fetchList(f.toUri().getPath(), false);
|
||||
if (fslist.size() == 0) {
|
||||
throw new FileNotFoundException("File does not exist: " + f);
|
||||
}
|
||||
return fslist.get(0);
|
||||
}
|
||||
|
||||
public FileStatus[] listStatus(Path f, boolean recur) throws IOException {
|
||||
fetchList(f.toUri().getPath(), recur);
|
||||
if (fslist.size() > 0 && (fslist.size() != 1 || fslist.get(0).isDirectory())) {
|
||||
fslist.remove(0);
|
||||
}
|
||||
return fslist.toArray(new FileStatus[0]);
|
||||
}
|
||||
|
||||
public FileStatus[] listStatus(Path f) throws IOException {
|
||||
return listStatus(f, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStatus[] listStatus(Path f) throws IOException {
|
||||
LsParser lsparser = new LsParser();
|
||||
return lsparser.listStatus(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStatus getFileStatus(Path f) throws IOException {
|
||||
LsParser lsparser = new LsParser();
|
||||
return lsparser.getFileStatus(f);
|
||||
}
|
||||
|
||||
private class ChecksumParser extends DefaultHandler {
|
||||
private FileChecksum filechecksum;
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname,
|
||||
Attributes attrs) throws SAXException {
|
||||
if (!MD5MD5CRC32FileChecksum.class.getName().equals(qname)) {
|
||||
if (RemoteException.class.getSimpleName().equals(qname)) {
|
||||
throw new SAXException(RemoteException.valueOf(attrs));
|
||||
}
|
||||
throw new SAXException("Unrecognized entry: " + qname);
|
||||
}
|
||||
|
||||
filechecksum = MD5MD5CRC32FileChecksum.valueOf(attrs);
|
||||
}
|
||||
|
||||
private FileChecksum getFileChecksum(String f) throws IOException {
|
||||
final HttpURLConnection connection = openConnection(
|
||||
"/fileChecksum" + ServletUtil.encodePath(f),
|
||||
"ugi=" + getEncodedUgiParameter());
|
||||
try {
|
||||
final XMLReader xr = XMLReaderFactory.createXMLReader();
|
||||
xr.setContentHandler(this);
|
||||
xr.parse(new InputSource(connection.getInputStream()));
|
||||
} catch(SAXException e) {
|
||||
final Exception embedded = e.getException();
|
||||
if (embedded != null && embedded instanceof IOException) {
|
||||
throw (IOException)embedded;
|
||||
}
|
||||
throw new IOException("invalid xml directory content", e);
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
return filechecksum;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileChecksum getFileChecksum(Path f) throws IOException {
|
||||
final String s = makeQualified(f).toUri().getPath();
|
||||
return new ChecksumParser().getFileChecksum(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getWorkingDirectory() {
|
||||
return new Path("/").makeQualified(getUri(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWorkingDirectory(Path f) { }
|
||||
|
||||
/** This optional operation is not yet supported. */
|
||||
@Override
|
||||
public FSDataOutputStream append(Path f, int bufferSize,
|
||||
Progressable progress) throws IOException {
|
||||
throw new IOException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSDataOutputStream create(Path f, FsPermission permission,
|
||||
boolean overwrite, int bufferSize, short replication,
|
||||
long blockSize, Progressable progress) throws IOException {
|
||||
throw new IOException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rename(Path src, Path dst) throws IOException {
|
||||
throw new IOException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Path f, boolean recursive) throws IOException {
|
||||
throw new IOException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdirs(Path f, FsPermission permission) throws IOException {
|
||||
throw new IOException("Not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
* A parser for parsing {@link ContentSummary} xml.
|
||||
*/
|
||||
private class ContentSummaryParser extends DefaultHandler {
|
||||
private ContentSummary contentsummary;
|
||||
|
||||
@Override
|
||||
public void startElement(String ns, String localname, String qname,
|
||||
Attributes attrs) throws SAXException {
|
||||
if (!ContentSummary.class.getName().equals(qname)) {
|
||||
if (RemoteException.class.getSimpleName().equals(qname)) {
|
||||
throw new SAXException(RemoteException.valueOf(attrs));
|
||||
}
|
||||
throw new SAXException("Unrecognized entry: " + qname);
|
||||
}
|
||||
|
||||
contentsummary = toContentSummary(attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the name node and get content summary.
|
||||
* @param path The path
|
||||
* @return The content summary for the path.
|
||||
* @throws IOException
|
||||
*/
|
||||
private ContentSummary getContentSummary(String path) throws IOException {
|
||||
final HttpURLConnection connection = openConnection(
|
||||
"/contentSummary" + ServletUtil.encodePath(path),
|
||||
"ugi=" + getEncodedUgiParameter());
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = connection.getInputStream();
|
||||
|
||||
final XMLReader xr = XMLReaderFactory.createXMLReader();
|
||||
xr.setContentHandler(this);
|
||||
xr.parse(new InputSource(in));
|
||||
} catch(FileNotFoundException fnfe) {
|
||||
//the server may not support getContentSummary
|
||||
return null;
|
||||
} catch(SAXException saxe) {
|
||||
final Exception embedded = saxe.getException();
|
||||
if (embedded != null && embedded instanceof IOException) {
|
||||
throw (IOException)embedded;
|
||||
}
|
||||
throw new IOException("Invalid xml format", saxe);
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
connection.disconnect();
|
||||
}
|
||||
return contentsummary;
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the object represented in the attributes. */
|
||||
private static ContentSummary toContentSummary(Attributes attrs
|
||||
) throws SAXException {
|
||||
final String length = attrs.getValue("length");
|
||||
final String fileCount = attrs.getValue("fileCount");
|
||||
final String directoryCount = attrs.getValue("directoryCount");
|
||||
final String quota = attrs.getValue("quota");
|
||||
final String spaceConsumed = attrs.getValue("spaceConsumed");
|
||||
final String spaceQuota = attrs.getValue("spaceQuota");
|
||||
|
||||
if (length == null
|
||||
|| fileCount == null
|
||||
|| directoryCount == null
|
||||
|| quota == null
|
||||
|| spaceConsumed == null
|
||||
|| spaceQuota == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new ContentSummary(
|
||||
Long.parseLong(length),
|
||||
Long.parseLong(fileCount),
|
||||
Long.parseLong(directoryCount),
|
||||
Long.parseLong(quota),
|
||||
Long.parseLong(spaceConsumed),
|
||||
Long.parseLong(spaceQuota));
|
||||
} catch(Exception e) {
|
||||
throw new SAXException("Invalid attributes: length=" + length
|
||||
+ ", fileCount=" + fileCount
|
||||
+ ", directoryCount=" + directoryCount
|
||||
+ ", quota=" + quota
|
||||
+ ", spaceConsumed=" + spaceConsumed
|
||||
+ ", spaceQuota=" + spaceQuota, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentSummary getContentSummary(Path f) throws IOException {
|
||||
final String s = makeQualified(f).toUri().getPath();
|
||||
final ContentSummary cs = new ContentSummaryParser().getContentSummary(s);
|
||||
return cs != null? cs: super.getContentSummary(f);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public long renewDelegationToken(final Token<?> token) throws IOException {
|
||||
// update the kerberos credentials, if they are coming from a keytab
|
||||
UserGroupInformation connectUgi = ugi.getRealUser();
|
||||
if (connectUgi == null) {
|
||||
connectUgi = ugi;
|
||||
}
|
||||
try {
|
||||
return connectUgi.doAs(new PrivilegedExceptionAction<Long>() {
|
||||
@Override
|
||||
public Long run() throws Exception {
|
||||
InetSocketAddress serviceAddr = SecurityUtil
|
||||
.getTokenServiceAddr(token);
|
||||
return DelegationTokenFetcher.renewDelegationToken(connectionFactory,
|
||||
DFSUtil.createUri(getUnderlyingProtocol(), serviceAddr),
|
||||
(Token<DelegationTokenIdentifier>) token);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void cancelDelegationToken(final Token<?> token) throws IOException {
|
||||
UserGroupInformation connectUgi = ugi.getRealUser();
|
||||
if (connectUgi == null) {
|
||||
connectUgi = ugi;
|
||||
}
|
||||
try {
|
||||
connectUgi.doAs(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
InetSocketAddress serviceAddr = SecurityUtil
|
||||
.getTokenServiceAddr(token);
|
||||
DelegationTokenFetcher.cancelDelegationToken(connectionFactory,
|
||||
DFSUtil.createUri(getUnderlyingProtocol(), serviceAddr),
|
||||
(Token<DelegationTokenIdentifier>) token);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hdfs.web;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.classification.InterfaceStability;
|
||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||
import org.apache.hadoop.io.Text;
|
||||
|
||||
/**
|
||||
* An implementation of a protocol for accessing filesystems over HTTPS. The
|
||||
* following implementation provides a limited, read-only interface to a
|
||||
* filesystem over HTTPS.
|
||||
*
|
||||
* @see org.apache.hadoop.hdfs.server.namenode.ListPathsServlet
|
||||
* @see org.apache.hadoop.hdfs.server.namenode.FileDataServlet
|
||||
*/
|
||||
@InterfaceAudience.Private
|
||||
@InterfaceStability.Evolving
|
||||
public class HsftpFileSystem extends HftpFileSystem {
|
||||
public static final Text TOKEN_KIND = new Text("HSFTP delegation");
|
||||
public static final String SCHEME = "hsftp";
|
||||
|
||||
/**
|
||||
* Return the protocol scheme for the FileSystem.
|
||||
* <p/>
|
||||
*
|
||||
* @return <code>hsftp</code>
|
||||
*/
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return SCHEME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying protocol that is used to talk to the namenode.
|
||||
*/
|
||||
@Override
|
||||
protected String getUnderlyingProtocol() {
|
||||
return "https";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTokenAspect() {
|
||||
tokenAspect = new TokenAspect<HsftpFileSystem>(this, tokenServiceName,
|
||||
TOKEN_KIND);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDefaultPort() {
|
||||
return DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT;
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
~~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~~ you may not use this file except in compliance with the License.
|
||||
~~ You may obtain a copy of the License at
|
||||
~~
|
||||
~~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~~
|
||||
~~ Unless required by applicable law or agreed to in writing, software
|
||||
~~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~~ See the License for the specific language governing permissions and
|
||||
~~ limitations under the License. See accompanying LICENSE file.
|
||||
|
||||
---
|
||||
HFTP Guide
|
||||
---
|
||||
---
|
||||
${maven.build.timestamp}
|
||||
|
||||
HFTP Guide
|
||||
|
||||
%{toc|section=1|fromDepth=0}
|
||||
|
||||
* Introduction
|
||||
|
||||
HFTP is a Hadoop filesystem implementation that lets you read data from
|
||||
a remote Hadoop HDFS cluster. The reads are done via HTTP, and data is
|
||||
sourced from DataNodes. HFTP is a read-only filesystem, and will throw
|
||||
exceptions if you try to use it to write data or modify the filesystem
|
||||
state.
|
||||
|
||||
HFTP is primarily useful if you have multiple HDFS clusters with
|
||||
different versions and you need to move data from one to another. HFTP
|
||||
is wire-compatible even between different versions of HDFS. For
|
||||
example, you can do things like: <<<hadoop distcp -i hftp://sourceFS:50070/src hdfs://destFS:50070/dest>>>.
|
||||
Note that HFTP is read-only so the destination must be an HDFS filesystem.
|
||||
(Also, in this example, the distcp should be run using the configuraton of
|
||||
the new filesystem.)
|
||||
|
||||
An extension, HSFTP, uses HTTPS by default. This means that data will
|
||||
be encrypted in transit.
|
||||
|
||||
* Implementation
|
||||
|
||||
The code for HFTP lives in the Java class
|
||||
<<<org.apache.hadoop.hdfs.HftpFileSystem>>>. Likewise, HSFTP is implemented
|
||||
in <<<org.apache.hadoop.hdfs.HsftpFileSystem>>>.
|
||||
|
||||
* Configuration Options
|
||||
|
||||
*-----------------------:-----------------------------------+
|
||||
| <<Name>> | <<Description>> |
|
||||
*-----------------------:-----------------------------------+
|
||||
| <<<dfs.hftp.https.port>>> | the HTTPS port on the remote cluster. If not set,
|
||||
| | HFTP will fall back on <<<dfs.https.port>>>.
|
||||
*-----------------------:-----------------------------------+
|
||||
| <<<hdfs.service.host_ip:port>>> | Specifies the service name (for the security
|
||||
| | subsystem) associated with the HFTP filesystem running at ip:port.
|
||||
*-----------------------:-----------------------------------+
|
|
@ -1,187 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.impl.Log4JLogger;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.ContentSummary;
|
||||
import org.apache.hadoop.fs.FSDataInputStream;
|
||||
import org.apache.hadoop.fs.FSDataOutputStream;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
|
||||
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
||||
import org.apache.hadoop.hdfs.server.datanode.FSDataset;
|
||||
import org.apache.hadoop.io.IOUtils;
|
||||
import org.apache.log4j.Level;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestFiHftp {
|
||||
final Log LOG = FileSystem.LOG;
|
||||
{
|
||||
((Log4JLogger)LOG).getLogger().setLevel(Level.ALL);
|
||||
}
|
||||
|
||||
static final short DATANODE_NUM = 1;
|
||||
static final Random ran = new Random();
|
||||
static final byte[] buffer = new byte[1 << 16];
|
||||
static final MessageDigest md5;
|
||||
static {
|
||||
try {
|
||||
md5 = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] createFile(FileSystem fs, Path name, long length,
|
||||
short replication, long blocksize) throws IOException {
|
||||
final FSDataOutputStream out = fs.create(name, false, 4096,
|
||||
replication, blocksize);
|
||||
try {
|
||||
for(long n = length; n > 0; ) {
|
||||
ran.nextBytes(buffer);
|
||||
final int w = n < buffer.length? (int)n: buffer.length;
|
||||
out.write(buffer, 0, w);
|
||||
md5.update(buffer, 0, w);
|
||||
n -= w;
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeStream(out);
|
||||
}
|
||||
return md5.digest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHftpOpen() throws IOException {
|
||||
final Configuration conf = new Configuration();
|
||||
MiniDFSCluster cluster = null;
|
||||
try {
|
||||
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(DATANODE_NUM).build();
|
||||
cluster.waitActive();
|
||||
|
||||
//test with a file
|
||||
//which is larger than the servlet response buffer size
|
||||
{
|
||||
final long blocksize = 1L << 20; //
|
||||
final long filesize = 2*blocksize + 100;
|
||||
runTestHftpOpen(cluster, "/foo", blocksize, filesize);
|
||||
}
|
||||
|
||||
//test with a small file
|
||||
//which is smaller than the servlet response buffer size
|
||||
{
|
||||
final long blocksize = 1L << 10; //
|
||||
final long filesize = 2*blocksize + 100;
|
||||
runTestHftpOpen(cluster, "/small", blocksize, filesize);
|
||||
}
|
||||
} finally {
|
||||
if (cluster != null) {cluster.shutdown();}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A test with a 3GB file.
|
||||
* It may take ~6 minutes.
|
||||
*/
|
||||
void largeFileTest(final MiniDFSCluster cluster) throws IOException {
|
||||
final long blocksize = 128L << 20;
|
||||
final long filesize = 3L << 30;
|
||||
runTestHftpOpen(cluster, "/large", blocksize, filesize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blocksize
|
||||
* @param filesize must be > block size
|
||||
*/
|
||||
private void runTestHftpOpen(final MiniDFSCluster cluster, final String file,
|
||||
final long blocksize, final long filesize) throws IOException {
|
||||
//create a file
|
||||
final DistributedFileSystem dfs = (DistributedFileSystem)cluster.getFileSystem();
|
||||
final Path filepath = new Path(file);
|
||||
final byte[] filemd5 = createFile(dfs, filepath, filesize, DATANODE_NUM,
|
||||
blocksize);
|
||||
DFSTestUtil.waitReplication(dfs, filepath, DATANODE_NUM);
|
||||
|
||||
//test hftp open and read
|
||||
final HftpFileSystem hftpfs = cluster.getHftpFileSystem(0);
|
||||
{
|
||||
final FSDataInputStream in = hftpfs.open(filepath);
|
||||
long bytesRead = 0;
|
||||
try {
|
||||
for(int r; (r = in.read(buffer)) != -1; ) {
|
||||
bytesRead += r;
|
||||
md5.update(buffer, 0, r);
|
||||
}
|
||||
} finally {
|
||||
LOG.info("bytesRead=" + bytesRead);
|
||||
in.close();
|
||||
}
|
||||
Assert.assertEquals(filesize, bytesRead);
|
||||
Assert.assertArrayEquals(filemd5, md5.digest());
|
||||
}
|
||||
|
||||
//delete the second block
|
||||
final DFSClient client = dfs.getClient();
|
||||
final LocatedBlocks locatedblocks = client.getNamenode().getBlockLocations(
|
||||
file, 0, filesize);
|
||||
Assert.assertEquals((filesize - 1)/blocksize + 1,
|
||||
locatedblocks.locatedBlockCount());
|
||||
final LocatedBlock lb = locatedblocks.get(1);
|
||||
final ExtendedBlock blk = lb.getBlock();
|
||||
Assert.assertEquals(blocksize, lb.getBlockSize());
|
||||
final DatanodeInfo[] datanodeinfos = lb.getLocations();
|
||||
Assert.assertEquals(DATANODE_NUM, datanodeinfos.length);
|
||||
final DataNode dn = cluster.getDataNode(datanodeinfos[0].getIpcPort());
|
||||
LOG.info("dn=" + dn + ", blk=" + blk + " (length=" + blk.getNumBytes() + ")");
|
||||
final FSDataset data = (FSDataset)dn.getFSDataset();
|
||||
final File blkfile = data.getBlockFile(blk);
|
||||
Assert.assertTrue(blkfile.delete());
|
||||
|
||||
//read again by hftp, should get an exception
|
||||
LOG.info("hftpfs.getUri() = " + hftpfs.getUri());
|
||||
final ContentSummary cs = hftpfs.getContentSummary(filepath);
|
||||
LOG.info("hftpfs.getContentSummary = " + cs);
|
||||
Assert.assertEquals(filesize, cs.getLength());
|
||||
|
||||
final FSDataInputStream in = hftpfs.open(hftpfs.makeQualified(filepath));
|
||||
long bytesRead = 0;
|
||||
try {
|
||||
for(int r; (r = in.read(buffer)) != -1; ) {
|
||||
bytesRead += r;
|
||||
}
|
||||
Assert.fail();
|
||||
} catch(IOException ioe) {
|
||||
LOG.info("GOOD: get an exception", ioe);
|
||||
} finally {
|
||||
LOG.info("bytesRead=" + bytesRead);
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.FileStatus;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.fs.permission.FsPermission;
|
||||
import org.apache.hadoop.hdfs.server.namenode.ListPathsServlet;
|
||||
import org.apache.hadoop.hdfs.web.HftpFileSystem;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test for {@link ListPathsServlet} that serves the URL
|
||||
* http://<namenodeaddress:httpport?/listPaths
|
||||
*
|
||||
* This test does not use the servlet directly. Instead it is based on
|
||||
* {@link HftpFileSystem}, which uses this servlet to implement
|
||||
* {@link HftpFileSystem#listStatus(Path)} method.
|
||||
*/
|
||||
public class TestListPathServlet {
|
||||
private static final Configuration CONF = new HdfsConfiguration();
|
||||
private static MiniDFSCluster cluster;
|
||||
private static FileSystem fs;
|
||||
private static URI hftpURI;
|
||||
private static HftpFileSystem hftpFs;
|
||||
private final Random r = new Random();
|
||||
private final List<String> filelist = new ArrayList<String>();
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception {
|
||||
// start a cluster with single datanode
|
||||
cluster = new MiniDFSCluster.Builder(CONF).build();
|
||||
cluster.waitActive();
|
||||
fs = cluster.getFileSystem();
|
||||
|
||||
final String str = "hftp://"
|
||||
+ CONF.get(DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY);
|
||||
hftpURI = new URI(str);
|
||||
hftpFs = cluster.getHftpFileSystem(0);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void teardown() {
|
||||
cluster.shutdown();
|
||||
}
|
||||
|
||||
/** create a file with a length of <code>fileLen</code> */
|
||||
private void createFile(String fileName, long fileLen) throws IOException {
|
||||
filelist.add(hftpURI + fileName);
|
||||
final Path filePath = new Path(fileName);
|
||||
DFSTestUtil.createFile(fs, filePath, fileLen, (short) 1, r.nextLong());
|
||||
}
|
||||
|
||||
private void mkdirs(String dirName) throws IOException {
|
||||
filelist.add(hftpURI + dirName);
|
||||
fs.mkdirs(new Path(dirName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListStatus() throws Exception {
|
||||
// Empty root directory
|
||||
checkStatus("/");
|
||||
|
||||
// Root directory with files and directories
|
||||
createFile("/a", 1);
|
||||
createFile("/b", 1);
|
||||
mkdirs("/dir");
|
||||
|
||||
checkFile(new Path("/a"));
|
||||
checkFile(new Path("/b"));
|
||||
checkStatus("/");
|
||||
|
||||
// A directory with files and directories
|
||||
createFile("/dir/.a.crc", 1);
|
||||
createFile("/dir/b", 1);
|
||||
mkdirs("/dir/dir1");
|
||||
|
||||
checkFile(new Path("/dir/.a.crc"));
|
||||
checkFile(new Path("/dir/b"));
|
||||
checkStatus("/dir");
|
||||
|
||||
// Non existent path
|
||||
checkStatus("/nonexistent");
|
||||
checkStatus("/nonexistent/a");
|
||||
|
||||
final String username = UserGroupInformation.getCurrentUser().getShortUserName() + "1";
|
||||
final HftpFileSystem hftp2 = cluster.getHftpFileSystemAs(username, CONF, 0, "somegroup");
|
||||
{ //test file not found on hftp
|
||||
final Path nonexistent = new Path("/nonexistent");
|
||||
try {
|
||||
hftp2.getFileStatus(nonexistent);
|
||||
Assert.fail();
|
||||
} catch(IOException ioe) {
|
||||
FileSystem.LOG.info("GOOD: getting an exception", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
{ //test permission error on hftp
|
||||
final Path dir = new Path("/dir");
|
||||
fs.setPermission(dir, new FsPermission((short)0));
|
||||
try {
|
||||
hftp2.getFileStatus(new Path(dir, "a"));
|
||||
Assert.fail();
|
||||
} catch(IOException ioe) {
|
||||
FileSystem.LOG.info("GOOD: getting an exception", ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStatus(String listdir) throws IOException {
|
||||
final Path listpath = hftpFs.makeQualified(new Path(listdir));
|
||||
listdir = listpath.toString();
|
||||
final FileStatus[] statuslist = hftpFs.listStatus(listpath);
|
||||
for (String directory : filelist) {
|
||||
System.out.println("dir:" + directory);
|
||||
}
|
||||
for (String file : filelist) {
|
||||
System.out.println("file:" + file);
|
||||
}
|
||||
for (FileStatus status : statuslist) {
|
||||
System.out.println("status:" + status.getPath().toString() + " type "
|
||||
+ (status.isDirectory() ? "directory"
|
||||
: ( status.isFile() ? "file" : "symlink")));
|
||||
}
|
||||
for (String file : filelist) {
|
||||
boolean found = false;
|
||||
// Consider only file under the list path
|
||||
if (!file.startsWith(listpath.toString()) ||
|
||||
file.equals(listpath.toString())) {
|
||||
continue;
|
||||
}
|
||||
for (FileStatus status : statuslist) {
|
||||
if (status.getPath().toString().equals(file)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertTrue("Directory/file not returned in list status " + file,
|
||||
found);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFile(final Path f) throws IOException {
|
||||
final Path hdfspath = fs.makeQualified(f);
|
||||
final FileStatus hdfsstatus = fs.getFileStatus(hdfspath);
|
||||
FileSystem.LOG.info("hdfspath=" + hdfspath);
|
||||
|
||||
final Path hftppath = hftpFs.makeQualified(f);
|
||||
final FileStatus hftpstatus = hftpFs.getFileStatus(hftppath);
|
||||
FileSystem.LOG.info("hftppath=" + hftppath);
|
||||
|
||||
Assert.assertEquals(hdfspath.toUri().getPath(),
|
||||
hdfsstatus.getPath().toUri().getPath());
|
||||
checkFileStatus(hdfsstatus, hftpstatus);
|
||||
}
|
||||
|
||||
private static void checkFileStatus(final FileStatus expected,
|
||||
final FileStatus computed) {
|
||||
Assert.assertEquals(expected.getPath().toUri().getPath(),
|
||||
computed.getPath().toUri().getPath());
|
||||
|
||||
// TODO: test will fail if the following is un-commented.
|
||||
// Assert.assertEquals(expected.getAccessTime(), computed.getAccessTime());
|
||||
// Assert.assertEquals(expected.getModificationTime(),
|
||||
// computed.getModificationTime());
|
||||
|
||||
Assert.assertEquals(expected.getBlockSize(), computed.getBlockSize());
|
||||
Assert.assertEquals(expected.getGroup(), computed.getGroup());
|
||||
Assert.assertEquals(expected.getLen(), computed.getLen());
|
||||
Assert.assertEquals(expected.getOwner(), computed.getOwner());
|
||||
Assert.assertEquals(expected.getPermission(), computed.getPermission());
|
||||
Assert.assertEquals(expected.getReplication(), computed.getReplication());
|
||||
}
|
||||
}
|
|
@ -1,312 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.server.namenode;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.hadoop.fs.FSInputStream;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.DFSClient;
|
||||
import org.apache.hadoop.hdfs.DFSInputStream;
|
||||
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
||||
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
||||
import org.apache.hadoop.hdfs.server.common.JspHelper;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mortbay.jetty.InclusiveByteRange;
|
||||
|
||||
/*
|
||||
* Mock input stream class that always outputs the current position of the stream.
|
||||
*/
|
||||
class MockFSInputStream extends FSInputStream {
|
||||
long currentPos = 0;
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return (int)(currentPos++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pos) throws IOException {
|
||||
currentPos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPos() throws IOException {
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean seekToNewSource(long targetPos) throws IOException {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TestStreamFile {
|
||||
private final HdfsConfiguration CONF = new HdfsConfiguration();
|
||||
private final DFSClient clientMock = Mockito.mock(DFSClient.class);
|
||||
private final HttpServletRequest mockHttpServletRequest =
|
||||
Mockito.mock(HttpServletRequest.class);
|
||||
private final HttpServletResponse mockHttpServletResponse =
|
||||
Mockito.mock(HttpServletResponse.class);
|
||||
private final ServletContext mockServletContext =
|
||||
Mockito.mock(ServletContext.class);
|
||||
|
||||
final StreamFile sfile = new StreamFile() {
|
||||
private static final long serialVersionUID = -5513776238875189473L;
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return mockServletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DFSClient getDFSClient(HttpServletRequest request)
|
||||
throws IOException, InterruptedException {
|
||||
return clientMock;
|
||||
}
|
||||
};
|
||||
|
||||
// return an array matching the output of mockfsinputstream
|
||||
private static byte[] getOutputArray(int start, int count) {
|
||||
byte[] a = new byte[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
a[i] = (byte)(start+i);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteTo() throws IOException {
|
||||
|
||||
FSInputStream fsin = new MockFSInputStream();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
// new int[]{s_1, c_1, s_2, c_2, ..., s_n, c_n} means to test
|
||||
// reading c_i bytes starting at s_i
|
||||
int[] pairs = new int[]{ 0, 10000,
|
||||
50, 100,
|
||||
50, 6000,
|
||||
1000, 2000,
|
||||
0, 1,
|
||||
0, 0,
|
||||
5000, 0,
|
||||
};
|
||||
|
||||
assertTrue("Pairs array must be even", pairs.length % 2 == 0);
|
||||
|
||||
for (int i = 0; i < pairs.length; i+=2) {
|
||||
StreamFile.copyFromOffset(fsin, os, pairs[i], pairs[i+1]);
|
||||
assertArrayEquals("Reading " + pairs[i+1]
|
||||
+ " bytes from offset " + pairs[i],
|
||||
getOutputArray(pairs[i], pairs[i+1]),
|
||||
os.toByteArray());
|
||||
os.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<InclusiveByteRange> strToRanges(String s, int contentLength) {
|
||||
List<String> l = Arrays.asList(new String[]{"bytes="+s});
|
||||
Enumeration<?> e = (new Vector<String>(l)).elements();
|
||||
return InclusiveByteRange.satisfiableRanges(e, contentLength);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendPartialData() throws IOException {
|
||||
FSInputStream in = new MockFSInputStream();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
// test if multiple ranges, then 416
|
||||
{
|
||||
List<InclusiveByteRange> ranges = strToRanges("0-,10-300", 500);
|
||||
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
|
||||
StreamFile.sendPartialData(in, os, response, 500, ranges);
|
||||
|
||||
// Multiple ranges should result in a 416 error
|
||||
Mockito.verify(response).setStatus(416);
|
||||
}
|
||||
|
||||
// test if no ranges, then 416
|
||||
{
|
||||
os.reset();
|
||||
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
|
||||
StreamFile.sendPartialData(in, os, response, 500, null);
|
||||
|
||||
// No ranges should result in a 416 error
|
||||
Mockito.verify(response).setStatus(416);
|
||||
}
|
||||
|
||||
// test if invalid single range (out of bounds), then 416
|
||||
{
|
||||
List<InclusiveByteRange> ranges = strToRanges("600-800", 500);
|
||||
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
|
||||
StreamFile.sendPartialData(in, os, response, 500, ranges);
|
||||
|
||||
// Single (but invalid) range should result in a 416
|
||||
Mockito.verify(response).setStatus(416);
|
||||
}
|
||||
|
||||
|
||||
// test if one (valid) range, then 206
|
||||
{
|
||||
List<InclusiveByteRange> ranges = strToRanges("100-300", 500);
|
||||
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
|
||||
StreamFile.sendPartialData(in, os, response, 500, ranges);
|
||||
|
||||
// Single (valid) range should result in a 206
|
||||
Mockito.verify(response).setStatus(206);
|
||||
|
||||
assertArrayEquals("Byte range from 100-300",
|
||||
getOutputArray(100, 201),
|
||||
os.toByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Test for positive scenario
|
||||
@Test
|
||||
public void testDoGetShouldWriteTheFileContentIntoServletOutputStream()
|
||||
throws Exception {
|
||||
|
||||
MiniDFSCluster cluster = new MiniDFSCluster.Builder(CONF).numDataNodes(1)
|
||||
.build();
|
||||
try {
|
||||
Path testFile = createFile();
|
||||
setUpForDoGetTest(cluster, testFile);
|
||||
ServletOutputStreamExtn outStream = new ServletOutputStreamExtn();
|
||||
Mockito.doReturn(outStream).when(mockHttpServletResponse)
|
||||
.getOutputStream();
|
||||
StreamFile sfile = new StreamFile() {
|
||||
|
||||
private static final long serialVersionUID = 7715590481809562722L;
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return mockServletContext;
|
||||
}
|
||||
};
|
||||
sfile.doGet(mockHttpServletRequest, mockHttpServletResponse);
|
||||
assertEquals("Not writing the file data into ServletOutputStream",
|
||||
outStream.getResult(), "test");
|
||||
} finally {
|
||||
cluster.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Test for cleaning the streams in exception cases also
|
||||
@Test
|
||||
public void testDoGetShouldCloseTheDFSInputStreamIfResponseGetOutPutStreamThrowsAnyException()
|
||||
throws Exception {
|
||||
|
||||
MiniDFSCluster cluster = new MiniDFSCluster.Builder(CONF).numDataNodes(1)
|
||||
.build();
|
||||
try {
|
||||
Path testFile = createFile();
|
||||
|
||||
setUpForDoGetTest(cluster, testFile);
|
||||
|
||||
Mockito.doThrow(new IOException()).when(mockHttpServletResponse)
|
||||
.getOutputStream();
|
||||
DFSInputStream fsMock = Mockito.mock(DFSInputStream.class);
|
||||
|
||||
Mockito.doReturn(fsMock).when(clientMock).open(testFile.toString());
|
||||
|
||||
Mockito.doReturn(Long.valueOf(4)).when(fsMock).getFileLength();
|
||||
|
||||
try {
|
||||
sfile.doGet(mockHttpServletRequest, mockHttpServletResponse);
|
||||
fail("Not throwing the IOException");
|
||||
} catch (IOException e) {
|
||||
Mockito.verify(clientMock, Mockito.atLeastOnce()).close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
cluster.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void setUpForDoGetTest(MiniDFSCluster cluster, Path testFile) {
|
||||
|
||||
Mockito.doReturn(CONF).when(mockServletContext).getAttribute(
|
||||
JspHelper.CURRENT_CONF);
|
||||
Mockito.doReturn(NetUtils.getHostPortString(NameNode.getAddress(CONF)))
|
||||
.when(mockHttpServletRequest).getParameter("nnaddr");
|
||||
Mockito.doReturn(testFile.toString()).when(mockHttpServletRequest)
|
||||
.getPathInfo();
|
||||
Mockito.doReturn("/streamFile"+testFile.toString()).when(mockHttpServletRequest)
|
||||
.getRequestURI();
|
||||
}
|
||||
|
||||
static Path writeFile(FileSystem fs, Path f) throws IOException {
|
||||
DataOutputStream out = fs.create(f);
|
||||
try {
|
||||
out.writeBytes("test");
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
assertTrue(fs.exists(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
private Path createFile() throws IOException {
|
||||
FileSystem fs = FileSystem.get(CONF);
|
||||
Path testFile = new Path("/test/mkdirs/doGet");
|
||||
writeFile(fs, testFile);
|
||||
return testFile;
|
||||
}
|
||||
|
||||
public static class ServletOutputStreamExtn extends ServletOutputStream {
|
||||
private final StringBuffer buffer = new StringBuffer(3);
|
||||
|
||||
public String getResult() {
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer.append((char) b);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hdfs.tools;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
import org.apache.hadoop.tools.FakeRenewer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
public class TestDelegationTokenFetcher {
|
||||
|
||||
private Configuration conf = new Configuration();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder f = new TemporaryFolder();
|
||||
private static final String tokenFile = "token";
|
||||
|
||||
/**
|
||||
* try to fetch token without http server with IOException
|
||||
*/
|
||||
@Test(expected = IOException.class)
|
||||
public void testTokenFetchFail() throws Exception {
|
||||
WebHdfsFileSystem fs = mock(WebHdfsFileSystem.class);
|
||||
doThrow(new IOException()).when(fs).getDelegationToken(anyString());
|
||||
Path p = new Path(f.getRoot().getAbsolutePath(), tokenFile);
|
||||
DelegationTokenFetcher.saveDelegationToken(conf, fs, null, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call fetch token using http server
|
||||
*/
|
||||
@Test
|
||||
public void expectedTokenIsRetrievedFromHttp() throws Exception {
|
||||
final Token<DelegationTokenIdentifier> testToken = new Token<DelegationTokenIdentifier>(
|
||||
"id".getBytes(), "pwd".getBytes(), FakeRenewer.KIND, new Text(
|
||||
"127.0.0.1:1234"));
|
||||
|
||||
WebHdfsFileSystem fs = mock(WebHdfsFileSystem.class);
|
||||
|
||||
doReturn(testToken).when(fs).getDelegationToken(anyString());
|
||||
Path p = new Path(f.getRoot().getAbsolutePath(), tokenFile);
|
||||
DelegationTokenFetcher.saveDelegationToken(conf, fs, null, p);
|
||||
|
||||
Credentials creds = Credentials.readTokenStorageFile(p, conf);
|
||||
Iterator<Token<?>> itr = creds.getAllTokens().iterator();
|
||||
assertTrue("token not exist error", itr.hasNext());
|
||||
|
||||
Token<?> fetchedToken = itr.next();
|
||||
Assert.assertArrayEquals("token wrong identifier error",
|
||||
testToken.getIdentifier(), fetchedToken.getIdentifier());
|
||||
Assert.assertArrayEquals("token wrong password error",
|
||||
testToken.getPassword(), fetchedToken.getPassword());
|
||||
|
||||
DelegationTokenFetcher.renewTokens(conf, p);
|
||||
Assert.assertEquals(testToken, FakeRenewer.getLastRenewed());
|
||||
|
||||
DelegationTokenFetcher.cancelTokens(conf, p);
|
||||
Assert.assertEquals(testToken, FakeRenewer.getLastCanceled());
|
||||
}
|
||||
}
|
|
@ -1,249 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.hdfs.web;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.hadoop.hdfs.server.namenode.StreamFile;
|
||||
import org.apache.hadoop.hdfs.web.HftpFileSystem;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestByteRangeInputStream {
|
||||
public static class MockHttpURLConnection extends HttpURLConnection {
|
||||
public MockHttpURLConnection(URL u) {
|
||||
super(u);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usingProxy(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return new ByteArrayInputStream("asdf".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
URL u = null;
|
||||
try {
|
||||
u = new URL("http://resolvedurl/");
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getResponseCode() {
|
||||
if (responseCode != -1) {
|
||||
return responseCode;
|
||||
} else {
|
||||
if (getRequestProperty("Range") == null) {
|
||||
return 200;
|
||||
} else {
|
||||
return 206;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setResponseCode(int resCode) {
|
||||
responseCode = resCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeaderField(String field) {
|
||||
return (field.equalsIgnoreCase(StreamFile.CONTENT_LENGTH)) ? "65535" : null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testByteRange() throws IOException {
|
||||
URLConnectionFactory factory = mock(URLConnectionFactory.class);
|
||||
HftpFileSystem.RangeHeaderUrlOpener ospy = spy(
|
||||
new HftpFileSystem.RangeHeaderUrlOpener(factory, new URL("http://test/")));
|
||||
doReturn(new MockHttpURLConnection(ospy.getURL())).when(ospy)
|
||||
.openConnection();
|
||||
HftpFileSystem.RangeHeaderUrlOpener rspy = spy(
|
||||
new HftpFileSystem.RangeHeaderUrlOpener(factory, null));
|
||||
doReturn(new MockHttpURLConnection(rspy.getURL())).when(rspy)
|
||||
.openConnection();
|
||||
ByteRangeInputStream is = new HftpFileSystem.RangeHeaderInputStream(ospy, rspy);
|
||||
|
||||
assertEquals("getPos wrong", 0, is.getPos());
|
||||
|
||||
is.read();
|
||||
|
||||
assertNull("Initial call made incorrectly (Range Check)", ospy
|
||||
.openConnection().getRequestProperty("Range"));
|
||||
|
||||
assertEquals("getPos should be 1 after reading one byte", 1, is.getPos());
|
||||
|
||||
is.read();
|
||||
|
||||
assertEquals("getPos should be 2 after reading two bytes", 2, is.getPos());
|
||||
|
||||
// No additional connections should have been made (no seek)
|
||||
|
||||
rspy.setURL(new URL("http://resolvedurl/"));
|
||||
|
||||
is.seek(100);
|
||||
is.read();
|
||||
|
||||
assertEquals("Seek to 100 bytes made incorrectly (Range Check)",
|
||||
"bytes=100-", rspy.openConnection().getRequestProperty("Range"));
|
||||
|
||||
assertEquals("getPos should be 101 after reading one byte", 101,
|
||||
is.getPos());
|
||||
|
||||
verify(rspy, times(2)).openConnection();
|
||||
|
||||
is.seek(101);
|
||||
is.read();
|
||||
|
||||
verify(rspy, times(2)).openConnection();
|
||||
|
||||
// Seek to 101 should not result in another request"
|
||||
|
||||
is.seek(2500);
|
||||
is.read();
|
||||
|
||||
assertEquals("Seek to 2500 bytes made incorrectly (Range Check)",
|
||||
"bytes=2500-", rspy.openConnection().getRequestProperty("Range"));
|
||||
|
||||
((MockHttpURLConnection) rspy.openConnection()).setResponseCode(200);
|
||||
is.seek(500);
|
||||
|
||||
try {
|
||||
is.read();
|
||||
fail("Exception should be thrown when 200 response is given "
|
||||
+ "but 206 is expected");
|
||||
} catch (IOException e) {
|
||||
assertEquals("Should fail because incorrect response code was sent",
|
||||
"HTTP_PARTIAL expected, received 200", e.getMessage());
|
||||
}
|
||||
|
||||
((MockHttpURLConnection) rspy.openConnection()).setResponseCode(206);
|
||||
is.seek(0);
|
||||
|
||||
try {
|
||||
is.read();
|
||||
fail("Exception should be thrown when 206 response is given "
|
||||
+ "but 200 is expected");
|
||||
} catch (IOException e) {
|
||||
assertEquals("Should fail because incorrect response code was sent",
|
||||
"HTTP_OK expected, received 206", e.getMessage());
|
||||
}
|
||||
is.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropagatedClose() throws IOException {
|
||||
URLConnectionFactory factory = mock(URLConnectionFactory.class);
|
||||
|
||||
ByteRangeInputStream brs = spy(new HftpFileSystem.RangeHeaderInputStream(
|
||||
factory, new URL("http://test/")));
|
||||
|
||||
InputStream mockStream = mock(InputStream.class);
|
||||
doReturn(mockStream).when(brs).openInputStream();
|
||||
|
||||
int brisOpens = 0;
|
||||
int brisCloses = 0;
|
||||
int isCloses = 0;
|
||||
|
||||
// first open, shouldn't close underlying stream
|
||||
brs.getInputStream();
|
||||
verify(brs, times(++brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
|
||||
// stream is open, shouldn't close underlying stream
|
||||
brs.getInputStream();
|
||||
verify(brs, times(brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
|
||||
// seek forces a reopen, should close underlying stream
|
||||
brs.seek(1);
|
||||
brs.getInputStream();
|
||||
verify(brs, times(++brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(++isCloses)).close();
|
||||
|
||||
// verify that the underlying stream isn't closed after a seek
|
||||
// ie. the state was correctly updated
|
||||
brs.getInputStream();
|
||||
verify(brs, times(brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
|
||||
// seeking to same location should be a no-op
|
||||
brs.seek(1);
|
||||
brs.getInputStream();
|
||||
verify(brs, times(brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
|
||||
// close should of course close
|
||||
brs.close();
|
||||
verify(brs, times(++brisCloses)).close();
|
||||
verify(mockStream, times(++isCloses)).close();
|
||||
|
||||
// it's already closed, underlying stream should not close
|
||||
brs.close();
|
||||
verify(brs, times(++brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
|
||||
// it's closed, don't reopen it
|
||||
boolean errored = false;
|
||||
try {
|
||||
brs.getInputStream();
|
||||
} catch (IOException e) {
|
||||
errored = true;
|
||||
assertEquals("Stream closed", e.getMessage());
|
||||
} finally {
|
||||
assertTrue("Read a closed steam", errored);
|
||||
}
|
||||
verify(brs, times(brisOpens)).openInputStream();
|
||||
verify(brs, times(brisCloses)).close();
|
||||
verify(mockStream, times(isCloses)).close();
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hdfs.web;
|
||||
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.SecurityUtil;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
import org.apache.hadoop.security.token.TokenIdentifier;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.util.reflection.Whitebox;
|
||||
|
||||
public class TestHftpDelegationToken {
|
||||
|
||||
/**
|
||||
* Test whether HftpFileSystem maintain wire-compatibility for 0.20.203 when
|
||||
* obtaining delegation token. See HDFS-5440 for more details.
|
||||
*/
|
||||
@Test
|
||||
public void testTokenCompatibilityFor203() throws IOException,
|
||||
URISyntaxException, AuthenticationException {
|
||||
Configuration conf = new Configuration();
|
||||
HftpFileSystem fs = new HftpFileSystem();
|
||||
|
||||
Token<?> token = new Token<TokenIdentifier>(new byte[0], new byte[0],
|
||||
DelegationTokenIdentifier.HDFS_DELEGATION_KIND, new Text(
|
||||
"127.0.0.1:8020"));
|
||||
Credentials cred = new Credentials();
|
||||
cred.addToken(HftpFileSystem.TOKEN_KIND, token);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
cred.write(new DataOutputStream(os));
|
||||
|
||||
HttpURLConnection conn = mock(HttpURLConnection.class);
|
||||
doReturn(new ByteArrayInputStream(os.toByteArray())).when(conn)
|
||||
.getInputStream();
|
||||
doReturn(HttpURLConnection.HTTP_OK).when(conn).getResponseCode();
|
||||
|
||||
URLConnectionFactory factory = mock(URLConnectionFactory.class);
|
||||
doReturn(conn).when(factory).openConnection(Mockito.<URL> any(),
|
||||
anyBoolean());
|
||||
|
||||
final URI uri = new URI("hftp://127.0.0.1:8020");
|
||||
fs.initialize(uri, conf);
|
||||
fs.connectionFactory = factory;
|
||||
|
||||
UserGroupInformation ugi = UserGroupInformation.createUserForTesting("foo",
|
||||
new String[] { "bar" });
|
||||
|
||||
TokenAspect<HftpFileSystem> tokenAspect = new TokenAspect<HftpFileSystem>(
|
||||
fs, SecurityUtil.buildTokenService(uri), HftpFileSystem.TOKEN_KIND);
|
||||
|
||||
tokenAspect.initDelegationToken(ugi);
|
||||
tokenAspect.ensureTokenInitialized();
|
||||
|
||||
Assert.assertSame(HftpFileSystem.TOKEN_KIND, fs.getRenewToken().getKind());
|
||||
|
||||
Token<?> tok = (Token<?>) Whitebox.getInternalState(fs, "delegationToken");
|
||||
Assert.assertNotSame("Not making a copy of the remote token", token, tok);
|
||||
Assert.assertEquals(token.getKind(), tok.getKind());
|
||||
}
|
||||
}
|
|
@ -1,395 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.hdfs.web;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.BlockLocation;
|
||||
import org.apache.hadoop.fs.FSDataInputStream;
|
||||
import org.apache.hadoop.fs.FSDataOutputStream;
|
||||
import org.apache.hadoop.fs.FileStatus;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.FileUtil;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||
import org.apache.hadoop.hdfs.MiniDFSCluster;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
||||
import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils;
|
||||
import org.apache.hadoop.hdfs.server.protocol.DatanodeRegistration;
|
||||
import org.apache.hadoop.security.ssl.KeyStoreTestUtil;
|
||||
import org.apache.hadoop.util.ServletUtil;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestHftpFileSystem {
|
||||
private static final String BASEDIR = System.getProperty("test.build.dir",
|
||||
"target/test-dir") + "/" + TestHftpFileSystem.class.getSimpleName();
|
||||
private static String keystoresDir;
|
||||
private static String sslConfDir;
|
||||
private static Configuration config = null;
|
||||
private static MiniDFSCluster cluster = null;
|
||||
private static String blockPoolId = null;
|
||||
private static String hftpUri = null;
|
||||
private FileSystem hdfs = null;
|
||||
private HftpFileSystem hftpFs = null;
|
||||
|
||||
private static final Path[] TEST_PATHS = new Path[] {
|
||||
// URI does not encode, Request#getPathInfo returns /foo
|
||||
new Path("/foo;bar"),
|
||||
|
||||
// URI does not encode, Request#getPathInfo returns verbatim
|
||||
new Path("/foo+"), new Path("/foo+bar/foo+bar"),
|
||||
new Path("/foo=bar/foo=bar"), new Path("/foo,bar/foo,bar"),
|
||||
new Path("/foo@bar/foo@bar"), new Path("/foo&bar/foo&bar"),
|
||||
new Path("/foo$bar/foo$bar"), new Path("/foo_bar/foo_bar"),
|
||||
new Path("/foo~bar/foo~bar"), new Path("/foo.bar/foo.bar"),
|
||||
new Path("/foo../bar/foo../bar"), new Path("/foo.../bar/foo.../bar"),
|
||||
new Path("/foo'bar/foo'bar"),
|
||||
new Path("/foo#bar/foo#bar"),
|
||||
new Path("/foo!bar/foo!bar"),
|
||||
// HDFS file names may not contain ":"
|
||||
|
||||
// URI percent encodes, Request#getPathInfo decodes
|
||||
new Path("/foo bar/foo bar"), new Path("/foo?bar/foo?bar"),
|
||||
new Path("/foo\">bar/foo\">bar"), };
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
config = new Configuration();
|
||||
cluster = new MiniDFSCluster.Builder(config).numDataNodes(2).build();
|
||||
blockPoolId = cluster.getNamesystem().getBlockPoolId();
|
||||
hftpUri = "hftp://"
|
||||
+ config.get(DFSConfigKeys.DFS_NAMENODE_HTTP_ADDRESS_KEY);
|
||||
File base = new File(BASEDIR);
|
||||
FileUtil.fullyDelete(base);
|
||||
base.mkdirs();
|
||||
keystoresDir = new File(BASEDIR).getAbsolutePath();
|
||||
sslConfDir = KeyStoreTestUtil.getClasspathDir(TestHftpFileSystem.class);
|
||||
|
||||
KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, config, false);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() throws Exception {
|
||||
if (cluster != null) {
|
||||
cluster.shutdown();
|
||||
}
|
||||
FileUtil.fullyDelete(new File(BASEDIR));
|
||||
KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initFileSystems() throws IOException {
|
||||
hdfs = cluster.getFileSystem();
|
||||
hftpFs = (HftpFileSystem) new Path(hftpUri).getFileSystem(config);
|
||||
// clear out the namespace
|
||||
for (FileStatus stat : hdfs.listStatus(new Path("/"))) {
|
||||
hdfs.delete(stat.getPath(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void resetFileSystems() throws IOException {
|
||||
FileSystem.closeAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test file creation and access with file names that need encoding.
|
||||
*/
|
||||
@Test
|
||||
public void testFileNameEncoding() throws IOException, URISyntaxException {
|
||||
for (Path p : TEST_PATHS) {
|
||||
// Create and access the path (data and streamFile servlets)
|
||||
FSDataOutputStream out = hdfs.create(p, true);
|
||||
out.writeBytes("0123456789");
|
||||
out.close();
|
||||
FSDataInputStream in = hftpFs.open(p);
|
||||
assertEquals('0', in.read());
|
||||
in.close();
|
||||
|
||||
// Check the file status matches the path. Hftp returns a FileStatus
|
||||
// with the entire URI, extract the path part.
|
||||
assertEquals(p, new Path(hftpFs.getFileStatus(p).getPath().toUri()
|
||||
.getPath()));
|
||||
|
||||
// Test list status (listPath servlet)
|
||||
assertEquals(1, hftpFs.listStatus(p).length);
|
||||
|
||||
// Test content summary (contentSummary servlet)
|
||||
assertNotNull("No content summary", hftpFs.getContentSummary(p));
|
||||
|
||||
// Test checksums (fileChecksum and getFileChecksum servlets)
|
||||
assertNotNull("No file checksum", hftpFs.getFileChecksum(p));
|
||||
}
|
||||
}
|
||||
|
||||
private void testDataNodeRedirect(Path path) throws IOException {
|
||||
// Create the file
|
||||
if (hdfs.exists(path)) {
|
||||
hdfs.delete(path, true);
|
||||
}
|
||||
FSDataOutputStream out = hdfs.create(path, (short) 1);
|
||||
out.writeBytes("0123456789");
|
||||
out.close();
|
||||
|
||||
// Get the path's block location so we can determine
|
||||
// if we were redirected to the right DN.
|
||||
BlockLocation[] locations = hdfs.getFileBlockLocations(path, 0, 10);
|
||||
String xferAddr = locations[0].getNames()[0];
|
||||
|
||||
// Connect to the NN to get redirected
|
||||
URL u = hftpFs.getNamenodeURL(
|
||||
"/data" + ServletUtil.encodePath(path.toUri().getPath()),
|
||||
"ugi=userx,groupy");
|
||||
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
conn.connect();
|
||||
conn.getInputStream();
|
||||
|
||||
boolean checked = false;
|
||||
// Find the datanode that has the block according to locations
|
||||
// and check that the URL was redirected to this DN's info port
|
||||
for (DataNode node : cluster.getDataNodes()) {
|
||||
DatanodeRegistration dnR = DataNodeTestUtils.getDNRegistrationForBP(node,
|
||||
blockPoolId);
|
||||
if (dnR.getXferAddr().equals(xferAddr)) {
|
||||
checked = true;
|
||||
assertEquals(dnR.getInfoPort(), conn.getURL().getPort());
|
||||
}
|
||||
}
|
||||
assertTrue("The test never checked that location of "
|
||||
+ "the block and hftp desitnation are the same", checked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that clients are redirected to the appropriate DN.
|
||||
*/
|
||||
@Test
|
||||
public void testDataNodeRedirect() throws IOException {
|
||||
for (Path p : TEST_PATHS) {
|
||||
testDataNodeRedirect(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getPos() functionality.
|
||||
*/
|
||||
@Test
|
||||
public void testGetPos() throws IOException {
|
||||
final Path testFile = new Path("/testfile+1");
|
||||
// Write a test file.
|
||||
FSDataOutputStream out = hdfs.create(testFile, true);
|
||||
out.writeBytes("0123456789");
|
||||
out.close();
|
||||
|
||||
FSDataInputStream in = hftpFs.open(testFile);
|
||||
|
||||
// Test read().
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
assertEquals(i, in.getPos());
|
||||
in.read();
|
||||
}
|
||||
|
||||
// Test read(b, off, len).
|
||||
assertEquals(5, in.getPos());
|
||||
byte[] buffer = new byte[10];
|
||||
assertEquals(2, in.read(buffer, 0, 2));
|
||||
assertEquals(7, in.getPos());
|
||||
|
||||
// Test read(b).
|
||||
int bytesRead = in.read(buffer);
|
||||
assertEquals(7 + bytesRead, in.getPos());
|
||||
|
||||
// Test EOF.
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
in.read();
|
||||
}
|
||||
assertEquals(10, in.getPos());
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests seek().
|
||||
*/
|
||||
@Test
|
||||
public void testSeek() throws IOException {
|
||||
final Path testFile = new Path("/testfile+1");
|
||||
FSDataOutputStream out = hdfs.create(testFile, true);
|
||||
out.writeBytes("0123456789");
|
||||
out.close();
|
||||
FSDataInputStream in = hftpFs.open(testFile);
|
||||
in.seek(7);
|
||||
assertEquals('7', in.read());
|
||||
in.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadClosedStream() throws IOException {
|
||||
final Path testFile = new Path("/testfile+2");
|
||||
FSDataOutputStream os = hdfs.create(testFile, true);
|
||||
os.writeBytes("0123456789");
|
||||
os.close();
|
||||
|
||||
// ByteRangeInputStream delays opens until reads. Make sure it doesn't
|
||||
// open a closed stream that has never been opened
|
||||
FSDataInputStream in = hftpFs.open(testFile);
|
||||
in.close();
|
||||
checkClosedStream(in);
|
||||
checkClosedStream(in.getWrappedStream());
|
||||
|
||||
// force the stream to connect and then close it
|
||||
in = hftpFs.open(testFile);
|
||||
int ch = in.read();
|
||||
assertEquals('0', ch);
|
||||
in.close();
|
||||
checkClosedStream(in);
|
||||
checkClosedStream(in.getWrappedStream());
|
||||
|
||||
// make sure seeking doesn't automagically reopen the stream
|
||||
in.seek(4);
|
||||
checkClosedStream(in);
|
||||
checkClosedStream(in.getWrappedStream());
|
||||
}
|
||||
|
||||
private void checkClosedStream(InputStream is) {
|
||||
IOException ioe = null;
|
||||
try {
|
||||
is.read();
|
||||
} catch (IOException e) {
|
||||
ioe = e;
|
||||
}
|
||||
assertNotNull("No exception on closed read", ioe);
|
||||
assertEquals("Stream closed", ioe.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHftpDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hftp://localhost");
|
||||
HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
|
||||
// HFTP uses http to get the token so canonical service name should
|
||||
// return the http port.
|
||||
assertEquals("127.0.0.1:" + DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT,
|
||||
fs.getCanonicalServiceName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHftpCustomUriPortWithDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hftp://localhost:123");
|
||||
HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
assertEquals("127.0.0.1:123", fs.getCanonicalServiceName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHftpCustomUriPortWithCustomDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hftp://localhost:789");
|
||||
HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
assertEquals("127.0.0.1:789", fs.getCanonicalServiceName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTimeout() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hftp://localhost");
|
||||
HftpFileSystem fs = (HftpFileSystem) FileSystem.get(uri, conf);
|
||||
URLConnection conn = fs.connectionFactory.openConnection(new URL(
|
||||
"http://localhost"));
|
||||
assertEquals(URLConnectionFactory.DEFAULT_SOCKET_TIMEOUT,
|
||||
conn.getConnectTimeout());
|
||||
assertEquals(URLConnectionFactory.DEFAULT_SOCKET_TIMEOUT,
|
||||
conn.getReadTimeout());
|
||||
}
|
||||
|
||||
// /
|
||||
|
||||
@Test
|
||||
public void testHsftpDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hsftp://localhost");
|
||||
HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
assertEquals("127.0.0.1:" + DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT,
|
||||
fs.getCanonicalServiceName());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHsftpCustomUriPortWithDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
URI uri = URI.create("hsftp://localhost:123");
|
||||
HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
assertEquals("127.0.0.1:123", fs.getCanonicalServiceName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHsftpCustomUriPortWithCustomDefaultPorts() throws IOException {
|
||||
Configuration conf = new Configuration();
|
||||
|
||||
URI uri = URI.create("hsftp://localhost:789");
|
||||
HsftpFileSystem fs = (HsftpFileSystem) FileSystem.get(uri, conf);
|
||||
|
||||
assertEquals(DFSConfigKeys.DFS_NAMENODE_HTTPS_PORT_DEFAULT,
|
||||
fs.getDefaultPort());
|
||||
|
||||
assertEquals(uri, fs.getUri());
|
||||
assertEquals("127.0.0.1:789", fs.getCanonicalServiceName());
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.hadoop.tools;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.FileSystemTestHelper;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.DistributedFileSystem;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.hdfs.tools.DelegationTokenFetcher;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import static org.mockito.Matchers.*;
|
||||
|
||||
public class TestDelegationTokenFetcher {
|
||||
private DistributedFileSystem dfs;
|
||||
private Configuration conf;
|
||||
private URI uri;
|
||||
private static final String SERVICE_VALUE = "localhost:2005";
|
||||
private static final String tokenFile = "file.dta";
|
||||
|
||||
@Before
|
||||
public void init() throws URISyntaxException, IOException {
|
||||
dfs = mock(DistributedFileSystem.class);
|
||||
conf = new Configuration();
|
||||
uri = new URI("hdfs://" + SERVICE_VALUE);
|
||||
FileSystemTestHelper.addFileSystemForTesting(uri, conf, dfs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that when the DelegationTokenFetcher runs, it talks to the Namenode,
|
||||
* pulls out the correct user's token and successfully serializes it to disk.
|
||||
*/
|
||||
@Test
|
||||
public void expectedTokenIsRetrievedFromDFS() throws Exception {
|
||||
final byte[] ident = new DelegationTokenIdentifier(new Text("owner"),
|
||||
new Text("renewer"), new Text("realuser")).getBytes();
|
||||
final byte[] pw = new byte[] { 42 };
|
||||
final Text service = new Text(uri.toString());
|
||||
|
||||
// Create a token for the fetcher to fetch, wire NN to return it when asked
|
||||
// for this particular user.
|
||||
final Token<DelegationTokenIdentifier> t =
|
||||
new Token<DelegationTokenIdentifier>(ident, pw, FakeRenewer.KIND, service);
|
||||
when(dfs.addDelegationTokens(eq((String) null), any(Credentials.class))).thenAnswer(
|
||||
new Answer<Token<?>[]>() {
|
||||
@Override
|
||||
public Token<?>[] answer(InvocationOnMock invocation) {
|
||||
Credentials creds = (Credentials)invocation.getArguments()[1];
|
||||
creds.addToken(service, t);
|
||||
return new Token<?>[]{t};
|
||||
}
|
||||
});
|
||||
when(dfs.getUri()).thenReturn(uri);
|
||||
FakeRenewer.reset();
|
||||
|
||||
FileSystem fileSys = FileSystem.getLocal(conf);
|
||||
try {
|
||||
DelegationTokenFetcher.main(new String[] { "-fs", uri.toString(),
|
||||
tokenFile });
|
||||
Path p = new Path(fileSys.getWorkingDirectory(), tokenFile);
|
||||
Credentials creds = Credentials.readTokenStorageFile(p, conf);
|
||||
Iterator<Token<?>> itr = creds.getAllTokens().iterator();
|
||||
// make sure we got back exactly the 1 token we expected
|
||||
assertTrue(itr.hasNext());
|
||||
assertEquals(t, itr.next());
|
||||
assertTrue(!itr.hasNext());
|
||||
|
||||
DelegationTokenFetcher.main(new String[] { "--print", tokenFile });
|
||||
DelegationTokenFetcher.main(new String[] { "--renew", tokenFile });
|
||||
assertEquals(t, FakeRenewer.lastRenewed);
|
||||
FakeRenewer.reset();
|
||||
|
||||
DelegationTokenFetcher.main(new String[] { "--cancel", tokenFile });
|
||||
assertEquals(t, FakeRenewer.lastCanceled);
|
||||
} finally {
|
||||
fileSys.delete(new Path(tokenFile), true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,374 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.hadoop.tools;
|
||||
|
||||
import static org.jboss.netty.handler.codec.http.HttpMethod.GET;
|
||||
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.OK;
|
||||
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.hdfs.tools.DelegationTokenFetcher;
|
||||
import org.apache.hadoop.hdfs.web.HftpFileSystem;
|
||||
import org.apache.hadoop.hdfs.web.URLConnectionFactory;
|
||||
import org.apache.hadoop.io.DataOutputBuffer;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.apache.hadoop.security.Credentials;
|
||||
import org.apache.hadoop.security.authentication.client.AuthenticationException;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jboss.netty.bootstrap.ServerBootstrap;
|
||||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
import org.jboss.netty.buffer.ChannelBuffers;
|
||||
import org.jboss.netty.channel.Channel;
|
||||
import org.jboss.netty.channel.ChannelFutureListener;
|
||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||
import org.jboss.netty.channel.ChannelPipeline;
|
||||
import org.jboss.netty.channel.ChannelPipelineFactory;
|
||||
import org.jboss.netty.channel.Channels;
|
||||
import org.jboss.netty.channel.ExceptionEvent;
|
||||
import org.jboss.netty.channel.MessageEvent;
|
||||
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
|
||||
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
||||
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
||||
import org.jboss.netty.handler.codec.http.HttpHeaders;
|
||||
import org.jboss.netty.handler.codec.http.HttpMethod;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.UnmodifiableIterator;
|
||||
|
||||
public class TestDelegationTokenRemoteFetcher {
|
||||
private static final Logger LOG = Logger
|
||||
.getLogger(TestDelegationTokenRemoteFetcher.class);
|
||||
|
||||
private static final String EXP_DATE = "124123512361236";
|
||||
private static final String tokenFile = "http.file.dta";
|
||||
private static final URLConnectionFactory connectionFactory = URLConnectionFactory.DEFAULT_SYSTEM_CONNECTION_FACTORY;
|
||||
|
||||
private int httpPort;
|
||||
private URI serviceUrl;
|
||||
private FileSystem fileSys;
|
||||
private Configuration conf;
|
||||
private ServerBootstrap bootstrap;
|
||||
private Token<DelegationTokenIdentifier> testToken;
|
||||
private volatile AssertionError assertionError;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
conf = new Configuration();
|
||||
fileSys = FileSystem.getLocal(conf);
|
||||
httpPort = NetUtils.getFreeSocketPort();
|
||||
serviceUrl = new URI("http://localhost:" + httpPort);
|
||||
testToken = createToken(serviceUrl);
|
||||
}
|
||||
|
||||
@After
|
||||
public void clean() throws IOException {
|
||||
if (fileSys != null)
|
||||
fileSys.delete(new Path(tokenFile), true);
|
||||
if (bootstrap != null)
|
||||
bootstrap.releaseExternalResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* try to fetch token without http server with IOException
|
||||
*/
|
||||
@Test
|
||||
public void testTokenFetchFail() throws Exception {
|
||||
try {
|
||||
DelegationTokenFetcher.main(new String[] { "-webservice=" + serviceUrl,
|
||||
tokenFile });
|
||||
fail("Token fetcher shouldn't start in absense of NN");
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* try to fetch token without http server with IOException
|
||||
*/
|
||||
@Test
|
||||
public void testTokenRenewFail() throws AuthenticationException {
|
||||
try {
|
||||
DelegationTokenFetcher.renewDelegationToken(connectionFactory, serviceUrl, testToken);
|
||||
fail("Token fetcher shouldn't be able to renew tokens in absense of NN");
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* try cancel token without http server with IOException
|
||||
*/
|
||||
@Test
|
||||
public void expectedTokenCancelFail() throws AuthenticationException {
|
||||
try {
|
||||
DelegationTokenFetcher.cancelDelegationToken(connectionFactory, serviceUrl, testToken);
|
||||
fail("Token fetcher shouldn't be able to cancel tokens in absense of NN");
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* try fetch token and get http response with error
|
||||
*/
|
||||
@Test
|
||||
public void expectedTokenRenewErrorHttpResponse()
|
||||
throws AuthenticationException, URISyntaxException {
|
||||
bootstrap = startHttpServer(httpPort, testToken, serviceUrl);
|
||||
try {
|
||||
DelegationTokenFetcher.renewDelegationToken(connectionFactory, new URI(
|
||||
serviceUrl.toString() + "/exception"), createToken(serviceUrl));
|
||||
fail("Token fetcher shouldn't be able to renew tokens using an invalid"
|
||||
+ " NN URL");
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
if (assertionError != null)
|
||||
throw assertionError;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testCancelTokenFromHttp() throws IOException,
|
||||
AuthenticationException {
|
||||
bootstrap = startHttpServer(httpPort, testToken, serviceUrl);
|
||||
DelegationTokenFetcher.cancelDelegationToken(connectionFactory, serviceUrl,
|
||||
testToken);
|
||||
if (assertionError != null)
|
||||
throw assertionError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call renew token using http server return new expiration time
|
||||
*/
|
||||
@Test
|
||||
public void testRenewTokenFromHttp() throws IOException,
|
||||
NumberFormatException, AuthenticationException {
|
||||
bootstrap = startHttpServer(httpPort, testToken, serviceUrl);
|
||||
assertTrue("testRenewTokenFromHttp error",
|
||||
Long.parseLong(EXP_DATE) == DelegationTokenFetcher.renewDelegationToken(
|
||||
connectionFactory, serviceUrl, testToken));
|
||||
if (assertionError != null)
|
||||
throw assertionError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call fetch token using http server
|
||||
*/
|
||||
@Test
|
||||
public void expectedTokenIsRetrievedFromHttp() throws Exception {
|
||||
bootstrap = startHttpServer(httpPort, testToken, serviceUrl);
|
||||
DelegationTokenFetcher.main(new String[] { "-webservice=" + serviceUrl,
|
||||
tokenFile });
|
||||
Path p = new Path(fileSys.getWorkingDirectory(), tokenFile);
|
||||
Credentials creds = Credentials.readTokenStorageFile(p, conf);
|
||||
Iterator<Token<?>> itr = creds.getAllTokens().iterator();
|
||||
assertTrue("token not exist error", itr.hasNext());
|
||||
Token<?> fetchedToken = itr.next();
|
||||
Assert.assertArrayEquals("token wrong identifier error",
|
||||
testToken.getIdentifier(), fetchedToken.getIdentifier());
|
||||
Assert.assertArrayEquals("token wrong password error",
|
||||
testToken.getPassword(), fetchedToken.getPassword());
|
||||
if (assertionError != null)
|
||||
throw assertionError;
|
||||
}
|
||||
|
||||
private static Token<DelegationTokenIdentifier> createToken(URI serviceUri) {
|
||||
byte[] pw = "hadoop".getBytes();
|
||||
byte[] ident = new DelegationTokenIdentifier(new Text("owner"), new Text(
|
||||
"renewer"), new Text("realuser")).getBytes();
|
||||
Text service = new Text(serviceUri.toString());
|
||||
return new Token<DelegationTokenIdentifier>(ident, pw,
|
||||
HftpFileSystem.TOKEN_KIND, service);
|
||||
}
|
||||
|
||||
private interface Handler {
|
||||
void handle(Channel channel, Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) throws IOException;
|
||||
}
|
||||
|
||||
private class FetchHandler implements Handler {
|
||||
|
||||
@Override
|
||||
public void handle(Channel channel, Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) throws IOException {
|
||||
Assert.assertEquals(testToken, token);
|
||||
|
||||
Credentials creds = new Credentials();
|
||||
creds.addToken(new Text(serviceUrl), token);
|
||||
DataOutputBuffer out = new DataOutputBuffer();
|
||||
creds.write(out);
|
||||
int fileLength = out.getData().length;
|
||||
ChannelBuffer cbuffer = ChannelBuffers.buffer(fileLength);
|
||||
cbuffer.writeBytes(out.getData());
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
|
||||
String.valueOf(fileLength));
|
||||
response.setContent(cbuffer);
|
||||
channel.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private class RenewHandler implements Handler {
|
||||
|
||||
@Override
|
||||
public void handle(Channel channel, Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) throws IOException {
|
||||
Assert.assertEquals(testToken, token);
|
||||
byte[] bytes = EXP_DATE.getBytes();
|
||||
ChannelBuffer cbuffer = ChannelBuffers.buffer(bytes.length);
|
||||
cbuffer.writeBytes(bytes);
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
|
||||
String.valueOf(bytes.length));
|
||||
response.setContent(cbuffer);
|
||||
channel.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private class ExceptionHandler implements Handler {
|
||||
|
||||
@Override
|
||||
public void handle(Channel channel, Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) throws IOException {
|
||||
Assert.assertEquals(testToken, token);
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1,
|
||||
HttpResponseStatus.METHOD_NOT_ALLOWED);
|
||||
channel.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private class CancelHandler implements Handler {
|
||||
|
||||
@Override
|
||||
public void handle(Channel channel, Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) throws IOException {
|
||||
Assert.assertEquals(testToken, token);
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
|
||||
channel.write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private final class CredentialsLogicHandler extends
|
||||
SimpleChannelUpstreamHandler {
|
||||
|
||||
private final Token<DelegationTokenIdentifier> token;
|
||||
private final String serviceUrl;
|
||||
private final ImmutableMap<String, Handler> routes = ImmutableMap.of(
|
||||
"/exception", new ExceptionHandler(),
|
||||
"/cancelDelegationToken", new CancelHandler(),
|
||||
"/getDelegationToken", new FetchHandler() ,
|
||||
"/renewDelegationToken", new RenewHandler());
|
||||
|
||||
public CredentialsLogicHandler(Token<DelegationTokenIdentifier> token,
|
||||
String serviceUrl) {
|
||||
this.token = token;
|
||||
this.serviceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e)
|
||||
throws Exception {
|
||||
HttpRequest request = (HttpRequest) e.getMessage();
|
||||
|
||||
if (request.getMethod() == HttpMethod.OPTIONS) {
|
||||
// Mimic SPNEGO authentication
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1,
|
||||
HttpResponseStatus.OK);
|
||||
response.addHeader("Set-Cookie", "hadoop-auth=1234");
|
||||
e.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
} else if (request.getMethod() != GET) {
|
||||
e.getChannel().close();
|
||||
}
|
||||
UnmodifiableIterator<Map.Entry<String, Handler>> iter = routes.entrySet()
|
||||
.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<String, Handler> entry = iter.next();
|
||||
if (request.getUri().contains(entry.getKey())) {
|
||||
Handler handler = entry.getValue();
|
||||
try {
|
||||
handler.handle(e.getChannel(), token, serviceUrl);
|
||||
} catch (AssertionError ee) {
|
||||
TestDelegationTokenRemoteFetcher.this.assertionError = ee;
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1,
|
||||
HttpResponseStatus.BAD_REQUEST);
|
||||
response.setContent(ChannelBuffers.copiedBuffer(ee.getMessage(),
|
||||
Charset.defaultCharset()));
|
||||
e.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
|
||||
throws Exception {
|
||||
Channel ch = e.getChannel();
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(cause.getMessage());
|
||||
ch.close().addListener(ChannelFutureListener.CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
private ServerBootstrap startHttpServer(int port,
|
||||
final Token<DelegationTokenIdentifier> token, final URI url) {
|
||||
ServerBootstrap bootstrap = new ServerBootstrap(
|
||||
new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
|
||||
Executors.newCachedThreadPool()));
|
||||
|
||||
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
return Channels.pipeline(new HttpRequestDecoder(),
|
||||
new HttpChunkAggregator(65536), new HttpResponseEncoder(),
|
||||
new CredentialsLogicHandler(token, url.toString()));
|
||||
}
|
||||
});
|
||||
bootstrap.bind(new InetSocketAddress("localhost", port));
|
||||
return bootstrap;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue