/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. */ package com.ubergeek42.weechat.relay.connection; import com.jcraft.jsch.SocketFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; class SocketChannelFactory implements SocketFactory { private SocketChannel channel; // JSch doesn't expose the cause of exceptions raised by createSocket. // Throw a RuntimeException so we know if we were interrupted or if // there was some other connection failure. @Override public Socket createSocket(String host, int port) throws IOException { try { channel = SocketChannel.open(); channel.connect(new InetSocketAddress(host, port)); return channel.socket(); } catch (ClosedByInterruptException e) { throw new RuntimeException(e); } } @Override public InputStream getInputStream(Socket socket) throws IOException { return socket.getInputStream(); } // you might ask, why on earth don't you just do socket.getOutputStream()? // well that's because somewhere deep in the java it's been decided that in some cases // there shall be a deadlock when you try to concurrently read and write both streams // WARNING: this method assumes that createSocket() is called before it @Override public OutputStream getOutputStream(Socket socket) throws IOException { return Channels.newOutputStream(new WritableByteChannel() { @Override public int write(ByteBuffer byteBuffer) throws IOException { return channel.write(byteBuffer); } @Override public boolean isOpen() { return channel.isOpen(); } @Override public void close() throws IOException { channel.close(); } }); } }