NativeLinuxSocketImpl.java
package eu.javaexperience.nativ.java.socket;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.SocketImplFactory;
import java.util.Map;
import java.util.Map.Entry;
import eu.javaexperience.collection.map.SmallMap;
import eu.javaexperience.exceptions.UnimplementedMethodException;
import eu.javaexperience.io.fd.FDIOStream;
import eu.javaexperience.nativ.java.JavaNativeExtension;
import eu.javaexperience.nativ.java.SockAddr;
import eu.javaexperience.nativ.posix.INTEGER;
import eu.javaexperience.nativ.posix.Posix;
import eu.javaexperience.nativ.posix.PosixErrnoException;
import eu.javaexperience.reflect.CastTo;
import eu.javaexperience.reflect.Mirror;
public class NativeLinuxSocketImpl extends SocketImpl implements FileDescriptorHolder, FDIOStream
{
public static final SocketImplFactory FACTORY = new SocketImplFactory()
{
@Override
public SocketImpl createSocketImpl()
{
return new NativeLinuxSocketImpl();
}
};
public static void overrideJavaSocketFacilities() throws IOException
{
ServerSocket.setSocketFactory(FACTORY);
Socket.setSocketImplFactory(FACTORY);
}
protected int fileDescriptor;
@Override
public FileDescriptor getFileDescriptor()
{
return super.getFileDescriptor();
}
public NativeLinuxSocketImpl()
{
setFd(-1);
}
public NativeLinuxSocketImpl(int fd)
{
setFd(fileDescriptor);
}
protected void setFd(int fd)
{
this.fileDescriptor = fd;
super.fd = fileDescriptor < 0? null:new INTEGER(fd).asFileDescriptor();
setSockAddr(null);
}
protected SockAddr addr;
protected void setSockAddr(SockAddr addr)
{
if(null != this.addr)
{
this.addr = null;
}
this.addr = addr;
}
Map<Integer, Integer> opts = new SmallMap<>();
@Override
public void setOption(int optID, Object value) throws SocketException
{
Integer v = (Integer) CastTo.Int.cast(value);
if(null != v)
{
if(fileDescriptor >= 0)
{
applyOneOption(optID, v);
}
opts.put(optID, v);
}
}
protected static final boolean printSetOpts = false;
protected void applyOneOption(int k, int v)
{
long addr = 0;
try
{
addr = Posix.malloc(4);
Posix.setIntAt(addr, v);
int ret = Posix.setsockopt(fileDescriptor, Posix.SOL_SOCKET, k, addr, 4);
if(printSetOpts)System.out.println("NativeLinuxSocketImpl.applyOneOption"+this+" "+k+" => "+v+" ret: "+ret+" "+PosixErrnoException.getIfOcurred());
}
finally
{
Posix.free(addr);
}
}
protected void applyOptions()
{
long addr = 0;
try
{
addr = Posix.malloc(4);
for(Entry<Integer, Integer> kv:opts.entrySet())
{
Posix.setIntAt(addr, kv.getValue());
int ret = Posix.setsockopt(fileDescriptor, Posix.SOL_SOCKET, kv.getKey(), addr, 4);
if(printSetOpts)System.out.println("NativeLinuxSocketImpl.applyOptions"+this+" "+kv.getKey()+" => "+kv.getValue()+" ret: "+ret);
}
}
finally
{
Posix.free(addr);
}
}
@Override
public Object getOption(int optID) throws SocketException
{
if(printSetOpts)System.out.println("NativeLinuxSocketImpl.getOption"+this+" "+optID);
return null;//opts.get(optID);
}
protected boolean createWithStream = true;
protected void tryCreateLazy(int family)
{
if(fileDescriptor < 0)
{
int ret = Posix.socket(family, createWithStream?Posix.SOCK_STREAM:Posix.SOCK_DGRAM, 0);
checkException(ret, false);
setFd(ret);
if(0 != JavaNativeExtension.initSocket(ret))
{
PosixErrnoException.throwRtIfErrorOcurred();
}
applyOptions();
}
}
@Override
public void create(boolean stream) throws IOException
{
createWithStream = stream;
}
public void connect(SockAddr a)
{
tryCreateLazy(a.getFamily());
setSockAddr(a);
int ret = Posix.connect(fileDescriptor, addr.getAddrPointer(), addr.getAddrLen());
checkException(ret, true);
}
@Override
public void connect(String host, int port) throws IOException
{
connect(SockAddr.fromIpv4(host, port));
}
@Override
public void connect(InetAddress address, int port) throws IOException
{
connect(SockAddr.fromIpv4(address.getHostAddress(), port));
}
@Override
public void connect(SocketAddress address, int timeout) throws IOException
{
//TODO timeout
connect(SockAddr.fromSocketaddress((InetSocketAddress) address));
}
public void bind(SockAddr a) throws IOException
{
tryCreateLazy(a.getFamily());
setSockAddr(a);
int ret = -1;
/*int att = 10;
do
{*/
ret = Posix.bind(fileDescriptor, addr.getAddrPointer(), addr.getAddrLen());
if(0 == ret)
{
return;
}
/*try
{
Thread.sleep(200);
}
catch(InterruptedException e)
{
Mirror.propagateAnyway(e);
}
}
while(att-- > 0);*/
checkException(ret, true);
}
@Override
public void bind(InetAddress host, int port) throws IOException
{
bind(SockAddr.fromIpv4(host, port));
}
@Override
public void listen(int backlog) throws IOException
{
int ret = Posix.listen(fileDescriptor, backlog);
checkException(ret, false);
}
@Override
public void accept(SocketImpl _s) throws IOException
{
NativeLinuxSocketImpl s = (NativeLinuxSocketImpl) _s;
int ret = JavaNativeExtension.accept(fileDescriptor, addr.getAddrPointer(), addr.getAddrLen());
checkException(ret, false);
s.setFd(ret);
}
protected NativeLinuxInputStream is = new NativeLinuxInputStream(this);
protected NativeLinuxOutputStream os = new NativeLinuxOutputStream(this);
@Override
public InputStream getInputStream()
{
return is;
}
@Override
public OutputStream getOutputStream()
{
return os;
}
@Override
protected int available() throws IOException
{
throw new UnimplementedMethodException("NativeLinuxSocketImpl.available");
}
@Override
public void close()
{
Posix.close(fileDescriptor);
setFd(-1);
}
@Override
protected void sendUrgentData(int data) throws IOException
{
throw new UnimplementedMethodException("NativeLinuxSocketImpl.sendUrgentData");
}
@Override
public int getFd()
{
return fileDescriptor;
}
protected boolean shIn = false;
protected boolean shOut = false;
protected void closeIfBothShutdown()
{
if(shIn && shOut)
{
close();
}
}
@Override
protected void shutdownInput() throws IOException
{
try
{
Posix.shutdown(fileDescriptor, Posix.SHUT_RD);
PosixErrnoException.throwRtIfErrorOcurred();
}
finally
{
shIn = true;
closeIfBothShutdown();
}
}
@Override
protected void shutdownOutput() throws IOException
{
try
{
Posix.shutdown(fileDescriptor, Posix.SHUT_WR);
PosixErrnoException.throwRtIfErrorOcurred();
}
finally
{
shOut = true;
closeIfBothShutdown();
}
}
public static void checkException(int ret, boolean zeroOnly)
{
if(zeroOnly?0 != ret:ret < 0)
{
try
{
PosixErrnoException.throwIfErrorOcurred();
}
catch(Exception e)
{
Mirror.propagateAnyway(e);
}
}
}
@Override
public boolean isClosed()
{
return -1 != fileDescriptor;
}
@Override
public String localAddress()
{
// TODO Auto-generated method stub
return null;
}
@Override
public String remoteAddress()
{
// TODO Auto-generated method stub
return null;
}
@Override
public void flush() throws IOException
{
os.flush();
}
@Override
public int getFD()
{
return fileDescriptor;
}
@Override
protected void finalize() throws Throwable
{
close();
}
}