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 */
027
028package org.apache.http.impl.execchain;
029
030import java.io.Closeable;
031import java.io.IOException;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import org.apache.commons.logging.Log;
036import org.apache.http.HttpClientConnection;
037import org.apache.http.annotation.Contract;
038import org.apache.http.annotation.ThreadingBehavior;
039import org.apache.http.concurrent.Cancellable;
040import org.apache.http.conn.ConnectionReleaseTrigger;
041import org.apache.http.conn.HttpClientConnectionManager;
042
043/**
044 * Internal connection holder.
045 *
046 * @since 4.3
047 */
048@Contract(threading = ThreadingBehavior.SAFE)
049class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeable {
050
051    private final Log log;
052
053    private final HttpClientConnectionManager manager;
054    private final HttpClientConnection managedConn;
055    private final AtomicBoolean released;
056    private volatile boolean reusable;
057    private volatile Object state;
058    private volatile long validDuration;
059    private volatile TimeUnit tunit;
060
061    public ConnectionHolder(
062            final Log log,
063            final HttpClientConnectionManager manager,
064            final HttpClientConnection managedConn) {
065        super();
066        this.log = log;
067        this.manager = manager;
068        this.managedConn = managedConn;
069        this.released = new AtomicBoolean(false);
070    }
071
072    public boolean isReusable() {
073        return this.reusable;
074    }
075
076    public void markReusable() {
077        this.reusable = true;
078    }
079
080    public void markNonReusable() {
081        this.reusable = false;
082    }
083
084    public void setState(final Object state) {
085        this.state = state;
086    }
087
088    public void setValidFor(final long duration, final TimeUnit tunit) {
089        synchronized (this.managedConn) {
090            this.validDuration = duration;
091            this.tunit = tunit;
092        }
093    }
094
095    private void releaseConnection(final boolean reusable) {
096        if (this.released.compareAndSet(false, true)) {
097            synchronized (this.managedConn) {
098                if (reusable) {
099                    this.manager.releaseConnection(this.managedConn,
100                            this.state, this.validDuration, this.tunit);
101                } else {
102                    try {
103                        this.managedConn.close();
104                        log.debug("Connection discarded");
105                    } catch (final IOException ex) {
106                        if (this.log.isDebugEnabled()) {
107                            this.log.debug(ex.getMessage(), ex);
108                        }
109                    } finally {
110                        this.manager.releaseConnection(
111                                this.managedConn, null, 0, TimeUnit.MILLISECONDS);
112                    }
113                }
114            }
115        }
116    }
117
118    @Override
119    public void releaseConnection() {
120        releaseConnection(this.reusable);
121    }
122
123    @Override
124    public void abortConnection() {
125        if (this.released.compareAndSet(false, true)) {
126            synchronized (this.managedConn) {
127                try {
128                    this.managedConn.shutdown();
129                    log.debug("Connection discarded");
130                } catch (final IOException ex) {
131                    if (this.log.isDebugEnabled()) {
132                        this.log.debug(ex.getMessage(), ex);
133                    }
134                } finally {
135                    this.manager.releaseConnection(
136                            this.managedConn, null, 0, TimeUnit.MILLISECONDS);
137                }
138            }
139        }
140    }
141
142    @Override
143    public boolean cancel() {
144        final boolean alreadyReleased = this.released.get();
145        log.debug("Cancelling request execution");
146        abortConnection();
147        return !alreadyReleased;
148    }
149
150    public boolean isReleased() {
151        return this.released.get();
152    }
153
154    @Override
155    public void close() throws IOException {
156        releaseConnection(false);
157    }
158
159}