com.sun.mail.util.WriteTimeoutSocket Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.mail.util;
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
import java.util.Collections;
import java.util.Set;
import java.nio.channels.SocketChannel;
import java.lang.reflect.*;
/**
* A special Socket that uses a ScheduledExecutorService to
* implement timeouts for writes. The write timeout is specified
* (in milliseconds) when the WriteTimeoutSocket is created.
*
* @author Bill Shannon
*/
public class WriteTimeoutSocket extends Socket {
// delegate all operations to this socket
private final Socket socket;
// to schedule task to cancel write after timeout
private final ScheduledExecutorService ses;
// the timeout, in milliseconds
private final int timeout;
public WriteTimeoutSocket(Socket socket, int timeout) throws IOException {
this.socket = socket;
// XXX - could share executor with all instances?
this.ses = Executors.newScheduledThreadPool(1);
this.timeout = timeout;
}
public WriteTimeoutSocket(int timeout) throws IOException {
this(new Socket(), timeout);
}
public WriteTimeoutSocket(InetAddress address, int port, int timeout)
throws IOException {
this(timeout);
socket.connect(new InetSocketAddress(address, port));
}
public WriteTimeoutSocket(InetAddress address, int port,
InetAddress localAddress, int localPort, int timeout)
throws IOException {
this(timeout);
socket.bind(new InetSocketAddress(localAddress, localPort));
socket.connect(new InetSocketAddress(address, port));
}
public WriteTimeoutSocket(String host, int port, int timeout)
throws IOException {
this(timeout);
socket.connect(new InetSocketAddress(host, port));
}
public WriteTimeoutSocket(String host, int port,
InetAddress localAddress, int localPort, int timeout)
throws IOException {
this(timeout);
socket.bind(new InetSocketAddress(localAddress, localPort));
socket.connect(new InetSocketAddress(host, port));
}
// override all Socket methods and delegate to underlying Socket
@Override
public void connect(SocketAddress remote) throws IOException {
socket.connect(remote, 0);
}
@Override
public void connect(SocketAddress remote, int timeout) throws IOException {
socket.connect(remote, timeout);
}
@Override
public void bind(SocketAddress local) throws IOException {
socket.bind(local);
}
@Override
public SocketAddress getRemoteSocketAddress() {
return socket.getRemoteSocketAddress();
}
@Override
public SocketAddress getLocalSocketAddress() {
return socket.getLocalSocketAddress();
}
@Override
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
socket.setPerformancePreferences(connectionTime, latency, bandwidth);
}
@Override
public SocketChannel getChannel() {
return socket.getChannel();
}
@Override
public InetAddress getInetAddress() {
return socket.getInetAddress();
}
@Override
public InetAddress getLocalAddress() {
return socket.getLocalAddress();
}
@Override
public int getPort() {
return socket.getPort();
}
@Override
public int getLocalPort() {
return socket.getLocalPort();
}
@Override
public InputStream getInputStream() throws IOException {
return socket.getInputStream();
}
@Override
public synchronized OutputStream getOutputStream() throws IOException {
// wrap the returned stream to implement write timeout
return new TimeoutOutputStream(socket.getOutputStream(), ses, timeout);
}
@Override
public void setTcpNoDelay(boolean on) throws SocketException {
socket.setTcpNoDelay(on);
}
@Override
public boolean getTcpNoDelay() throws SocketException {
return socket.getTcpNoDelay();
}
@Override
public void setSoLinger(boolean on, int linger) throws SocketException {
socket.setSoLinger(on, linger);
}
@Override
public int getSoLinger() throws SocketException {
return socket.getSoLinger();
}
@Override
public void sendUrgentData(int data) throws IOException {
socket.sendUrgentData(data);
}
@Override
public void setOOBInline(boolean on) throws SocketException {
socket.setOOBInline(on);
}
@Override
public boolean getOOBInline() throws SocketException {
return socket.getOOBInline();
}
@Override
public void setSoTimeout(int timeout) throws SocketException {
socket.setSoTimeout(timeout);
}
@Override
public int getSoTimeout() throws SocketException {
return socket.getSoTimeout();
}
@Override
public void setSendBufferSize(int size) throws SocketException {
socket.setSendBufferSize(size);
}
@Override
public int getSendBufferSize() throws SocketException {
return socket.getSendBufferSize();
}
@Override
public void setReceiveBufferSize(int size) throws SocketException {
socket.setReceiveBufferSize(size);
}
@Override
public int getReceiveBufferSize() throws SocketException {
return socket.getReceiveBufferSize();
}
@Override
public void setKeepAlive(boolean on) throws SocketException {
socket.setKeepAlive(on);
}
@Override
public boolean getKeepAlive() throws SocketException {
return socket.getKeepAlive();
}
@Override
public void setTrafficClass(int tc) throws SocketException {
socket.setTrafficClass(tc);
}
@Override
public int getTrafficClass() throws SocketException {
return socket.getTrafficClass();
}
@Override
public void setReuseAddress(boolean on) throws SocketException {
socket.setReuseAddress(on);
}
@Override
public boolean getReuseAddress() throws SocketException {
return socket.getReuseAddress();
}
@Override
public void close() throws IOException {
try {
socket.close();
} finally {
ses.shutdownNow();
}
}
@Override
public void shutdownInput() throws IOException {
socket.shutdownInput();
}
@Override
public void shutdownOutput() throws IOException {
socket.shutdownOutput();
}
@Override
public String toString() {
return socket.toString();
}
@Override
public boolean isConnected() {
return socket.isConnected();
}
@Override
public boolean isBound() {
return socket.isBound();
}
@Override
public boolean isClosed() {
return socket.isClosed();
}
@Override
public boolean isInputShutdown() {
return socket.isInputShutdown();
}
@Override
public boolean isOutputShutdown() {
return socket.isOutputShutdown();
}
/*
* The following three methods were added to java.net.Socket in Java SE 9.
* Since they're not supported on Android, and since we know that we
* never use them in JavaMail, we just stub them out here.
*/
//@Override
public Socket setOption(SocketOption so, T val) throws IOException {
// socket.setOption(so, val);
// return this;
throw new UnsupportedOperationException("WriteTimeoutSocket.setOption");
}
//@Override
public T getOption(SocketOption so) throws IOException {
// return socket.getOption(so);
throw new UnsupportedOperationException("WriteTimeoutSocket.getOption");
}
//@Override
public Set> supportedOptions() {
// return socket.supportedOptions();
return Collections.emptySet();
}
/**
* KLUDGE for Android, which has this illegal non-Java Compatible method.
*
* @return the FileDescriptor object
*/
public FileDescriptor getFileDescriptor$() {
try {
Method m = Socket.class.getDeclaredMethod("getFileDescriptor$");
return (FileDescriptor)m.invoke(socket);
} catch (Exception ex) {
return null;
}
}
}
/**
* An OutputStream that wraps the Socket's OutputStream and uses
* the ScheduledExecutorService to schedule a task to close the
* socket (aborting the write) if the timeout expires.
*/
class TimeoutOutputStream extends OutputStream {
private final OutputStream os;
private final ScheduledExecutorService ses;
private final Callable