/***********************************************************************************************************************
* Copyright (C) 2010-2014 by the Stratosphere project (http://stratosphere.eu)
*
* 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 eu.stratosphere.runtime.io.channels;
import eu.stratosphere.nephele.event.task.AbstractEvent;
import eu.stratosphere.nephele.event.task.AbstractTaskEvent;
import eu.stratosphere.nephele.jobgraph.JobID;
import eu.stratosphere.runtime.io.Buffer;
import eu.stratosphere.runtime.io.network.Envelope;
import eu.stratosphere.runtime.io.gates.OutputGate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.util.Arrays;
public class OutputChannel extends Channel {
private static final Log LOG = LogFactory.getLog(OutputChannel.class);
private final Object closeLock = new Object();
private final OutputGate outputGate;
private boolean senderCloseRequested;
private boolean receiverCloseRequested;
private int currentSeqNum;
// -----------------------------------------------------------------------------------------------------------------
/**
* Creates a new output channel object.
*
* @param outputGate the output gate this channel is connected to
* @param index the index of the channel in the output gate
* @param id the ID of the channel
* @param connectedId the ID of the channel this channel is connected to
* @param type the type of this channel
*/
public OutputChannel(OutputGate outputGate, int index, ChannelID id, ChannelID connectedId, ChannelType type) {
super(index, id, connectedId, type);
this.outputGate = outputGate;
}
// -----------------------------------------------------------------------------------------------------------------
// Data processing
// -----------------------------------------------------------------------------------------------------------------
public void sendBuffer(Buffer buffer) throws IOException, InterruptedException {
checkStatus();
// discard empty buffers
if (buffer.size() == 0) {
return;
}
Envelope envelope = createNextEnvelope();
envelope.setBuffer(buffer);
this.envelopeDispatcher.dispatchFromOutputChannel(envelope);
}
public void sendEvent(AbstractEvent event) throws IOException, InterruptedException {
checkStatus();
Envelope envelope = createNextEnvelope();
envelope.serializeEventList(Arrays.asList(event));
this.envelopeDispatcher.dispatchFromOutputChannel(envelope);
}
public void sendBufferAndEvent(Buffer buffer, AbstractEvent event) throws IOException, InterruptedException {
checkStatus();
Envelope envelope = createNextEnvelope();
envelope.setBuffer(buffer);
envelope.serializeEventList(Arrays.asList(event));
this.envelopeDispatcher.dispatchFromOutputChannel(envelope);
}
// -----------------------------------------------------------------------------------------------------------------
// Event processing
// -----------------------------------------------------------------------------------------------------------------
@Override
public void queueEnvelope(Envelope envelope) {
if (envelope.hasBuffer()) {
throw new IllegalStateException("Envelope for OutputChannel has Buffer attached.");
}
for (AbstractEvent event : envelope.deserializeEvents()) {
if (event.getClass() == ChannelCloseEvent.class) {
synchronized (this.closeLock) {
this.receiverCloseRequested = true;
this.closeLock.notifyAll();
}
}
else if (event instanceof AbstractTaskEvent) {
if (LOG.isDebugEnabled()) {
LOG.debug("OutputChannel received task event: " + event);
}
this.outputGate.deliverEvent((AbstractTaskEvent) event);
}
else {
throw new RuntimeException("OutputChannel received an event that is neither close nor task event.");
}
}
}
// -----------------------------------------------------------------------------------------------------------------
// Shutdown
// -----------------------------------------------------------------------------------------------------------------
public void requestClose() throws IOException, InterruptedException {
if (this.senderCloseRequested) {
return;
}
this.senderCloseRequested = true;
Envelope envelope = createNextEnvelope();
envelope.serializeEventList(Arrays.asList(new ChannelCloseEvent()));
this.envelopeDispatcher.dispatchFromOutputChannel(envelope);
}
@Override
public boolean isClosed() {
return this.senderCloseRequested && this.receiverCloseRequested;
}
public void waitForChannelToBeClosed() throws InterruptedException {
synchronized (this.closeLock) {
while (!this.receiverCloseRequested) {
this.closeLock.wait(1000);
}
}
}
// -----------------------------------------------------------------------------------------------------------------
@Override
public boolean isInputChannel() {
return false;
}
@Override
public JobID getJobID() {
return this.outputGate.getJobID();
}
private void checkStatus() throws IOException {
if (this.senderCloseRequested) {
throw new IllegalStateException(String.format("Channel %s already requested to be closed", getID()));
}
if (this.receiverCloseRequested) {
throw new ReceiverAlreadyClosedException();
}
}
private Envelope createNextEnvelope() {
return new Envelope(this.currentSeqNum++, getJobID(), getID());
}
@Override
public void transferEvent(AbstractEvent event) throws IOException, InterruptedException {
// TODO remove with pending changes for input side
}
@Override
public void releaseAllResources() {
// nothing to do for buffer oriented runtime => TODO remove with pending changes for input side
}
@Override
public void destroy() {
// nothing to do for buffer oriented runtime => TODO remove with pending changes for input side
}
}