/**
* Copyright 2014 Ricardo Padilha
*
* 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 net.dsys.snio.impl.group;
import java.io.IOException;
import java.net.SocketAddress;
import java.net.SocketOption;
import java.nio.channels.NetworkChannel;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import net.dsys.commons.api.future.CallbackFuture;
import net.dsys.commons.api.lang.Copier;
import net.dsys.commons.impl.future.MergingCallbackFuture;
import net.dsys.snio.api.buffer.MessageBufferConsumer;
import net.dsys.snio.api.buffer.MessageBufferProducer;
import net.dsys.snio.api.channel.CloseListener;
import net.dsys.snio.api.channel.MessageChannel;
import net.dsys.snio.api.group.GroupSocketAddress;
/**
* @author Ricardo Padilha
*/
final class GroupChannel<T> implements MessageChannel<T> {
@Nonnull
private final MessageBufferConsumer<T> in;
@Nonnull
private final ChannelFactory<T> factory;
@Nonnull
private final Copier<T> copier;
private MessageChannel<T>[] channels;
private MessageBufferProducer<T> out;
private SocketAddress local;
private CallbackFuture<Void> bindFuture;
private CallbackFuture<Void> connectFuture;
private CallbackFuture<Void> closeFuture;
GroupChannel(@Nonnull final MessageBufferConsumer<T> in, @Nonnull final ChannelFactory<T> factory,
@Nonnull final Copier<T> copier) {
if (in == null) {
throw new NullPointerException("in == null");
}
if (factory == null) {
throw new NullPointerException("factory == null");
}
if (copier == null) {
throw new NullPointerException("copier == null");
}
this.in = in;
this.factory = factory;
this.copier = copier;
}
void open(@Nonnegative final int size) throws IOException {
if (channels != null) {
return;
}
@SuppressWarnings("unchecked")
final MessageChannel<T>[] channels = new MessageChannel[size];
for (int i = 0; i < size; i++) {
channels[i] = factory.open();
}
this.channels = channels;
}
/**
* {@inheritDoc}
*/
@Override
public MessageChannel<T> onClose(final CloseListener<T> listener) {
if (listener == null) {
throw new NullPointerException("listener == null");
}
for (final MessageChannel<T> channel : channels) {
channel.onClose(listener);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOpen() {
if (channels == null) {
return false;
}
boolean open = true;
for (final MessageChannel<T> channel : channels) {
open &= channel.isOpen();
}
return open;
}
/**
* {@inheritDoc}
*/
@Override
public GroupChannel<T> bind(final SocketAddress local) throws IOException {
if (!(local instanceof GroupSocketAddress)) {
throw new IllegalArgumentException("GroupChannel can only bind to a GroupSocketAddress");
}
final GroupSocketAddress group = (GroupSocketAddress) local;
if (group.size() != channels.length) {
throw new IllegalArgumentException("local.size() != channels.length");
}
final int k = channels.length;
for (int i = 0; i < k; i++) {
channels[i].bind(group.get(i));
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public NetworkChannel bind(final SocketAddress local, final int backlog) throws IOException {
if (!(local instanceof GroupSocketAddress)) {
throw new IllegalArgumentException("GroupChannel can only bind to a GroupSocketAddress");
}
final GroupSocketAddress group = (GroupSocketAddress) local;
if (group.size() != channels.length) {
throw new IllegalArgumentException("local.size() != channels.length");
}
final int k = channels.length;
for (int i = 0; i < k; i++) {
channels[i].bind(group.get(i), backlog);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public CallbackFuture<Void> getBindFuture() {
if (bindFuture == null) {
final MergingCallbackFuture.Builder<Void> builder = MergingCallbackFuture.builder();
for (final MessageChannel<T> channel : channels) {
builder.add(channel.getBindFuture());
}
this.bindFuture = builder.build();
}
return bindFuture;
}
/**
* {@inheritDoc}
*/
@Override
public SocketAddress getLocalAddress() throws IOException {
if (local == null) {
final GroupSocketAddress.Builder builder = GroupSocketAddress.build();
for (final MessageChannel<T> channel : channels) {
builder.add(channel.getLocalAddress());
}
local = builder.build();
}
return local;
}
/**
* {@inheritDoc}
*/
@Override
public void connect(final SocketAddress remote) throws IOException {
if (!(remote instanceof GroupSocketAddress)) {
throw new IllegalArgumentException("!(remote instanceof GroupSocketAddress)");
}
final GroupSocketAddress group = (GroupSocketAddress) remote;
if (group.size() != channels.length) {
throw new IllegalArgumentException("remote.size() != channels.length");
}
final int k = channels.length;
for (int i = 0; i < k; i++) {
channels[i].connect(group.get(i));
}
}
/**
* {@inheritDoc}
*/
@Override
public CallbackFuture<Void> getConnectFuture() {
if (connectFuture == null) {
final MergingCallbackFuture.Builder<Void> builder = MergingCallbackFuture.builder();
for (final MessageChannel<T> channel : channels) {
builder.add(channel.getConnectFuture());
}
this.connectFuture = builder.build();
}
return connectFuture;
}
/**
* {@inheritDoc}
*/
@Override
public MessageBufferConsumer<T> getInputBuffer() {
return in;
}
/**
* {@inheritDoc}
*/
@Override
public MessageBufferProducer<T> getOutputBuffer() {
if (out == null) {
final GroupMessageBufferProducer.Builder<T> builder = GroupMessageBufferProducer.build();
builder.setCopier(copier);
for (final MessageChannel<T> channel : channels) {
builder.add(channel.getOutputBuffer());
}
out = builder.build();
}
return out;
}
/**
* {@inheritDoc}
*/
@Override
public void close() throws IOException {
if (channels == null) {
return;
}
for (final MessageChannel<T> channel : channels) {
channel.close();
}
}
/**
* {@inheritDoc}
*/
@Override
public CallbackFuture<Void> getCloseFuture() {
if (closeFuture == null) {
final MergingCallbackFuture.Builder<Void> builder = MergingCallbackFuture.builder();
for (final MessageChannel<T> channel : channels) {
builder.add(channel.getCloseFuture());
}
this.closeFuture = builder.build();
}
return closeFuture;
}
/**
* {@inheritDoc}
*/
@Override
public Set<SocketOption<?>> supportedOptions() {
final Set<SocketOption<?>> options = new HashSet<>();
for (final MessageChannel<T> channel : channels) {
options.addAll(channel.supportedOptions());
}
return options;
}
/**
* {@inheritDoc}
*/
@Override
public <E> GroupChannel<T> setOption(final SocketOption<E> name, final E value) throws IOException {
for (final MessageChannel<T> channel : channels) {
channel.setOption(name, value);
}
return this;
}
/**
* {@inheritDoc}
*/
@Override
public <E> E getOption(final SocketOption<E> name) throws IOException {
for (final MessageChannel<T> channel : channels) {
final E value = channel.getOption(name);
if (value != null) {
return value;
}
}
return null;
}
}