/* * 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(); } } }