/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.processors.hadoop.taskexecutor.external.communication;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.util.ipc.IpcEndpoint;
import org.apache.ignite.internal.util.nio.GridNioFilter;
import org.apache.ignite.internal.util.nio.GridNioFilterAdapter;
import org.apache.ignite.internal.util.nio.GridNioFilterChain;
import org.apache.ignite.internal.util.nio.GridNioFinishedFuture;
import org.apache.ignite.internal.util.nio.GridNioFuture;
import org.apache.ignite.internal.util.nio.GridNioServerListener;
import org.apache.ignite.internal.util.nio.GridNioSession;
import org.apache.ignite.internal.util.nio.GridNioSessionImpl;
import org.apache.ignite.lang.IgniteInClosure;
/**
* Allows to re-use existing {@link GridNioFilter}s on IPC (specifically shared memory IPC)
* communications.
*
* Note that this class consumes an entire thread inside {@link #serve()} method
* in order to serve one {@link org.apache.ignite.internal.util.ipc.IpcEndpoint}.
*/
public class HadoopIpcToNioAdapter<T> {
/** */
private final IpcEndpoint endp;
/** */
private final GridNioFilterChain<T> chain;
/** */
private final GridNioSessionImpl ses;
/** */
private final AtomicReference<CountDownLatch> latchRef = new AtomicReference<>();
/** */
private final ByteBuffer writeBuf;
/**
* @param log Log.
* @param endp Endpoint.
* @param lsnr Listener.
* @param filters Filters.
*/
public HadoopIpcToNioAdapter(IgniteLogger log, IpcEndpoint endp, boolean accepted,
GridNioServerListener<T> lsnr, GridNioFilter... filters) {
this.endp = endp;
chain = new GridNioFilterChain<>(log, lsnr, new HeadFilter(), filters);
ses = new GridNioSessionImpl(chain, null, null, accepted);
writeBuf = ByteBuffer.allocate(8 << 10);
writeBuf.order(ByteOrder.nativeOrder());
}
/**
* Serves given set of listeners repeatedly reading data from the endpoint.
*
* @throws InterruptedException If interrupted.
*/
public void serve() throws InterruptedException {
try {
chain.onSessionOpened(ses);
InputStream in = endp.inputStream();
ByteBuffer readBuf = ByteBuffer.allocate(8 << 10);
readBuf.order(ByteOrder.nativeOrder());
assert readBuf.hasArray();
while (!Thread.interrupted()) {
int pos = readBuf.position();
int read = in.read(readBuf.array(), pos, readBuf.remaining());
if (read > 0) {
readBuf.position(0);
readBuf.limit(pos + read);
chain.onMessageReceived(ses, readBuf);
if (readBuf.hasRemaining())
readBuf.compact();
else
readBuf.clear();
CountDownLatch latch = latchRef.get();
if (latch != null)
latch.await();
}
else if (read < 0) {
endp.close();
break; // And close below.
}
}
// Assuming remote end closed connection - pushing event from head to tail.
chain.onSessionClosed(ses);
}
catch (Exception e) {
chain.onExceptionCaught(ses, new IgniteCheckedException("Failed to read from IPC endpoint.", e));
}
}
/**
* Gets dummy session for this adapter.
*
* @return Session.
*/
public GridNioSession session() {
return ses;
}
/**
* Handles write events on chain.
*
* @param msg Buffer to send.
* @return Send result.
*/
private GridNioFuture<?> send(ByteBuffer msg) {
assert writeBuf.hasArray();
try {
while (msg.hasRemaining()) {
writeBuf.clear();
writeBuf.put(msg);
endp.outputStream().write(writeBuf.array(), 0, writeBuf.position());
}
}
catch (IOException | IgniteCheckedException e) {
return new GridNioFinishedFuture<Object>(e);
}
return new GridNioFinishedFuture<>((Object)null);
}
/**
* Filter forwarding messages from chain's head to this server.
*/
private class HeadFilter extends GridNioFilterAdapter {
/**
* Assigns filter name.
*/
protected HeadFilter() {
super("HeadFilter");
}
/** {@inheritDoc} */
@Override public void onSessionOpened(GridNioSession ses) throws IgniteCheckedException {
proceedSessionOpened(ses);
}
/** {@inheritDoc} */
@Override public void onSessionClosed(GridNioSession ses) throws IgniteCheckedException {
proceedSessionClosed(ses);
}
/** {@inheritDoc} */
@Override public void onExceptionCaught(GridNioSession ses, IgniteCheckedException ex) throws IgniteCheckedException {
proceedExceptionCaught(ses, ex);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onSessionWrite(GridNioSession ses,
Object msg,
boolean fut,
IgniteInClosure<IgniteException> ackC) {
assert ses == HadoopIpcToNioAdapter.this.ses : "ses=" + ses +
", this.ses=" + HadoopIpcToNioAdapter.this.ses;
return send((ByteBuffer)msg);
}
/** {@inheritDoc} */
@Override public void onMessageReceived(GridNioSession ses, Object msg) throws IgniteCheckedException {
proceedMessageReceived(ses, msg);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onPauseReads(GridNioSession ses) throws IgniteCheckedException {
// This call should be synced externally to avoid races.
boolean b = latchRef.compareAndSet(null, new CountDownLatch(1));
assert b;
return new GridNioFinishedFuture<>(b);
}
/** {@inheritDoc} */
@Override public GridNioFuture<?> onResumeReads(GridNioSession ses) throws IgniteCheckedException {
// This call should be synced externally to avoid races.
CountDownLatch latch = latchRef.getAndSet(null);
if (latch != null)
latch.countDown();
return new GridNioFinishedFuture<Object>(latch != null);
}
/** {@inheritDoc} */
@Override public GridNioFuture<Boolean> onSessionClose(GridNioSession ses) {
assert ses == HadoopIpcToNioAdapter.this.ses;
boolean closed = HadoopIpcToNioAdapter.this.ses.setClosed();
if (closed)
endp.close();
return new GridNioFinishedFuture<>(closed);
}
/** {@inheritDoc} */
@Override public void onSessionIdleTimeout(GridNioSession ses) throws IgniteCheckedException {
proceedSessionIdleTimeout(ses);
}
/** {@inheritDoc} */
@Override public void onSessionWriteTimeout(GridNioSession ses) throws IgniteCheckedException {
proceedSessionWriteTimeout(ses);
}
}
}