001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.hdfs;
019    
020    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT;
021    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
022    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX;
023    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT;
024    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY;
025    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT;
026    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY;
027    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_MAX_ATTEMPTS_KEY;
028    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_MAX_ATTEMPTS_DEFAULT;
029    
030    import java.io.IOException;
031    import java.lang.reflect.Constructor;
032    import java.lang.reflect.InvocationHandler;
033    import java.lang.reflect.Proxy;
034    import java.net.InetSocketAddress;
035    import java.net.URI;
036    import java.util.HashMap;
037    import java.util.Map;
038    import java.util.concurrent.TimeUnit;
039    
040    import org.apache.commons.logging.Log;
041    import org.apache.commons.logging.LogFactory;
042    import org.apache.hadoop.conf.Configuration;
043    import org.apache.hadoop.hdfs.DFSClient.Conf;
044    import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
045    import org.apache.hadoop.hdfs.protocol.ClientProtocol;
046    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
047    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB;
048    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB;
049    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolPB;
050    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolTranslatorPB;
051    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolPB;
052    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB;
053    import org.apache.hadoop.hdfs.server.namenode.NameNode;
054    import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
055    import org.apache.hadoop.hdfs.server.protocol.JournalProtocol;
056    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocol;
057    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
058    import org.apache.hadoop.io.Text;
059    import org.apache.hadoop.io.retry.DefaultFailoverProxyProvider;
060    import org.apache.hadoop.io.retry.FailoverProxyProvider;
061    import org.apache.hadoop.io.retry.LossyRetryInvocationHandler;
062    import org.apache.hadoop.io.retry.RetryPolicies;
063    import org.apache.hadoop.io.retry.RetryPolicy;
064    import org.apache.hadoop.io.retry.RetryProxy;
065    import org.apache.hadoop.io.retry.RetryUtils;
066    import org.apache.hadoop.ipc.ProtobufRpcEngine;
067    import org.apache.hadoop.ipc.RPC;
068    import org.apache.hadoop.ipc.RemoteException;
069    import org.apache.hadoop.net.NetUtils;
070    import org.apache.hadoop.security.RefreshUserMappingsProtocol;
071    import org.apache.hadoop.security.SecurityUtil;
072    import org.apache.hadoop.security.UserGroupInformation;
073    import org.apache.hadoop.security.authorize.RefreshAuthorizationPolicyProtocol;
074    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolClientSideTranslatorPB;
075    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolPB;
076    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolClientSideTranslatorPB;
077    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolPB;
078    import org.apache.hadoop.ipc.RefreshCallQueueProtocol;
079    import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolPB;
080    import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolClientSideTranslatorPB;
081    import org.apache.hadoop.tools.GetUserMappingsProtocol;
082    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolClientSideTranslatorPB;
083    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolPB;
084    
085    import com.google.common.annotations.VisibleForTesting;
086    import com.google.common.base.Preconditions;
087    
088    /**
089     * Create proxy objects to communicate with a remote NN. All remote access to an
090     * NN should be funneled through this class. Most of the time you'll want to use
091     * {@link NameNodeProxies#createProxy(Configuration, URI, Class)}, which will
092     * create either an HA- or non-HA-enabled client proxy as appropriate.
093     */
094    public class NameNodeProxies {
095      
096      private static final Log LOG = LogFactory.getLog(NameNodeProxies.class);
097    
098      /**
099       * Wrapper for a client proxy as well as its associated service ID.
100       * This is simply used as a tuple-like return type for
101       * {@link NameNodeProxies#createProxy} and
102       * {@link NameNodeProxies#createNonHAProxy}.
103       */
104      public static class ProxyAndInfo<PROXYTYPE> {
105        private final PROXYTYPE proxy;
106        private final Text dtService;
107        
108        public ProxyAndInfo(PROXYTYPE proxy, Text dtService) {
109          this.proxy = proxy;
110          this.dtService = dtService;
111        }
112        
113        public PROXYTYPE getProxy() {
114          return proxy;
115        }
116        
117        public Text getDelegationTokenService() {
118          return dtService;
119        }
120      }
121    
122      /**
123       * Creates the namenode proxy with the passed protocol. This will handle
124       * creation of either HA- or non-HA-enabled proxy objects, depending upon
125       * if the provided URI is a configured logical URI.
126       * 
127       * @param conf the configuration containing the required IPC
128       *        properties, client failover configurations, etc.
129       * @param nameNodeUri the URI pointing either to a specific NameNode
130       *        or to a logical nameservice.
131       * @param xface the IPC interface which should be created
132       * @return an object containing both the proxy and the associated
133       *         delegation token service it corresponds to
134       * @throws IOException if there is an error creating the proxy
135       **/
136      @SuppressWarnings("unchecked")
137      public static <T> ProxyAndInfo<T> createProxy(Configuration conf,
138          URI nameNodeUri, Class<T> xface) throws IOException {
139        Class<FailoverProxyProvider<T>> failoverProxyProviderClass =
140            getFailoverProxyProviderClass(conf, nameNodeUri, xface);
141      
142        if (failoverProxyProviderClass == null) {
143          // Non-HA case
144          return createNonHAProxy(conf, NameNode.getAddress(nameNodeUri), xface,
145              UserGroupInformation.getCurrentUser(), true);
146        } else {
147          // HA case
148          FailoverProxyProvider<T> failoverProxyProvider = NameNodeProxies
149              .createFailoverProxyProvider(conf, failoverProxyProviderClass, xface,
150                  nameNodeUri);
151          Conf config = new Conf(conf);
152          T proxy = (T) RetryProxy.create(xface, failoverProxyProvider,
153              RetryPolicies.failoverOnNetworkException(
154                  RetryPolicies.TRY_ONCE_THEN_FAIL, config.maxFailoverAttempts,
155                  config.maxRetryAttempts, config.failoverSleepBaseMillis,
156                  config.failoverSleepMaxMillis));
157          
158          Text dtService = HAUtil.buildTokenServiceForLogicalUri(nameNodeUri);
159          return new ProxyAndInfo<T>(proxy, dtService);
160        }
161      }
162      
163      /**
164       * Generate a dummy namenode proxy instance that utilizes our hacked
165       * {@link LossyRetryInvocationHandler}. Proxy instance generated using this
166       * method will proactively drop RPC responses. Currently this method only
167       * support HA setup. null will be returned if the given configuration is not 
168       * for HA.
169       * 
170       * @param config the configuration containing the required IPC
171       *        properties, client failover configurations, etc.
172       * @param nameNodeUri the URI pointing either to a specific NameNode
173       *        or to a logical nameservice.
174       * @param xface the IPC interface which should be created
175       * @param numResponseToDrop The number of responses to drop for each RPC call
176       * @return an object containing both the proxy and the associated
177       *         delegation token service it corresponds to. Will return null of the
178       *         given configuration does not support HA.
179       * @throws IOException if there is an error creating the proxy
180       */
181      @SuppressWarnings("unchecked")
182      public static <T> ProxyAndInfo<T> createProxyWithLossyRetryHandler(
183          Configuration config, URI nameNodeUri, Class<T> xface,
184          int numResponseToDrop) throws IOException {
185        Preconditions.checkArgument(numResponseToDrop > 0);
186        Class<FailoverProxyProvider<T>> failoverProxyProviderClass = 
187            getFailoverProxyProviderClass(config, nameNodeUri, xface);
188        if (failoverProxyProviderClass != null) { // HA case
189          FailoverProxyProvider<T> failoverProxyProvider = 
190              createFailoverProxyProvider(config, failoverProxyProviderClass, 
191                  xface, nameNodeUri);
192          int delay = config.getInt(
193              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY,
194              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT);
195          int maxCap = config.getInt(
196              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY,
197              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT);
198          int maxFailoverAttempts = config.getInt(
199              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY,
200              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT);
201          int maxRetryAttempts = config.getInt(
202              DFS_CLIENT_RETRY_MAX_ATTEMPTS_KEY,
203              DFS_CLIENT_RETRY_MAX_ATTEMPTS_DEFAULT);
204          InvocationHandler dummyHandler = new LossyRetryInvocationHandler<T>(
205                  numResponseToDrop, failoverProxyProvider,
206                  RetryPolicies.failoverOnNetworkException(
207                      RetryPolicies.TRY_ONCE_THEN_FAIL, maxFailoverAttempts, 
208                      Math.max(numResponseToDrop + 1, maxRetryAttempts), delay, 
209                      maxCap));
210          
211          T proxy = (T) Proxy.newProxyInstance(
212              failoverProxyProvider.getInterface().getClassLoader(),
213              new Class[] { xface }, dummyHandler);
214          Text dtService = HAUtil.buildTokenServiceForLogicalUri(nameNodeUri);
215          return new ProxyAndInfo<T>(proxy, dtService);
216        } else {
217          LOG.warn("Currently creating proxy using " +
218                    "LossyRetryInvocationHandler requires NN HA setup");
219          return null;
220        }
221      }
222    
223      /**
224       * Creates an explicitly non-HA-enabled proxy object. Most of the time you
225       * don't want to use this, and should instead use {@link NameNodeProxies#createProxy}.
226       * 
227       * @param conf the configuration object
228       * @param nnAddr address of the remote NN to connect to
229       * @param xface the IPC interface which should be created
230       * @param ugi the user who is making the calls on the proxy object
231       * @param withRetries certain interfaces have a non-standard retry policy
232       * @return an object containing both the proxy and the associated
233       *         delegation token service it corresponds to
234       * @throws IOException
235       */
236      @SuppressWarnings("unchecked")
237      public static <T> ProxyAndInfo<T> createNonHAProxy(
238          Configuration conf, InetSocketAddress nnAddr, Class<T> xface,
239          UserGroupInformation ugi, boolean withRetries) throws IOException {
240        Text dtService = SecurityUtil.buildTokenService(nnAddr);
241      
242        T proxy;
243        if (xface == ClientProtocol.class) {
244          proxy = (T) createNNProxyWithClientProtocol(nnAddr, conf, ugi,
245              withRetries);
246        } else if (xface == JournalProtocol.class) {
247          proxy = (T) createNNProxyWithJournalProtocol(nnAddr, conf, ugi);
248        } else if (xface == NamenodeProtocol.class) {
249          proxy = (T) createNNProxyWithNamenodeProtocol(nnAddr, conf, ugi,
250              withRetries);
251        } else if (xface == GetUserMappingsProtocol.class) {
252          proxy = (T) createNNProxyWithGetUserMappingsProtocol(nnAddr, conf, ugi);
253        } else if (xface == RefreshUserMappingsProtocol.class) {
254          proxy = (T) createNNProxyWithRefreshUserMappingsProtocol(nnAddr, conf, ugi);
255        } else if (xface == RefreshAuthorizationPolicyProtocol.class) {
256          proxy = (T) createNNProxyWithRefreshAuthorizationPolicyProtocol(nnAddr,
257              conf, ugi);
258        } else if (xface == RefreshCallQueueProtocol.class) {
259          proxy = (T) createNNProxyWithRefreshCallQueueProtocol(nnAddr, conf, ugi);
260        } else {
261          String message = "Unsupported protocol found when creating the proxy " +
262              "connection to NameNode: " +
263              ((xface != null) ? xface.getClass().getName() : "null");
264          LOG.error(message);
265          throw new IllegalStateException(message);
266        }
267    
268        return new ProxyAndInfo<T>(proxy, dtService);
269      }
270      
271      private static JournalProtocol createNNProxyWithJournalProtocol(
272          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
273          throws IOException {
274        JournalProtocolPB proxy = (JournalProtocolPB) createNameNodeProxy(address,
275            conf, ugi, JournalProtocolPB.class);
276        return new JournalProtocolTranslatorPB(proxy);
277      }
278    
279      private static RefreshAuthorizationPolicyProtocol
280          createNNProxyWithRefreshAuthorizationPolicyProtocol(InetSocketAddress address,
281              Configuration conf, UserGroupInformation ugi) throws IOException {
282        RefreshAuthorizationPolicyProtocolPB proxy = (RefreshAuthorizationPolicyProtocolPB)
283            createNameNodeProxy(address, conf, ugi, RefreshAuthorizationPolicyProtocolPB.class);
284        return new RefreshAuthorizationPolicyProtocolClientSideTranslatorPB(proxy);
285      }
286      
287      private static RefreshUserMappingsProtocol
288          createNNProxyWithRefreshUserMappingsProtocol(InetSocketAddress address,
289              Configuration conf, UserGroupInformation ugi) throws IOException {
290        RefreshUserMappingsProtocolPB proxy = (RefreshUserMappingsProtocolPB)
291            createNameNodeProxy(address, conf, ugi, RefreshUserMappingsProtocolPB.class);
292        return new RefreshUserMappingsProtocolClientSideTranslatorPB(proxy);
293      }
294    
295      private static RefreshCallQueueProtocol
296          createNNProxyWithRefreshCallQueueProtocol(InetSocketAddress address,
297              Configuration conf, UserGroupInformation ugi) throws IOException {
298        RefreshCallQueueProtocolPB proxy = (RefreshCallQueueProtocolPB)
299            createNameNodeProxy(address, conf, ugi, RefreshCallQueueProtocolPB.class);
300        return new RefreshCallQueueProtocolClientSideTranslatorPB(proxy);
301      }
302    
303      private static GetUserMappingsProtocol createNNProxyWithGetUserMappingsProtocol(
304          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
305          throws IOException {
306        GetUserMappingsProtocolPB proxy = (GetUserMappingsProtocolPB)
307            createNameNodeProxy(address, conf, ugi, GetUserMappingsProtocolPB.class);
308        return new GetUserMappingsProtocolClientSideTranslatorPB(proxy);
309      }
310      
311      private static NamenodeProtocol createNNProxyWithNamenodeProtocol(
312          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
313          boolean withRetries) throws IOException {
314        NamenodeProtocolPB proxy = (NamenodeProtocolPB) createNameNodeProxy(
315            address, conf, ugi, NamenodeProtocolPB.class);
316        if (withRetries) { // create the proxy with retries
317          RetryPolicy timeoutPolicy = RetryPolicies.exponentialBackoffRetry(5, 200,
318              TimeUnit.MILLISECONDS);
319          Map<Class<? extends Exception>, RetryPolicy> exceptionToPolicyMap 
320                         = new HashMap<Class<? extends Exception>, RetryPolicy>();
321          RetryPolicy methodPolicy = RetryPolicies.retryByException(timeoutPolicy,
322              exceptionToPolicyMap);
323          Map<String, RetryPolicy> methodNameToPolicyMap 
324                         = new HashMap<String, RetryPolicy>();
325          methodNameToPolicyMap.put("getBlocks", methodPolicy);
326          methodNameToPolicyMap.put("getAccessKeys", methodPolicy);
327          proxy = (NamenodeProtocolPB) RetryProxy.create(NamenodeProtocolPB.class,
328              proxy, methodNameToPolicyMap);
329        }
330        return new NamenodeProtocolTranslatorPB(proxy);
331      }
332      
333      private static ClientProtocol createNNProxyWithClientProtocol(
334          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
335          boolean withRetries) throws IOException {
336        RPC.setProtocolEngine(conf, ClientNamenodeProtocolPB.class, ProtobufRpcEngine.class);
337    
338        final RetryPolicy defaultPolicy = 
339            RetryUtils.getDefaultRetryPolicy(
340                conf, 
341                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_KEY, 
342                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_DEFAULT, 
343                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_KEY,
344                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_DEFAULT,
345                SafeModeException.class);
346        
347        final long version = RPC.getProtocolVersion(ClientNamenodeProtocolPB.class);
348        ClientNamenodeProtocolPB proxy = RPC.getProtocolProxy(
349            ClientNamenodeProtocolPB.class, version, address, ugi, conf,
350            NetUtils.getDefaultSocketFactory(conf),
351            org.apache.hadoop.ipc.Client.getTimeout(conf), defaultPolicy)
352                .getProxy();
353    
354        if (withRetries) { // create the proxy with retries
355    
356          RetryPolicy createPolicy = RetryPolicies
357              .retryUpToMaximumCountWithFixedSleep(5,
358                  HdfsConstants.LEASE_SOFTLIMIT_PERIOD, TimeUnit.MILLISECONDS);
359        
360          Map<Class<? extends Exception>, RetryPolicy> remoteExceptionToPolicyMap 
361                     = new HashMap<Class<? extends Exception>, RetryPolicy>();
362          remoteExceptionToPolicyMap.put(AlreadyBeingCreatedException.class,
363              createPolicy);
364        
365          Map<Class<? extends Exception>, RetryPolicy> exceptionToPolicyMap
366                     = new HashMap<Class<? extends Exception>, RetryPolicy>();
367          exceptionToPolicyMap.put(RemoteException.class, RetryPolicies
368              .retryByRemoteException(defaultPolicy,
369                  remoteExceptionToPolicyMap));
370          RetryPolicy methodPolicy = RetryPolicies.retryByException(
371              defaultPolicy, exceptionToPolicyMap);
372          Map<String, RetryPolicy> methodNameToPolicyMap 
373                     = new HashMap<String, RetryPolicy>();
374        
375          methodNameToPolicyMap.put("create", methodPolicy);
376        
377          proxy = (ClientNamenodeProtocolPB) RetryProxy.create(
378              ClientNamenodeProtocolPB.class,
379              new DefaultFailoverProxyProvider<ClientNamenodeProtocolPB>(
380                  ClientNamenodeProtocolPB.class, proxy),
381              methodNameToPolicyMap,
382              defaultPolicy);
383        }
384        return new ClientNamenodeProtocolTranslatorPB(proxy);
385      }
386      
387      private static Object createNameNodeProxy(InetSocketAddress address,
388          Configuration conf, UserGroupInformation ugi, Class<?> xface)
389          throws IOException {
390        RPC.setProtocolEngine(conf, xface, ProtobufRpcEngine.class);
391        Object proxy = RPC.getProxy(xface, RPC.getProtocolVersion(xface), address,
392            ugi, conf, NetUtils.getDefaultSocketFactory(conf));
393        return proxy;
394      }
395    
396      /** Gets the configured Failover proxy provider's class */
397      @VisibleForTesting
398      public static <T> Class<FailoverProxyProvider<T>> getFailoverProxyProviderClass(
399          Configuration conf, URI nameNodeUri, Class<T> xface) throws IOException {
400        if (nameNodeUri == null) {
401          return null;
402        }
403        String host = nameNodeUri.getHost();
404      
405        String configKey = DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX + "."
406            + host;
407        try {
408          @SuppressWarnings("unchecked")
409          Class<FailoverProxyProvider<T>> ret = (Class<FailoverProxyProvider<T>>) conf
410              .getClass(configKey, null, FailoverProxyProvider.class);
411          if (ret != null) {
412            // If we found a proxy provider, then this URI should be a logical NN.
413            // Given that, it shouldn't have a non-default port number.
414            int port = nameNodeUri.getPort();
415            if (port > 0 && port != NameNode.DEFAULT_PORT) {
416              throw new IOException("Port " + port + " specified in URI "
417                  + nameNodeUri + " but host '" + host
418                  + "' is a logical (HA) namenode"
419                  + " and does not use port information.");
420            }
421          }
422          return ret;
423        } catch (RuntimeException e) {
424          if (e.getCause() instanceof ClassNotFoundException) {
425            throw new IOException("Could not load failover proxy provider class "
426                + conf.get(configKey) + " which is configured for authority "
427                + nameNodeUri, e);
428          } else {
429            throw e;
430          }
431        }
432      }
433    
434      /** Creates the Failover proxy provider instance*/
435      @VisibleForTesting
436      public static <T> FailoverProxyProvider<T> createFailoverProxyProvider(
437          Configuration conf, Class<FailoverProxyProvider<T>> failoverProxyProviderClass,
438          Class<T> xface, URI nameNodeUri) throws IOException {
439        Preconditions.checkArgument(
440            xface.isAssignableFrom(NamenodeProtocols.class),
441            "Interface %s is not a NameNode protocol", xface);
442        try {
443          Constructor<FailoverProxyProvider<T>> ctor = failoverProxyProviderClass
444              .getConstructor(Configuration.class, URI.class, Class.class);
445          FailoverProxyProvider<T> provider = ctor.newInstance(conf, nameNodeUri,
446              xface);
447          return provider;
448        } catch (Exception e) {
449          String message = "Couldn't create proxy provider " + failoverProxyProviderClass;
450          if (LOG.isDebugEnabled()) {
451            LOG.debug(message, e);
452          }
453          if (e.getCause() instanceof IOException) {
454            throw (IOException) e.getCause();
455          } else {
456            throw new IOException(message, e);
457          }
458        }
459      }
460    
461    }