protected AbstractSftpClientExtension( String name, SftpClient client, RawSftpClient raw, boolean supported) { this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No extension name"); this.client = ValidateUtils.checkNotNull(client, "No client instance"); this.raw = ValidateUtils.checkNotNull(raw, "No raw access"); this.supported = supported; }
public MacVectorsTest(VectorSeed seed, String factoryName, String expected) { this.seed = ValidateUtils.checkNotNull(seed, "No seed"); this.macFactory = ValidateUtils.checkNotNull( BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName); this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected); }
@Override public void download(String remote, Path local, Collection<Option> options) throws IOException { local = ValidateUtils.checkNotNull(local, "Invalid argument local: %s", local); remote = ValidateUtils.checkNotNullAndNotEmpty(remote, "Invalid argument remote: %s", remote); LinkOption[] opts = IoUtils.getLinkOptions(false); if (Files.isDirectory(local, opts)) { options = addTargetIsDirectory(options); } if (options.contains(Option.TargetIsDirectory)) { Boolean status = IoUtils.checkFileExists(local, opts); if (status == null) { throw new SshException("Target directory " + local.toString() + " is probaly inaccesible"); } if (!status.booleanValue()) { throw new SshException("Target directory " + local.toString() + " does not exist"); } if (!Files.isDirectory(local, opts)) { throw new SshException("Target directory " + local.toString() + " is not a directory"); } } download(remote, local.getFileSystem(), local, options); }
public SshdSocketAddress(String hostName, int port) { ValidateUtils.checkNotNull(hostName, "Host name may not be null"); this.hostName = GenericUtils.isEmpty(hostName) ? "0.0.0.0" : hostName; ValidateUtils.checkTrue(port >= 0, "Port must be >= 0", Integer.valueOf(port)); this.port = port; }
protected Nio2Service(FactoryManager manager, IoHandler handler, AsynchronousChannelGroup group) { if (log.isTraceEnabled()) { log.trace("Creating {}", getClass().getSimpleName()); } this.manager = ValidateUtils.checkNotNull(manager, "No factory manager provided"); this.handler = ValidateUtils.checkNotNull(handler, "No I/O handler provided"); this.group = ValidateUtils.checkNotNull(group, "No async. channel group provided"); this.sessions = new ConcurrentHashMap<>(); }
public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData) throws IOException, GeneralSecurityException { ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey); ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey); try (InputStream pubStream = new ByteArrayInputStream(pubData); InputStream prvStream = new ByteArrayInputStream(prvData)) { return loadKeyPairs(resourceKey, pubStream, prvStream); } }
@Override protected OpenFuture doInit(Buffer buffer) { OpenFuture f = new DefaultOpenFuture(this); try { out = new ChannelOutputStream( this, getRemoteWindow(), log, SshConstants.SSH_MSG_CHANNEL_DATA, true); authSocket = PropertyResolverUtils.getString(this, SshAgent.SSH_AUTHSOCKET_ENV_NAME); pool = Pool.create(AprLibrary.getInstance().getRootPool()); handle = Local.create(authSocket, pool); int result = Local.connect(handle, 0); if (result != Status.APR_SUCCESS) { throwException(result); } ExecutorService service = getExecutorService(); forwardService = (service == null) ? ThreadUtils.newSingleThreadExecutor("ChannelAgentForwarding[" + authSocket + "]") : service; shutdownForwarder = service != forwardService || isShutdownOnExit(); final int copyBufSize = PropertyResolverUtils.getIntProperty( this, FORWARDER_BUFFER_SIZE, DEFAULT_FORWARDER_BUF_SIZE); ValidateUtils.checkTrue( copyBufSize >= MIN_FORWARDER_BUF_SIZE, "Copy buf size below min.: %d", copyBufSize); ValidateUtils.checkTrue( copyBufSize <= MAX_FORWARDER_BUF_SIZE, "Copy buf size above max.: %d", copyBufSize); forwarder = forwardService.submit( () -> { try { byte[] buf = new byte[copyBufSize]; while (true) { int len = Socket.recv(handle, buf, 0, buf.length); if (len > 0) { out.write(buf, 0, len); out.flush(); } } } catch (IOException e) { close(true); } }); signalChannelOpenSuccess(); f.setOpened(); } catch (Throwable t) { Throwable e = GenericUtils.peelException(t); signalChannelOpenFailure(e); f.setException(e); } return f; }
@Override public Boolean doAuth(Buffer buffer, boolean init) throws Exception { ValidateUtils.checkTrue(init, "Instance not initialized"); boolean hasSig = buffer.getBoolean(); String alg = buffer.getString(); int oldLim = buffer.wpos(); int oldPos = buffer.rpos(); int len = buffer.getInt(); buffer.wpos(buffer.rpos() + len); PublicKey key = buffer.getRawPublicKey(); ServerFactoryManager manager = session.getFactoryManager(); Signature verif = ValidateUtils.checkNotNull( NamedFactory.Utils.create(manager.getSignatureFactories(), alg), "No verifier located for algorithm=%s", alg); verif.initVerifier(key); buffer.wpos(oldLim); byte[] sig = hasSig ? buffer.getBytes() : null; PublickeyAuthenticator authenticator = ValidateUtils.checkNotNull( manager.getPublickeyAuthenticator(), "No PublickeyAuthenticator configured"); if (!authenticator.authenticate(username, key, session)) { return Boolean.FALSE; } if (!hasSig) { Buffer buf = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK); buf.putString(alg); buf.putRawBytes(buffer.array(), oldPos, 4 + len); session.writePacket(buf); return null; } else { Buffer buf = new ByteArrayBuffer(); buf.putBytes(session.getKex().getH()); buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST); buf.putString(username); buf.putString(service); buf.putString(UserAuthPublicKeyFactory.NAME); buf.putBoolean(true); buf.putString(alg); buffer.rpos(oldPos); buffer.wpos(oldPos + 4 + len); buf.putBuffer(buffer); verif.update(buf.array(), buf.rpos(), buf.available()); if (!verif.verify(sig)) { throw new Exception("Key verification failed"); } return Boolean.TRUE; } }
/** * Registered a {@link NamedFactory} to be available besides the built-in ones when parsing * configuration * * @param extension The factory to register * @throws IllegalArgumentException if factory instance is {@code null}, or overrides a built-in * one or overrides another registered factory with the same name (case <U>insensitive</U>). */ public static void registerExtension(SignatureFactory extension) { String name = Objects.requireNonNull(extension, "No extension provided").getName(); ValidateUtils.checkTrue( fromFactoryName(name) == null, "Extension overrides built-in: %s", name); synchronized (EXTENSIONS) { ValidateUtils.checkTrue( !EXTENSIONS.containsKey(name), "Extension overrides existing: %s", name); EXTENSIONS.put(name, extension); } }
public Collection<KeyPair> loadKeyPairs( String resourceKey, InputStream pubData, InputStream prvData) throws IOException, GeneralSecurityException { try (PuttyKeyReader pubReader = new PuttyKeyReader( ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey)); PuttyKeyReader prvReader = new PuttyKeyReader( ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) { return loadKeyPairs(resourceKey, pubReader, prvReader); } }
static SecurityProviderChoice toSecurityProviderChoice(String name) { ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided"); return new SecurityProviderChoice() { private final String s = SecurityProviderChoice.class.getSimpleName() + "[" + name + "]"; @Override public String getName() { return name; } @Override public boolean isNamedProviderUsed() { return true; } @Override public Provider getSecurityProvider() { return null; } @Override public String toString() { return s; } }; }
/** * @param props The {@link Properties} holding the server's configuration - ignored if {@code * null}/empty * @param options The {@link LinkOption}s to use when checking files existence * @return A {@link Map} of the found identities where key=the identity type (case * <U>insensitive</U>) and value=the {@link Path} of the file holding the specific type key * @throws IOException If failed to access the file system * @see #getIdentityType(String) * @see #HOST_KEY_CONFIG_PROP * @see org.apache.sshd.common.config.SshConfigFileReader#readConfigFile(File) */ public static Map<String, Path> findIdentities(Properties props, LinkOption... options) throws IOException { if (GenericUtils.isEmpty(props)) { return Collections.emptyMap(); } String keyList = props.getProperty(HOST_KEY_CONFIG_PROP); String[] paths = GenericUtils.split(keyList, ','); if (GenericUtils.isEmpty(paths)) { return Collections.emptyMap(); } Map<String, Path> ids = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (String p : paths) { File file = new File(p); Path path = file.toPath(); if (!Files.exists(path, options)) { continue; } String type = getIdentityType(path.getFileName().toString()); if (GenericUtils.isEmpty(type)) { type = p; // just in case the file name does not adhere to the standard naming convention } Path prev = ids.put(type, path); ValidateUtils.checkTrue(prev == null, "Multiple mappings for type=%s", type); } return ids; }
@Override public void download(String[] remote, String local, Collection<Option> options) throws IOException { local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local); remote = ValidateUtils.checkNotNullAndNotEmpty( remote, "Invalid argument remote: %s", (Object) remote); if (remote.length > 1) { options = addTargetIsDirectory(options); } for (String r : remote) { download(r, local, options); } }
@Override public void handleOpenSuccess(int recipient, int rwSize, int packetSize, Buffer buffer) { setRecipient(recipient); Session session = getSession(); FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager"); this.remoteWindow.init(rwSize, packetSize, manager.getProperties()); ChannelListener listener = getChannelListenerProxy(); try { doOpen(); listener.channelOpenSuccess(this); this.opened.set(true); this.openFuture.setOpened(); } catch (Throwable t) { Throwable e = GenericUtils.peelException(t); try { listener.channelOpenFailure(this, e); } catch (Throwable ignored) { log.warn( "handleOpenSuccess({}) failed ({}) to inform listener of open failure={}: {}", this, ignored.getClass().getSimpleName(), e.getClass().getSimpleName(), ignored.getMessage()); } this.openFuture.setException(e); this.closeFuture.setClosed(); this.doCloseImmediately(); } finally { notifyStateChanged(); } }
@Override public void upload(Path local, String remote, Collection<Option> options) throws IOException { upload( new Path[] {ValidateUtils.checkNotNull(local, "Invalid local argument: %s", local)}, remote, GenericUtils.isEmpty(options) ? Collections.<Option>emptySet() : GenericUtils.of(options)); }
@Override public void upload(String local, String remote, Collection<Option> options) throws IOException { upload( new String[] { ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local) }, remote, options); }
@Override protected void doWriteData(byte[] data, int off, long len) throws IOException { ValidateUtils.checkTrue( len <= Integer.MAX_VALUE, "Data length exceeds int boundaries: %d", len); int result = Socket.send(handle, data, off, (int) len); if (result < Status.APR_SUCCESS) { throwException(result); } }
public static <E extends KnownHostEntry> E parseKnownHostEntry(E entry, String data) { String line = data; if (GenericUtils.isEmpty(line) || (line.charAt(0) == PublicKeyEntry.COMMENT_CHAR)) { return entry; } entry.setConfigLine(line); if (line.charAt(0) == MARKER_INDICATOR) { int pos = line.indexOf(' '); ValidateUtils.checkTrue(pos > 0, "Missing marker name end delimiter in line=%s", data); ValidateUtils.checkTrue(pos > 1, "No marker name after indicator in line=%s", data); entry.setMarker(line.substring(1, pos)); line = line.substring(pos + 1).trim(); } else { entry.setMarker(null); } int pos = line.indexOf(' '); ValidateUtils.checkTrue(pos > 0, "Missing host patterns end delimiter in line=%s", data); String hostPattern = line.substring(0, pos); line = line.substring(pos + 1).trim(); if (hostPattern.charAt(0) == KnownHostHashValue.HASHED_HOST_DELIMITER) { KnownHostHashValue hash = ValidateUtils.checkNotNull( KnownHostHashValue.parse(hostPattern), "Failed to extract host hash value from line=%s", data); entry.setHashedEntry(hash); entry.setPatterns(null); } else { entry.setHashedEntry(null); entry.setPatterns(parsePatterns(GenericUtils.split(hostPattern, ','))); } AuthorizedKeyEntry key = ValidateUtils.checkNotNull( AuthorizedKeyEntry.parseAuthorizedKeyEntry(line), "No valid key entry recovered from line=%s", data); entry.setKeyEntry(key); return entry; }
@Override public synchronized SshdSocketAddress localPortForwardingRequested(SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local); FactoryManager manager = session.getFactoryManager(); ForwardingFilter filter = manager.getTcpipForwardingFilter(); if ((filter == null) || (!filter.canListen(local, session))) { if (log.isDebugEnabled()) { log.debug( "localPortForwardingRequested(" + session + ")[" + local + "][haveFilter=" + (filter != null) + "] rejected"); } throw new IOException("Rejected address: " + local); } InetSocketAddress bound = doBind(local, staticIoHandlerFactory); SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), bound.getPort()); if (log.isDebugEnabled()) { log.debug("localPortForwardingRequested(" + local + "): " + result); } boolean added; synchronized (localForwards) { // NOTE !!! it is crucial to use the bound address host name first added = localForwards.add( new LocalForwardingEntry( result.getHostName(), local.getHostName(), result.getPort())); } if (!added) { throw new IOException( "Failed to add local port forwarding entry for " + local + " -> " + result); } return result; }
protected void checkConfig() { ValidateUtils.checkNotNullAndNotEmpty( getKeyExchangeFactories(), "KeyExchangeFactories not set"); if (getScheduledExecutorService() == null) { setScheduledExecutorService( ThreadUtils.newSingleThreadScheduledExecutor(this.toString() + "-timer"), true); } ValidateUtils.checkNotNullAndNotEmpty(getCipherFactories(), "CipherFactories not set"); ValidateUtils.checkNotNullAndNotEmpty( getCompressionFactories(), "CompressionFactories not set"); ValidateUtils.checkNotNullAndNotEmpty(getMacFactories(), "MacFactories not set"); ValidateUtils.checkNotNull(getRandomFactory(), "RandomFactory not set"); if (getIoServiceFactoryFactory() == null) { setIoServiceFactoryFactory(new DefaultIoServiceFactoryFactory()); } }
@Override public synchronized SshdSocketAddress startRemotePortForwarding( SshdSocketAddress remote, SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkNotNull(remote, "Remote address is null"); Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_GLOBAL_REQUEST); buffer.putString("tcpip-forward"); buffer.putBoolean(true); buffer.putString(remote.getHostName()); buffer.putInt(remote.getPort()); Buffer result = session.request(buffer); if (result == null) { throw new SshException("Tcpip forwarding request denied by server"); } int port = (remote.getPort() == 0) ? result.getInt() : remote.getPort(); // TODO: Is it really safe to only store the local address after the request ? SshdSocketAddress prev; synchronized (remoteToLocal) { prev = remoteToLocal.put(port, local); } if (prev != null) { throw new IOException( "Multiple remote port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev); } SshdSocketAddress bound = new SshdSocketAddress(remote.getHostName(), port); if (log.isDebugEnabled()) { log.debug("startRemotePortForwarding(" + remote + " -> " + local + "): " + bound); } return bound; }
@Override public synchronized SshdSocketAddress startDynamicPortForwarding(SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local); if (isClosed()) { throw new IllegalStateException("TcpipForwarder is closed"); } if (isClosing()) { throw new IllegalStateException("TcpipForwarder is closing"); } SocksProxy socksProxy = new SocksProxy(service); SocksProxy prev; InetSocketAddress bound = doBind(local, socksProxyIoHandlerFactory); int port = bound.getPort(); synchronized (dynamicLocal) { prev = dynamicLocal.put(port, socksProxy); } if (prev != null) { throw new IOException( "Multiple dynamic port mappings found for port=" + port + ": current=" + socksProxy + ", previous=" + prev); } SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port); if (log.isDebugEnabled()) { log.debug("startDynamicPortForwarding(" + local + "): " + result); } return result; }
public static String createReceiveCommand(String remote, Collection<Option> options) { ValidateUtils.checkNotNullAndNotEmpty(remote, "No remote location specified"); StringBuilder sb = new StringBuilder(remote.length() + Long.SIZE).append(ScpHelper.SCP_COMMAND_PREFIX); if (options.contains(Option.Recursive)) { sb.append(" -r"); } if (options.contains(Option.PreserveAttributes)) { sb.append(" -p"); } sb.append(" -f").append(" --").append(' ').append(remote); return sb.toString(); }
@Override public synchronized SshdSocketAddress startLocalPortForwarding( SshdSocketAddress local, SshdSocketAddress remote) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); ValidateUtils.checkTrue(local.getPort() >= 0, "Invalid local port: %s", local); ValidateUtils.checkNotNull(remote, "Remote address is null"); if (isClosed()) { throw new IllegalStateException("TcpipForwarder is closed"); } if (isClosing()) { throw new IllegalStateException("TcpipForwarder is closing"); } InetSocketAddress bound = doBind(local, staticIoHandlerFactory); int port = bound.getPort(); SshdSocketAddress prev; synchronized (localToRemote) { prev = localToRemote.put(port, remote); } if (prev != null) { throw new IOException( "Multiple local port forwarding bindings on port=" + port + ": current=" + remote + ", previous=" + prev); } SshdSocketAddress result = new SshdSocketAddress(bound.getHostString(), port); if (log.isDebugEnabled()) { log.debug("startLocalPortForwarding(" + local + " -> " + remote + "): " + result); } return result; }
@Override public void download(String remote, String local, Collection<Option> options) throws IOException { local = ValidateUtils.checkNotNullAndNotEmpty(local, "Invalid argument local: %s", local); ClientSession session = getClientSession(); FactoryManager manager = session.getFactoryManager(); FileSystemFactory factory = manager.getFileSystemFactory(); FileSystem fs = factory.createFileSystem(session); try { download(remote, fs, fs.getPath(local), options); } finally { try { fs.close(); } catch (UnsupportedOperationException e) { // Ignore } } }
@Override public void addChannelListener(ChannelListener listener) { ValidateUtils.checkNotNull(listener, "addChannelListener(%s) null instance", this); // avoid race conditions on notifications while manager is being closed if (!isOpen()) { log.warn( "addChannelListener({})[{}] ignore registration while session is closing", this, listener); return; } if (this.channelListeners.add(listener)) { log.trace("addChannelListener({})[{}] registered", this, listener); } else { log.trace("addChannelListener({})[{}] ignored duplicate", this, listener); } }
@Override public synchronized void stopLocalPortForwarding(SshdSocketAddress local) throws IOException { ValidateUtils.checkNotNull(local, "Local address is null"); SshdSocketAddress bound; synchronized (localToRemote) { bound = localToRemote.remove(local.getPort()); } if ((bound != null) && (acceptor != null)) { if (log.isDebugEnabled()) { log.debug("stopLocalPortForwarding(" + local + ") unbind " + bound); } acceptor.unbind(bound.toInetSocketAddress()); } else { if (log.isDebugEnabled()) { log.debug("stopLocalPortForwarding(" + local + ") no mapping/acceptor for " + bound); } } }
@Override public void upload(Path[] local, String remote, Collection<Option> options) throws IOException { final Collection<Path> paths = Arrays.asList( ValidateUtils.checkNotNullAndNotEmpty( local, "Invalid argument local: %s", (Object) local)); runUpload( remote, options, paths, new ScpOperationExecutor<Path>() { @Override public void execute( ScpHelper helper, Collection<Path> local, Collection<Option> sendOptions) throws IOException { helper.sendPaths( local, sendOptions.contains(Option.Recursive), sendOptions.contains(Option.PreserveAttributes), ScpHelper.DEFAULT_SEND_BUFFER_SIZE); } }); }
@Override public PathMatcher getPathMatcher(String syntaxAndPattern) { int colonIndex = ValidateUtils.checkNotNull(syntaxAndPattern, "No argument").indexOf(':'); if ((colonIndex <= 0) || (colonIndex == syntaxAndPattern.length() - 1)) { throw new IllegalArgumentException( "syntaxAndPattern must have form \"syntax:pattern\" but was \"" + syntaxAndPattern + "\""); } String syntax = syntaxAndPattern.substring(0, colonIndex); String pattern = syntaxAndPattern.substring(colonIndex + 1); String expr; switch (syntax) { case "glob": expr = globToRegex(pattern); break; case "regex": expr = pattern; break; default: throw new UnsupportedOperationException( "Unsupported path matcher syntax: \'" + syntax + "\'"); } if (log.isTraceEnabled()) { log.trace("getPathMatcher({}): {}", syntaxAndPattern, expr); } final Pattern regex = Pattern.compile(expr); return new PathMatcher() { @Override public boolean matches(Path path) { Matcher m = regex.matcher(path.toString()); return m.matches(); } }; }
@Override public Set<ClientChannelEvent> waitFor(Collection<ClientChannelEvent> mask, long timeout) { ValidateUtils.checkNotNull(mask, "No mask specified"); long t = 0; synchronized (lock) { for (Set<ClientChannelEvent> cond = EnumSet.noneOf(ClientChannelEvent.class); ; cond.clear()) { if (openFuture != null && openFuture.isOpened()) { cond.add(ClientChannelEvent.OPENED); } if (closeFuture.isClosed()) { cond.add(ClientChannelEvent.CLOSED); cond.add(ClientChannelEvent.EOF); } if (isEofSignalled()) { cond.add(ClientChannelEvent.EOF); } if (exitStatusHolder.get() != null) { if (log.isDebugEnabled()) { log.debug("waitFor({}) mask={} - exit status={}", this, mask, exitStatusHolder); } cond.add(ClientChannelEvent.EXIT_STATUS); } if (exitSignalHolder.get() != null) { if (log.isDebugEnabled()) { log.debug("waitFor({}) mask={} - exit signal={}", this, mask, exitSignalHolder); } cond.add(ClientChannelEvent.EXIT_SIGNAL); } boolean nothingInCommon = Collections.disjoint(mask, cond); if (!nothingInCommon) { if (log.isTraceEnabled()) { log.trace("WaitFor call returning on channel {}, mask={}, cond={}", this, mask, cond); } return cond; } if (timeout > 0L) { if (t == 0L) { t = System.currentTimeMillis() + timeout; } else { timeout = t - System.currentTimeMillis(); if (timeout <= 0L) { if (log.isTraceEnabled()) { log.trace("WaitFor call timeout on channel {}, mask={}", this, mask); } cond.add(ClientChannelEvent.TIMEOUT); return cond; } } } if (log.isTraceEnabled()) { log.trace( "Waiting {} millis for lock on channel {}, mask={}, cond={}", timeout, this, mask, cond); } long nanoStart = System.nanoTime(); try { if (timeout > 0L) { lock.wait(timeout); } else { lock.wait(); } long nanoEnd = System.nanoTime(); long nanoDuration = nanoEnd - nanoStart; if (log.isTraceEnabled()) { log.trace("Lock notified on channel {} after {} nanos", this, nanoDuration); } } catch (InterruptedException e) { long nanoEnd = System.nanoTime(); long nanoDuration = nanoEnd - nanoStart; if (log.isTraceEnabled()) { log.trace( "waitFor({}) mask={} - ignoring interrupted exception after {} nanos", this, mask, nanoDuration); } } } } }