Java tutorial
/** * 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 java.io.BufferedReader; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URL; import java.net.URLConnection; import java.security.PrivilegedExceptionAction; import java.util.Collection; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.HftpFileSystem; import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; import org.apache.hadoop.hdfs.server.namenode.CancelDelegationTokenServlet; import org.apache.hadoop.hdfs.server.namenode.GetDelegationTokenServlet; import org.apache.hadoop.hdfs.server.namenode.RenewDelegationTokenServlet; import org.apache.hadoop.io.IOUtils; 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.util.GenericOptionsParser; /** * Fetch a DelegationToken from the current Namenode and store it in the * specified file. */ public class DelegationTokenFetcher { static { Configuration.addDefaultResource("hdfs-default.xml"); Configuration.addDefaultResource("hdfs-site.xml"); } private static final Log LOG = LogFactory.getLog(DelegationTokenFetcher.class); private static final String WEBSERVICE = "webservice"; private static final String CANCEL = "cancel"; private static final String RENEW = "renew"; static { // Enable Kerberos sockets System.setProperty("https.cipherSuites", "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"); } private static void printUsage(PrintStream err) throws IOException { err.println("fetchdt retrieves delegation tokens from the NameNode"); err.println(); err.println("fetchdt <opts> <token file>"); err.println("Options:"); err.println(" --webservice <url> Url to contact NN on"); err.println(" --cancel Cancel the delegation token"); err.println(" --renew Renew the delegation token"); err.println(); GenericOptionsParser.printGenericCommandUsage(err); System.exit(1); } private static Collection<Token<?>> readTokens(Path file, Configuration conf) throws IOException { Credentials creds = Credentials.readTokenStorageFile(file, conf); return creds.getAllTokens(); } /** * Command-line interface */ public static void main(final String[] args) throws Exception { final Configuration conf = new Configuration(); Options fetcherOptions = new Options(); fetcherOptions.addOption(WEBSERVICE, true, "HTTPS url to reach the NameNode at"); fetcherOptions.addOption(CANCEL, false, "cancel the token"); fetcherOptions.addOption(RENEW, false, "renew the token"); GenericOptionsParser parser = new GenericOptionsParser(conf, fetcherOptions, args); CommandLine cmd = parser.getCommandLine(); // get options final String webUrl = cmd.hasOption(WEBSERVICE) ? cmd.getOptionValue(WEBSERVICE) : null; final boolean cancel = cmd.hasOption(CANCEL); final boolean renew = cmd.hasOption(RENEW); String[] remaining = parser.getRemainingArgs(); // check option validity if (cancel && renew) { System.err.println("ERROR: Only specify cancel or renew."); printUsage(System.err); } if (remaining.length != 1 || remaining[0].charAt(0) == '-') { System.err.println("ERROR: Must specify exactly one token file"); printUsage(System.err); } // default to using the local file system FileSystem local = FileSystem.getLocal(conf); final Path tokenFile = new Path(local.getWorkingDirectory(), remaining[0]); // Login the current user final UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); ugi.doAs(new PrivilegedExceptionAction<Object>() { @Override public Object run() throws Exception { if (cancel) { for (Token<?> token : readTokens(tokenFile, conf)) { if (token.isManaged()) { token.cancel(conf); if (LOG.isDebugEnabled()) { LOG.debug("Cancelled token for " + token.getService()); } } } } else if (renew) { for (Token<?> token : readTokens(tokenFile, conf)) { if (token.isManaged()) { token.renew(conf); if (LOG.isDebugEnabled()) { LOG.debug("Renewed token for " + token.getService()); } } } } else { if (webUrl != null) { getDTfromRemote(webUrl, null).writeTokenStorageFile(tokenFile, conf); if (LOG.isDebugEnabled()) { LOG.debug("Fetched token via http for " + webUrl); } } else { FileSystem fs = FileSystem.get(conf); Token<?> token = fs.getDelegationToken(ugi.getShortUserName()); Credentials cred = new Credentials(); cred.addToken(token.getService(), token); cred.writeTokenStorageFile(tokenFile, conf); if (LOG.isDebugEnabled()) { LOG.debug("Fetched token for " + fs.getUri() + " into " + tokenFile); } } } return null; } }); } /** * Utility method to obtain a delegation token over http * @param nnHttpAddr Namenode http addr, such as http://namenode:50070 */ static public Credentials getDTfromRemote(String nnAddr, String renewer) throws IOException { DataInputStream dis = null; InetSocketAddress serviceAddr = NetUtils.createSocketAddr(nnAddr); try { StringBuffer url = new StringBuffer(); if (renewer != null) { url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC).append("?") .append(GetDelegationTokenServlet.RENEWER).append("=").append(renewer); } else { url.append(nnAddr).append(GetDelegationTokenServlet.PATH_SPEC); } if (LOG.isDebugEnabled()) { LOG.debug("Retrieving token from: " + url); } URL remoteURL = new URL(url.toString()); SecurityUtil.fetchServiceTicket(remoteURL); URLConnection connection = remoteURL.openConnection(); InputStream in = connection.getInputStream(); Credentials ts = new Credentials(); dis = new DataInputStream(in); ts.readFields(dis); for (Token<?> token : ts.getAllTokens()) { token.setKind(HftpFileSystem.TOKEN_KIND); SecurityUtil.setTokenService(token, serviceAddr); } return ts; } catch (Exception e) { throw new IOException("Unable to obtain remote token", e); } finally { if (dis != null) dis.close(); } } /** * Renew a Delegation Token. * @param nnAddr the NameNode's address * @param tok the token to renew * @return the Date that the token will expire next. * @throws IOException */ static public long renewDelegationToken(String nnAddr, Token<DelegationTokenIdentifier> tok) throws IOException { StringBuilder buf = new StringBuilder(); buf.append(nnAddr); buf.append(RenewDelegationTokenServlet.PATH_SPEC); buf.append("?"); buf.append(RenewDelegationTokenServlet.TOKEN); buf.append("="); buf.append(tok.encodeToUrlString()); BufferedReader in = null; HttpURLConnection connection = null; try { URL url = new URL(buf.toString()); SecurityUtil.fetchServiceTicket(url); connection = (HttpURLConnection) url.openConnection(); in = new BufferedReader(new InputStreamReader(connection.getInputStream())); long result = Long.parseLong(in.readLine()); in.close(); return result; } catch (IOException ie) { LOG.info("error in renew over HTTP", ie); IOException e = null; if (connection != null) { String resp = connection.getResponseMessage(); e = getExceptionFromResponse(resp); } IOUtils.cleanup(LOG, in); if (e != null) { LOG.info("rethrowing exception from HTTP request: " + e.getLocalizedMessage()); throw e; } throw ie; } } static private IOException getExceptionFromResponse(String resp) { String exceptionClass = "", exceptionMsg = ""; if (resp != null && !resp.isEmpty()) { String[] rs = resp.split(";"); exceptionClass = rs[0]; exceptionMsg = rs[1]; } LOG.info("Error response from HTTP request=" + resp + ";ec=" + exceptionClass + ";em=" + exceptionMsg); IOException e = null; if (exceptionClass != null && !exceptionClass.isEmpty()) { if (exceptionClass.contains("InvalidToken")) { e = new org.apache.hadoop.security.token.SecretManager.InvalidToken(exceptionMsg); e.setStackTrace(new StackTraceElement[0]); // stack is not relevant } else if (exceptionClass.contains("AccessControlException")) { e = new org.apache.hadoop.security.AccessControlException(exceptionMsg); e.setStackTrace(new StackTraceElement[0]); // stack is not relevant } } LOG.info("Exception from HTTP response=" + e.getLocalizedMessage()); return e; } /** * Cancel a Delegation Token. * @param nnAddr the NameNode's address * @param tok the token to cancel * @throws IOException */ static public void cancelDelegationToken(String nnAddr, Token<DelegationTokenIdentifier> tok) throws IOException { StringBuilder buf = new StringBuilder(); buf.append(nnAddr); buf.append(CancelDelegationTokenServlet.PATH_SPEC); buf.append("?"); buf.append(CancelDelegationTokenServlet.TOKEN); buf.append("="); buf.append(tok.encodeToUrlString()); BufferedReader in = null; try { URL url = new URL(buf.toString()); SecurityUtil.fetchServiceTicket(url); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { throw new IOException("Error cancelling token:" + connection.getResponseMessage()); } } catch (IOException ie) { IOUtils.cleanup(LOG, in); throw ie; } } }