
com.aitusoftware.flute.send.NonBlockingAggregator Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2016 Aitu Software Limited.
*
* 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 com.aitusoftware.flute.send;
import com.aitusoftware.flute.collection.LockFreeCopyOnWriteArray;
import com.aitusoftware.flute.compatibility.Consumer;
import com.aitusoftware.flute.compatibility.Supplier;
import com.aitusoftware.flute.exchanger.Exchanger;
import com.aitusoftware.flute.protocol.Version;
import com.aitusoftware.flute.protocol.VersionCodec;
import com.aitusoftware.flute.send.events.AggregatorEvents;
import com.aitusoftware.flute.util.timing.SystemEpochMillisTimeSupplier;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public final class NonBlockingAggregator implements Runnable
{
private final LockFreeCopyOnWriteArray exchangers =
new LockFreeCopyOnWriteArray();
private final NonBlockingSocketChannelConnector socketChannelConnector;
private final long pollInterval;
private final TimeUnit pollUnit;
private final AggregatorEvents aggregatorEvents;
private final Consumer exchangePollingConsumer = new ExchangePollingConsumer();
private final Consumer pendingDataSenderConsumer = new PendingDataSender();
private final ConnectionAttemptThrottler throttler =
new ConnectionAttemptThrottler(new SystemEpochMillisTimeSupplier(), 250L, 5000L, TimeUnit.SECONDS);
public NonBlockingAggregator(
final Supplier socketChannelSupplier,
final long pollInterval,
final TimeUnit pollUnit,
final AggregatorEvents aggregatorEvents)
{
this.aggregatorEvents = aggregatorEvents;
this.pollInterval = pollInterval;
this.pollUnit = pollUnit;
socketChannelConnector = new NonBlockingSocketChannelConnector(socketChannelSupplier, aggregatorEvents);
}
@Override
public void run()
{
while (!Thread.currentThread().isInterrupted())
{
exchangers.forEach(exchangePollingConsumer);
exchangers.forEach(pendingDataSenderConsumer);
LockSupport.parkNanos(pollUnit.toNanos(pollInterval));
}
}
private final class ExchangePollingConsumer implements Consumer
{
@Override
public void accept(final SocketChannelAndExchanger unit)
{
try
{
unit.exchanger.poll();
if (unit.socketChannel == null)
{
tryConnect(unit);
}
}
catch (RuntimeException e)
{
logExceptionInSendLoop(e);
}
}
}
private final class PendingDataSender implements Consumer
{
@Override
public void accept(final SocketChannelAndExchanger unit)
{
try
{
try
{
if (unit.socketChannel != null)
{
final WritableByteChannel dataSink = unit.socketChannel;
if (unit.needsToSendVersion())
{
unit.writeVersion(dataSink);
}
else
{
unit.sender.send(dataSink);
}
}
}
catch (final IOException e)
{
unit.socketChannel = null;
unit.sender.clear();
reportFailureToSendData(unit, e);
}
}
catch (RuntimeException e)
{
logExceptionInSendLoop(e);
}
}
}
private void reportFailureToSendData(final SocketChannelAndExchanger unit, final IOException e)
{
aggregatorEvents.failedToSendDataForSender(unit.identifier, e);
}
private void logExceptionInSendLoop(final RuntimeException e)
{
aggregatorEvents.exceptionInSendLoop(e);
}
private void tryConnect(final SocketChannelAndExchanger unit)
{
if(throttler.shouldAttemptConnection())
{
final SocketChannel socketChannel = socketChannelConnector.registerSenderWithSocket();
unit.reset(socketChannel);
throttler.connectionSuccessful();
}
}
public void register(final Exchanger exchanger, final AggregatingDataSender sender, final String identifier)
{
exchangers.add(new SocketChannelAndExchanger(exchanger, sender, identifier));
}
private static final class SocketChannelAndExchanger
{
private final Exchanger exchanger;
private final AggregatingDataSender sender;
private final String identifier;
private final ByteBuffer version = VersionCodec.asBuffer(Version.ONE);
private SocketChannel socketChannel;
private SocketChannelAndExchanger(
final Exchanger exchanger,
final AggregatingDataSender sender,
final String identifier)
{
this.exchanger = exchanger;
this.sender = sender;
this.identifier = identifier;
version.clear();
}
private boolean needsToSendVersion()
{
return version.remaining() != 0;
}
private void reset(final SocketChannel socketChannel)
{
this.socketChannel = socketChannel;
version.clear();
}
private void writeVersion(final WritableByteChannel channel) throws IOException
{
channel.write(version);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy