package org.playorm.nio.impl.cm.basic.udp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.PortUnreachableException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectableChannel;
import java.util.Calendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.playorm.nio.api.channels.NioException;
import org.playorm.nio.api.channels.UDPChannel;
import org.playorm.nio.api.handlers.FutureOperation;
import org.playorm.nio.api.handlers.OperationCallback;
import org.playorm.nio.api.libs.BufferFactory;
import org.playorm.nio.impl.cm.basic.BasChannelImpl;
import org.playorm.nio.impl.cm.basic.FutureConnectImpl;
import org.playorm.nio.impl.cm.basic.IdObject;
import org.playorm.nio.impl.cm.basic.SelectorManager2;
public class UDPChannelImpl extends BasChannelImpl implements UDPChannel {
private static final Logger log = Logger.getLogger(UDPChannel.class.getName());
private static final Logger apiLog = Logger.getLogger(UDPChannel.class.getName());
private DatagramChannel channel;
private boolean isConnected = false;
private Calendar expires;
public UDPChannelImpl(IdObject id, BufferFactory factory, SelectorManager2 selMgr) throws IOException {
super(id, factory, selMgr);
channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.socket().setReuseAddress(true);
}
public void bindImpl2(SocketAddress addr) throws IOException {
channel.socket().bind(addr);
}
@Override
public FutureOperation connect(SocketAddress addr) {
try {
return connectImpl(addr);
} catch (IOException e) {
throw new NioException(e);
} catch (InterruptedException e) {
throw new NioException(e);
}
}
private synchronized FutureOperation connectImpl(SocketAddress addr) throws IOException, InterruptedException {
FutureConnectImpl future = new FutureConnectImpl();
try {
if(apiLog.isLoggable(Level.FINE))
apiLog.fine(this+"Basic.connect called-addr="+addr);
channel.connect(addr);
isConnected = true;
future.connected(this);
} catch(Exception e) {
future.failed(this, e);
}
return future;
}
public synchronized void oldConnect(SocketAddress addr) {
if(apiLog.isLoggable(Level.FINE))
apiLog.fine(this+"Basic.connect called-addr="+addr);
try {
channel.connect(addr);
isConnected = true;
} catch(IOException e) {
throw new NioException(e);
}
}
public synchronized void disconnect() {
if(apiLog.isLoggable(Level.FINE))
apiLog.fine(this+"Basic.disconnect called");
try {
isConnected = false;
channel.disconnect();
} catch(IOException e) {
throw new NioException(e);
}
}
public void setReuseAddress(boolean b) {
throw new UnsupportedOperationException("not implemented yet");
}
public boolean isBlocking() {
return channel.isBlocking();
}
public void closeImpl() throws IOException {
channel.close();
}
public boolean isClosed() {
return channel.socket().isClosed();
}
public boolean isBound() {
return channel.socket().isBound();
}
public InetSocketAddress getLocalAddress() {
InetAddress addr = channel.socket().getLocalAddress();
int port = channel.socket().getLocalPort();
return new InetSocketAddress(addr, port);
}
public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress)channel.socket().getRemoteSocketAddress();
}
public boolean isConnected() {
return channel.isConnected();
}
@Override
public SelectableChannel getRealChannel() {
return channel;
}
@Override
public int readImpl(ByteBuffer b) throws IOException {
if(b == null)
throw new IllegalArgumentException("Cannot use a null ByteBuffer");
else if(!isConnected)
throw new IllegalStateException("Currently not connected");
try {
return channel.read(b);
} catch(PortUnreachableException e) {
if(expires != null) {
//ignore the event if we are not at expires yet
if(Calendar.getInstance().before(expires)) {
return 0;
}
}
expires = Calendar.getInstance();
expires.add(Calendar.SECOND, 10);
log.warning("PortUnreachable. NOTICE NOTICE: We will ignore this exc again on this channel for 10 seconds");
throw e;
}
}
@Override
protected int writeImpl(ByteBuffer b) throws IOException {
return channel.write(b);
}
/**
* @see org.playorm.nio.impl.cm.basic.BasChannelImpl#oldWrite(java.nio.ByteBuffer, org.playorm.nio.api.handlers.OperationCallback)
*/
@Override
public void oldWrite(ByteBuffer b, OperationCallback h) {
if(!isConnected)
throw new IllegalStateException(this+"Channel is not currently connected");
super.oldWrite(b, h);
}
/**
* @see org.playorm.nio.impl.cm.basic.BasChannelImpl#oldWrite(java.nio.ByteBuffer)
*/
@Override
public int oldWrite(ByteBuffer b) {
if(!isConnected)
throw new IllegalStateException(this+"Channel is not currently connected");
return super.oldWrite(b);
}
}