001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.nio.protocol;
028
029import java.io.IOException;
030import java.util.concurrent.atomic.AtomicBoolean;
031
032import org.apache.http.HttpEntity;
033import org.apache.http.HttpEntityEnclosingRequest;
034import org.apache.http.HttpException;
035import org.apache.http.HttpRequest;
036import org.apache.http.entity.ContentType;
037import org.apache.http.nio.ContentDecoder;
038import org.apache.http.nio.IOControl;
039import org.apache.http.protocol.HttpContext;
040
041/**
042 * Abstract {@link HttpAsyncRequestConsumer} implementation that relieves its
043 * subclasses from having to manage internal state and provides a number of protected
044 * event methods that they need to implement.
045 *
046 * @since 4.2
047 */
048public abstract class AbstractAsyncRequestConsumer<T> implements HttpAsyncRequestConsumer<T> {
049
050    private final AtomicBoolean completed;
051
052    private volatile T result;
053    private volatile Exception ex;
054
055    public AbstractAsyncRequestConsumer() {
056        super();
057        this.completed = new AtomicBoolean(false);
058    }
059
060    /**
061     * Invoked when a HTTP request message is received. Please note
062     * that the {@link #onContentReceived(ContentDecoder, IOControl)} method
063     * will be invoked only for if the request message implements
064     * {@link HttpEntityEnclosingRequest} interface and has a content
065     * entity enclosed.
066     *
067     * @param request HTTP request message.
068     * @throws HttpException in case of HTTP protocol violation
069     * @throws IOException in case of an I/O error
070     */
071    protected abstract void onRequestReceived(
072            HttpRequest request) throws HttpException, IOException;
073
074    /**
075     * Invoked if the request message encloses a content entity.
076     *
077     * @param entity HTTP entity
078     * @param contentType expected content type.
079     * @throws IOException in case of an I/O error
080     */
081    protected abstract void onEntityEnclosed(
082            HttpEntity entity, ContentType contentType) throws IOException;
083
084    /**
085     * Invoked to process a chunk of content from the {@link ContentDecoder}.
086     * The {@link IOControl} interface can be used to suspend input events
087     * if the consumer is temporarily unable to consume more content.
088     * <p>
089     * The consumer can use the {@link ContentDecoder#isCompleted()} method
090     * to find out whether or not the message content has been fully consumed.
091     *
092     * @param decoder content decoder.
093     * @param ioctrl I/O control of the underlying connection.
094     * @throws IOException in case of an I/O error
095     */
096    protected abstract void onContentReceived(
097            ContentDecoder decoder, IOControl ioctrl) throws IOException;
098
099    /**
100     * Invoked to generate a result object from the received HTTP request
101     * message.
102     *
103     * @param context HTTP context.
104     * @return result of the request processing.
105     * @throws Exception in case of an abnormal termination.
106     */
107    protected abstract T buildResult(HttpContext context) throws Exception;
108
109    /**
110     * Invoked to release all system resources currently allocated.
111     */
112    protected abstract void releaseResources();
113
114    /**
115     * Invoked when the consumer is being closed.
116     * @throws IOException may be thrown by subclassses
117     *
118     * @since 4.3
119     */
120    protected void onClose() throws IOException {
121    }
122
123    /**
124     * Use {@link #onRequestReceived(HttpRequest)} instead.
125     */
126    @Override
127    public final void requestReceived(
128            final HttpRequest request) throws HttpException, IOException {
129        onRequestReceived(request);
130        if (request instanceof HttpEntityEnclosingRequest) {
131            final HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
132            if (entity != null) {
133                final ContentType contentType = ContentType.getOrDefault(entity);
134                onEntityEnclosed(entity, contentType);
135            }
136        }
137    }
138
139    /**
140     * Use {@link #onContentReceived(ContentDecoder, IOControl)} instead.
141     */
142    @Override
143    public final void consumeContent(
144            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
145        onContentReceived(decoder, ioctrl);
146    }
147
148    /**
149     * Use {@link #buildResult(HttpContext)} instead.
150     */
151    @Override
152    public final void requestCompleted(final HttpContext context) {
153        if (this.completed.compareAndSet(false, true)) {
154            try {
155                this.result = buildResult(context);
156            } catch (final Exception ex) {
157                this.ex = ex;
158            } finally {
159                releaseResources();
160            }
161        }
162    }
163
164    @Override
165    public final void failed(final Exception ex) {
166        if (this.completed.compareAndSet(false, true)) {
167            this.ex = ex;
168            releaseResources();
169        }
170    }
171
172    @Override
173    public final void close() throws IOException {
174        if (this.completed.compareAndSet(false, true)) {
175            releaseResources();
176            onClose();
177        }
178    }
179
180    @Override
181    public Exception getException() {
182        return this.ex;
183    }
184
185    @Override
186    public T getResult() {
187        return this.result;
188    }
189
190    @Override
191    public boolean isDone() {
192        return this.completed.get();
193    }
194
195}