/**************************************************************************
* Copyright (c) 2009, 2015 by Chris Gray, KIFFER Ltd. *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of KIFFER Ltd nor the names of other contributors *
* may be used to endorse or promote products derived from this *
* software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL KIFFER LTD OR OTHER CONTRIBUTORS BE LIABLE FOR ANY *
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS *
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING *
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
package java.net;
import java.io.FileDescriptor;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
/**
* Package-local implementation of SocketImpl for "plain vanilla" sockets.
*/
class PlainSocketImpl extends SocketImpl {
/**
* Set of all sockets for which listen(), accept(), or connect() has been
* called but not yet close(). We store these as weak references to avoid
* accidentally keeping sockets from getting garbage-collected. During
* VM shutdown we try to close() any sockets which are found in this set.
*/
private static HashSet opensockets;
/**
* Number of references which have been added to <code>opensockets</code>
* since the last purge of stale references.
*/
private static int regcount;
static {
opensockets = new HashSet();
/*
* We add a shutdown hook which attempts to close() every socket
* found in <code>opensockets</code>.
*/
Runtime.getRuntime().addShutdownHook(new Thread("CloseOpenSockets") {
public void run() {
Iterator iter = opensockets.iterator();
ArrayList threadlist = new ArrayList();
while(iter.hasNext()) {
threadlist.add(iter.next());
}
iter = threadlist.iterator();
while(iter.hasNext()) {
WeakReference wr = (WeakReference)iter.next();
PlainSocketImpl psi = (PlainSocketImpl)wr.get();
if (psi != null) {
try {
Thread t = SocketUsers.get(psi);
if (t != null) {
psi.signal(t);
}
psi._close();
}
catch (IOException ioe) {
}
}
}
}
});
}
private InputStream in;
private OutputStream out;
private int timeout;
private boolean oob;
private boolean keepalive;
private boolean nodelay;
private int linger = -1;
private boolean open;
private WeakReference wr;
InetAddress localAddress;
private boolean ipv6;
public PlainSocketImpl() { }
public synchronized Object getOption(int opt) throws SocketException {
switch (opt) {
case SO_TIMEOUT:
return new Integer(timeout);
case SO_RCVBUF:
return new Integer(getRcvBuf());
case SO_SNDBUF:
return new Integer(getSndBuf());
case SO_KEEPALIVE:
return new Boolean(keepalive);
case SO_LINGER:
return new Integer(linger);
case SO_OOBINLINE:
return new Boolean(oob);
case TCP_NODELAY:
return new Boolean(nodelay);
case SO_BINDADDR:
if (ipv6) {
Inet6Address i6a = new Inet6Address();
byte[] b = new byte[16];
getLocal6Address(b);
return i6a;
}
Inet4Address i4a = new Inet4Address();
i4a.address = getLocal4Address();
return i4a;
case IP_TOS:
return new Integer(getIpTos());
default:
return PlainDatagramSocketImpl.options(opt, null, getSocket());
}
}
public synchronized void setOption(int opt, Object value) throws SocketException {
if(value == null){
throw new SocketException("a non 'null' option value is required");
}
switch (opt) {
case SO_TIMEOUT:
if(value instanceof Integer){
Integer iv = (Integer)value;
timeout = iv.intValue();
setSoTimeout(timeout);
}
break;
case SO_RCVBUF:
if(value instanceof Integer){
setRcvBuf(((Integer)value).intValue());
}
break;
case SO_SNDBUF:
if(value instanceof Integer){
setSndBuf(((Integer)value).intValue());
}
break;
case SO_KEEPALIVE:
keepalive = value instanceof Boolean ? ((Boolean)value).booleanValue() : true;
setKeepAlive(keepalive);
break;
case SO_LINGER:
if(value instanceof Boolean && !((Boolean)value).booleanValue()) {
linger = -1;
setLinger(-1);
}
else if(value instanceof Integer){
linger = ((Integer)value).intValue();
setLinger(linger);
}
break;
case SO_OOBINLINE:
oob = value instanceof Boolean ? ((Boolean)value).booleanValue() : true;
setOOBInline(oob);
break;
case TCP_NODELAY:
nodelay = value instanceof Boolean ? ((Boolean)value).booleanValue() : true;
setNoDelay(nodelay);
break;
case IP_TOS:
if(value instanceof Integer){
setIpTos(((Integer)value).intValue());
}
break;
default:
PlainDatagramSocketImpl.options(opt, value, getSocket());
}
}
protected void connect(String host, int port) throws IOException {
connect(InetAddress.getByName(host),port);
}
protected synchronized InputStream getInputStream() throws IOException {
if (!open) {
throw new IOException("SocketImpl has been closed");
}
if (in == null) {
in = new SocketInputStream(this);
}
return in;
}
protected synchronized OutputStream getOutputStream() throws IOException {
if (!open) {
throw new IOException("SocketImpl has been closed");
}
if (out == null) {
out = new SocketOutputStream(this);
}
return out;
}
protected synchronized void create(boolean stream) throws IOException {
if (stream == false) {
throw new IOException("datagram services not supported");
}
fd = new FileDescriptor();
}
protected synchronized void accept(SocketImpl s) throws IOException {
try {
SocketUsers.put(this, Thread.currentThread());
int ip = nativeAccept(s);
s.address = InetAddress.createInetAddress(ip);
}
catch (ClassCastException cce) {
}
finally {
SocketUsers.remove(this);
}
}
protected synchronized void shutdownInput() throws IOException {
shutdown(true);
}
protected synchronized void shutdownOutput() throws IOException {
shutdown(false);
}
protected synchronized void connect(InetAddress address, int port) throws IOException {
this.address = address;
this.port = port;
if (address instanceof Inet6Address) {
ipv6 = true;
}
try {
SocketUsers.put(this, Thread.currentThread());
nativeCreate();
nativeBind();
nativeConnect(0);
register();
}
finally {
SocketUsers.remove(this);
}
}
protected synchronized void connect(SocketAddress sa, int timeout) throws IOException {
try {
SocketUsers.put(this, Thread.currentThread());
InetSocketAddress isa = (InetSocketAddress)sa;
this.address = isa.addr;
this.port = isa.port;
if (address instanceof Inet6Address) {
ipv6 = true;
}
nativeCreate();
nativeBind();
nativeConnect(timeout);
}
catch (ClassCastException cce) {
throw new SocketException();
}
finally {
SocketUsers.remove(this);
}
}
protected synchronized void bind(InetAddress host, int port) throws IOException {
localAddress = host;
localport = port;
ipv6 = host instanceof Inet6Address;
}
protected synchronized void listen(int backlog) throws IOException {
nativeCreate();
nativeBind();
nativeListen(backlog);
register();
}
protected void close() throws IOException {
Thread t = SocketUsers.get(this);
_close();
if (t != null) {
signal(t);
}
}
private void register() {
if (wr == null) {
wr = new WeakReference(this);
synchronized (opensockets) {
opensockets.add(wr);
if (++regcount >= 100) {
// Clean up stale references
regcount = 0;
Iterator iter = opensockets.iterator();
while (iter.hasNext()) {
WeakReference wr0 = (WeakReference)iter.next();
if (wr0.get() == null) {
iter.remove();
}
}
}
}
}
}
private void deregister() {
if (wr != null) {
synchronized (opensockets) {
opensockets.remove(wr);
}
}
}
//package private methods for communication with SocketStreams ...
int read(byte [] bytes, int off, int length) throws IOException {
try {
SocketUsers.put(this, Thread.currentThread());
return _read(bytes, off, length);
}
finally {
SocketUsers.remove(this);
}
}
native int _read(byte [] bytes, int off, int length) throws IOException;
native void write(byte [] bytes, int off, int length) throws IOException;
protected native void finalize();
//private native methods
private native void signal(Thread t);
private native int getSocket() throws SocketException;
private native void nativeCreate();
private native int nativeAccept(SocketImpl s) throws IOException;
private native void shutdown(boolean in) throws IOException;
private native void setKeepAlive(boolean on) throws SocketException;
private native void setNoDelay(boolean on) throws SocketException;
private native void setSoTimeout(int millis) throws SocketException;
private native int getRcvBuf() throws SocketException;
private native void setRcvBuf(int size) throws SocketException;
private native int getSndBuf() throws SocketException;
private native void setSndBuf(int size) throws SocketException;
private native void setLinger(int secs) throws SocketException;
private native void setOOBInline(boolean on) throws SocketException;
private native void setReuseAddr(boolean on) throws SocketException;
private native int getIpTos() throws SocketException;
private native void setIpTos(int tos) throws SocketException;
private native int getLocal4Address() throws SocketException;
private native void getLocal6Address(byte[] output) throws SocketException;
protected synchronized native void nativeConnect(int timeout) throws IOException;
protected synchronized native void nativeBind() throws IOException;
protected synchronized native void nativeListen(int backlog) throws IOException;
//straight calls to native code ...
protected native void sendUrgentData(int udata) throws IOException;
protected synchronized native int available() throws IOException;
protected native void _close() throws IOException;
/*
TCP_NODELAY = 0x0001; --> Boolean
IP_TOS = 0x0003; --> Integer
SO_LINGER = 0x0080; --> Boolean
SO_BINDADDR = 0x000F; --> InetAddress
SO_REUSEADDR = 0x04; --> Integer
SO_BROADCAST = 0x0020; --> Boolean
IP_MULTICAST_IF = 0x10; --> InetAddress
IP_MULTICAST_IF2 = 0x1F; --> InetAddress
IP_MULTICAST_LOOP = 0x0012;> Boolean
SO_TIMEOUT = 0x1006; --> Integer
SO_SNDBUF =0x1001; --> Integer
SO_RCVBUF = 0x1002; --> Integer
SO_OOBINLINE = 0x1003; --> Boolean
SO_KEEPALIVE = 0x0008; --> Boolean
*/
}