AFSelectorProvider.java
/*
* junixsocket
*
* Copyright 2009-2024 Christian Kohlschütter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.newsclub.net.unix;
import java.io.IOException;
import java.net.ProtocolFamily;
import java.net.SocketAddress;
import java.net.StandardProtocolFamily;
import java.nio.channels.DatagramChannel;
import java.nio.channels.Pipe;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.util.Objects;
import org.eclipse.jdt.annotation.NonNull;
/**
* Service-provider class for junixsocket selectors and selectable channels.
*
* @param <A> The concrete {@link AFSocketAddress} that is supported by this type.
*/
public abstract class AFSelectorProvider<A extends AFSocketAddress> extends SelectorProviderShim {
private static final SelectorProvider AF_PROVIDER = new SelectorProviderShim() {
@Override
public SocketChannel openSocketChannel() throws IOException {
throw new UnsupportedOperationException(
"Use openSocketChannel(ProtocolFamily) or a specific AFSelectorProvider subclass");
}
@Override
public ServerSocketChannel openServerSocketChannel() throws IOException {
throw new UnsupportedOperationException(
"Use openServerSocketChannel(ProtocolFamily) or a specific AFSelectorProvider subclass");
}
@Override
public DatagramChannel openDatagramChannel() throws IOException {
throw new UnsupportedOperationException(
"Use openDatagramChannel(ProtocolFamily) or a specific AFSelectorProvider subclass");
}
@Override
public AbstractSelector openSelector() throws IOException {
throw new UnsupportedOperationException("Use a specific AFSelectorProvider subclass");
}
@Override
public Pipe openPipe() throws IOException {
throw new UnsupportedOperationException("Use a specific AFSelectorProvider subclass");
}
@Override
public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
if (family instanceof AFProtocolFamily) {
return ((AFProtocolFamily) family).openDatagramChannel();
} else {
throw new UnsupportedOperationException("Unsupported protocol family");
}
}
@Override
public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
if (family instanceof AFProtocolFamily) {
return ((AFProtocolFamily) family).openSocketChannel();
} else {
throw new UnsupportedOperationException("Unsupported protocol family");
}
}
@Override
public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
if (family instanceof AFProtocolFamily) {
return ((AFProtocolFamily) family).openServerSocketChannel();
} else {
throw new UnsupportedOperationException("Unsupported protocol family");
}
}
};
/**
* Constructs a new {@link AFSelectorProvider}.
*/
protected AFSelectorProvider() {
super();
}
/**
* Constructs a new pipe.
*
* @param selectable {@code true} if the pipe should be selectable.
* @return The pipe.
* @throws IOException on error.
*/
private AFPipe newPipe(boolean selectable) throws IOException {
return new AFPipe(this, selectable);
}
/**
* Constructs a new socket pair from two sockets.
*
* @param <Y> The type of the pair.
* @param s1 Some socket, the first one.
* @param s2 Some socket, the second one.
* @return The pair.
*/
protected abstract <Y extends AFSomeSocket> AFSocketPair<Y> newSocketPair(Y s1, Y s2);
/**
* Constructs a new socket.
*
* @return The socket instance.
* @throws IOException on error.
*/
protected abstract AFSocket<A> newSocket() throws IOException;
/**
* Returns the protocol family supported by this implementation.
*
* @return The protocol family.
*/
protected abstract ProtocolFamily protocolFamily();
/**
* Returns the address family supported by this implementation.
*
* @return The address family.
*/
protected abstract AFAddressFamily<@NonNull A> addressFamily();
/**
* Returns the domain ID for the supported protocol, as specified by {@link NativeUnixSocket}.
*
* @return The domain ID.
*/
protected final int domainId() {
return addressFamily().getDomain();
}
/**
* Opens a socket pair of interconnected channels.
*
* @return The new channel pair.
* @throws IOException on error.
*/
@SuppressWarnings("resource")
public AFSocketPair<? extends AFSocketChannel<A>> openSocketChannelPair() throws IOException {
AFSocketChannel<A> s1 = openSocketChannel();
AFSocketChannel<A> s2 = openSocketChannel();
NativeUnixSocket.socketPair(domainId(), NativeUnixSocket.SOCK_STREAM, s1.getAFCore().fd, s2
.getAFCore().fd);
s1.socket().internalDummyConnect();
s2.socket().internalDummyConnect();
return newSocketPair(s1, s2);
}
/**
* Opens a socket pair of interconnected datagram channels.
*
* @return The new channel pair.
* @throws IOException on error.
*/
public AFSocketPair<? extends AFDatagramChannel<A>> openDatagramChannelPair() throws IOException {
return openDatagramChannelPair(AFSocketType.SOCK_DGRAM);
}
/**
* Opens a socket pair of interconnected {@link DatagramChannel}s, using the given
* {@link AFSocketType}.
*
* @param type The socket type.
* @return The new channel pair.
* @throws IOException on error.
*/
@SuppressWarnings("resource")
public AFSocketPair<? extends AFDatagramChannel<A>> openDatagramChannelPair(AFSocketType type)
throws IOException {
ProtocolFamily pf = protocolFamily();
AFDatagramChannel<A> s1 = openDatagramChannel(pf);
AFDatagramChannel<A> s2 = openDatagramChannel(pf);
NativeUnixSocket.socketPair(domainId(), type.getId(), s1.getAFCore().fd, s2.getAFCore().fd);
s1.socket().internalDummyBind();
s2.socket().internalDummyBind();
s1.socket().internalDummyConnect();
s2.socket().internalDummyConnect();
return newSocketPair(s1, s2);
}
@Override
public abstract AFDatagramChannel<A> openDatagramChannel() throws IOException;
/**
* Opens a {@link DatagramChannel} using the given socket type.
*
* @param type The socket type.
* @return the new channel
* @throws IOException on error.
*/
public abstract AFDatagramChannel<A> openDatagramChannel(AFSocketType type) throws IOException;
@Override
public final AFPipe openPipe() throws IOException {
return newPipe(false);
}
/**
* Opens a pipe with support for selectors.
*
* @return The new pipe
* @throws IOException on error.
*/
final AFPipe openSelectablePipe() throws IOException {
return newPipe(true);
}
@Override
public final AbstractSelector openSelector() throws IOException {
return new AFSelector(this);
}
@Override
public abstract AFServerSocketChannel<A> openServerSocketChannel() throws IOException;
/**
* Opens a server-socket channel bound on the given {@link SocketAddress}.
*
* @param sa The socket address to bind on.
* @return The new channel
* @throws IOException on error.
*/
public abstract AFServerSocketChannel<A> openServerSocketChannel(SocketAddress sa)
throws IOException;
@Override
public AFSocketChannel<A> openSocketChannel() throws IOException {
return newSocket().getChannel();
}
/**
* Opens a socket channel connected to the given {@link SocketAddress}.
*
* @param sa The socket address to connect to.
* @return The new channel
* @throws IOException on error.
*/
public abstract AFSocketChannel<A> openSocketChannel(SocketAddress sa) throws IOException;
@Override
public AFSocketChannel<A> openSocketChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
// Workaround for StandardProtocolFamily.UNIX check on older Java versions
if (protocolFamily().equals(family) || (family instanceof StandardProtocolFamily
&& protocolFamily().name().equals(family.name()))) {
return openSocketChannel();
}
throw new UnsupportedOperationException("Protocol family not supported");
}
@Override
public AFServerSocketChannel<A> openServerSocketChannel(ProtocolFamily family)
throws IOException {
Objects.requireNonNull(family);
// Workaround for StandardProtocolFamily.UNIX check on older Java versions
if (protocolFamily().equals(family) || (family instanceof StandardProtocolFamily
&& protocolFamily().name().equals(family.name()))) {
return openServerSocketChannel();
}
throw new UnsupportedOperationException("Protocol family not supported");
}
@Override
public AFDatagramChannel<A> openDatagramChannel(ProtocolFamily family) throws IOException {
Objects.requireNonNull(family);
// Workaround for StandardProtocolFamily.UNIX check on older Java versions
if (protocolFamily().equals(family) || (family instanceof StandardProtocolFamily
&& protocolFamily().name().equals(family.name()))) {
return openDatagramChannel();
}
throw new UnsupportedOperationException("Protocol family not supported");
}
/**
* Returns the singleton instance for an "fallback" provider that supports the methods taking
* {@link ProtocolFamily}, as long as a junixsocket-specific {@link AFProtocolFamily} is used.
*
* @return The instance.
*/
public static SelectorProvider provider() {
return AF_PROVIDER;
}
}