package com.linkedin.databus.client.netty; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * 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. * */ import org.apache.log4j.Logger; import org.jboss.netty.handler.codec.http.HttpChunk; import org.jboss.netty.handler.codec.http.HttpChunkTrailer; import org.jboss.netty.handler.codec.http.HttpResponse; public abstract class AbstractHttpResponseProcessorDecorator<T extends HttpResponseProcessor> implements HttpResponseProcessor { public static final String MODULE = AbstractHttpResponseProcessorDecorator.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); protected T _decorated; /* * Sometimes, Netty seems to call both ChannelException and ChannelClosed for the same issue resulting * in multiple messages to Relay/Bootstrap Pull Thread causing bad state in its state-machine */ protected volatile boolean _errorHandled = false; public static enum ResponseStatus { WAITING_FOR_FIRST_CHUNK, CHUNKS_SEEN, CHUNKS_FINISHED, CHUNKS_EXCEPTION; public boolean isActivelyProcessing() { return (this.equals(WAITING_FOR_FIRST_CHUNK) || this.equals(CHUNKS_SEEN)); } }; protected volatile ResponseStatus _responseStatus; public AbstractHttpResponseProcessorDecorator(T decorated) { super(); _decorated = decorated; _responseStatus = ResponseStatus.WAITING_FOR_FIRST_CHUNK; } @Override public void addChunk(HttpChunk chunk) throws Exception { if (null != _decorated) { _decorated.addChunk(chunk); } else { LOG.error("addChunk ignored; no decorated object"); } } @Override public void addTrailer(HttpChunkTrailer trailer) throws Exception { if (null != _decorated) { _decorated.addTrailer(trailer); } else { LOG.error("addTrailer ignored; no decorated object"); } } @Override public void finishResponse() throws Exception { _responseStatus = ResponseStatus.CHUNKS_FINISHED; if (null != _decorated) { _decorated.finishResponse(); } else { LOG.error("finishResponse ignored; no decorated object"); } } @Override public void startResponse(HttpResponse response) throws Exception { _responseStatus = ResponseStatus.CHUNKS_SEEN; if (null != _decorated) { _decorated.startResponse(response); } else { LOG.error("startResponse ignored; no decorated object"); } } @Override public final void channelException(Throwable cause) { if ( _errorHandled ) { if (LOG.isDebugEnabled()) { LOG.debug("skipping exception as it is already handled by the client: " + cause.getMessage(), cause); } return; } handleChannelException(cause); _errorHandled = true; _responseStatus = ResponseStatus.CHUNKS_EXCEPTION; } public void handleChannelException(Throwable cause) { if (null != _decorated) { _decorated.channelException(cause); } else { if (LOG.isDebugEnabled()) { LOG.debug("channel exception but no decorated object:" + cause.getMessage(), cause); } } } }