/*
* Copyright © 2015 Cask Data, Inc.
*
* 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 co.cask.cdap.internal.app.runtime.service.http;
import co.cask.cdap.api.Transactional;
import co.cask.cdap.api.TxRunnable;
import co.cask.cdap.api.data.DatasetContext;
import co.cask.cdap.api.service.http.HttpContentProducer;
import co.cask.cdap.common.lang.ClassLoaders;
import co.cask.http.BodyProducer;
import org.apache.twill.common.Cancellable;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
/**
* An adapter class to delegate calls from {@link HttpContentProducer} to {@link BodyProducer}
*/
public class BodyProducerAdapter extends BodyProducer {
private static final Logger LOG = LoggerFactory.getLogger(BodyProducerAdapter.class);
private final HttpContentProducer delegate;
private final Transactional transactional;
private final ClassLoader programContextClassloader;
private final TransactionalHttpServiceContext serviceContext;
private final Cancellable contextReleaser;
private boolean completed;
public BodyProducerAdapter(HttpContentProducer delegate, Transactional transactional,
ClassLoader programContextClassLoader,
TransactionalHttpServiceContext serviceContext, Cancellable contextReleaser) {
this.delegate = delegate;
this.transactional = transactional;
this.programContextClassloader = programContextClassLoader;
this.serviceContext = serviceContext;
this.contextReleaser = contextReleaser;
}
@Override
public long getContentLength() {
ClassLoader oldClassLoader = ClassLoaders.setContextClassLoader(programContextClassloader);
try {
return delegate.getContentLength();
} finally {
ClassLoaders.setContextClassLoader(oldClassLoader);
}
}
@Override
public ChannelBuffer nextChunk() throws Exception {
ByteBuffer buffer;
ClassLoader oldClassLoader = ClassLoaders.setContextClassLoader(programContextClassloader);
try {
buffer = delegate.nextChunk(transactional);
} finally {
ClassLoaders.setContextClassLoader(oldClassLoader);
}
return ChannelBuffers.wrappedBuffer(buffer);
}
@Override
public void finished() throws Exception {
transactional.execute(new TxRunnable() {
@Override
public void run(DatasetContext context) throws Exception {
delegate.onFinish();
}
});
try {
serviceContext.dismissTransactionContext();
} finally {
completed = true;
contextReleaser.cancel();
}
}
@Override
public void handleError(final Throwable throwable) {
if (completed) {
return;
}
// To the HttpContentProducer, if there is error, no other methods will be triggered
completed = true;
try {
transactional.execute(new TxRunnable() {
@Override
public void run(DatasetContext context) throws Exception {
delegate.onError(throwable);
}
});
} catch (Throwable t) {
throwable.addSuppressed(t);
// nothing much can be done. Simply emit a debug log.
LOG.debug("Exception in calling HttpContentProducer.onError.", t);
}
try {
serviceContext.dismissTransactionContext();
} finally {
contextReleaser.cancel();
}
}
}