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.nio.reactor;
029
030import java.io.IOException;
031import java.net.SocketAddress;
032import java.nio.channels.Channel;
033import java.nio.channels.SelectionKey;
034
035import org.apache.http.annotation.ThreadingBehavior;
036import org.apache.http.annotation.Contract;
037import org.apache.http.nio.reactor.IOSession;
038import org.apache.http.nio.reactor.SessionRequest;
039import org.apache.http.nio.reactor.SessionRequestCallback;
040import org.apache.http.util.Args;
041
042/**
043 * Default implementation of {@link SessionRequest}.
044 *
045 * @since 4.0
046 */
047@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
048public class SessionRequestImpl implements SessionRequest {
049
050    private volatile boolean completed;
051    private volatile SelectionKey key;
052
053    private final SocketAddress remoteAddress;
054    private final SocketAddress localAddress;
055    private final Object attachment;
056    private final SessionRequestCallback callback;
057
058    private volatile int connectTimeout;
059    private volatile IOSession session = null;
060    private volatile IOException exception = null;
061
062    public SessionRequestImpl(
063            final SocketAddress remoteAddress,
064            final SocketAddress localAddress,
065            final Object attachment,
066            final SessionRequestCallback callback) {
067        super();
068        Args.notNull(remoteAddress, "Remote address");
069        this.remoteAddress = remoteAddress;
070        this.localAddress = localAddress;
071        this.attachment = attachment;
072        this.callback = callback;
073        this.connectTimeout = 0;
074    }
075
076    @Override
077    public SocketAddress getRemoteAddress() {
078        return this.remoteAddress;
079    }
080
081    @Override
082    public SocketAddress getLocalAddress() {
083        return this.localAddress;
084    }
085
086    @Override
087    public Object getAttachment() {
088        return this.attachment;
089    }
090
091    @Override
092    public boolean isCompleted() {
093        return this.completed;
094    }
095
096    protected void setKey(final SelectionKey key) {
097        this.key = key;
098    }
099
100    @Override
101    public void waitFor() throws InterruptedException {
102        if (this.completed) {
103            return;
104        }
105        synchronized (this) {
106            while (!this.completed) {
107                wait();
108            }
109        }
110    }
111
112    @Override
113    public IOSession getSession() {
114        synchronized (this) {
115            return this.session;
116        }
117    }
118
119    @Override
120    public IOException getException() {
121        synchronized (this) {
122            return this.exception;
123        }
124    }
125
126    public void completed(final IOSession session) {
127        Args.notNull(session, "Session");
128        if (this.completed) {
129            return;
130        }
131        this.completed = true;
132        synchronized (this) {
133            this.session = session;
134            if (this.callback != null) {
135                this.callback.completed(this);
136            }
137            notifyAll();
138        }
139    }
140
141    public void failed(final IOException exception) {
142        if (exception == null) {
143            return;
144        }
145        if (this.completed) {
146            return;
147        }
148        this.completed = true;
149        final SelectionKey key = this.key;
150        if (key != null) {
151            key.cancel();
152            final Channel channel = key.channel();
153            try {
154                channel.close();
155            } catch (final IOException ignore) {}
156        }
157        synchronized (this) {
158            this.exception = exception;
159            if (this.callback != null) {
160                this.callback.failed(this);
161            }
162            notifyAll();
163        }
164    }
165
166    public void timeout() {
167        if (this.completed) {
168            return;
169        }
170        this.completed = true;
171        final SelectionKey key = this.key;
172        if (key != null) {
173            key.cancel();
174            final Channel channel = key.channel();
175            if (channel.isOpen()) {
176                try {
177                    channel.close();
178                } catch (final IOException ignore) {}
179            }
180        }
181        synchronized (this) {
182            if (this.callback != null) {
183                this.callback.timeout(this);
184            }
185        }
186    }
187
188    @Override
189    public int getConnectTimeout() {
190        return this.connectTimeout;
191    }
192
193    @Override
194    public void setConnectTimeout(final int timeout) {
195        if (this.connectTimeout != timeout) {
196            this.connectTimeout = timeout;
197            final SelectionKey key = this.key;
198            if (key != null) {
199                key.selector().wakeup();
200            }
201        }
202    }
203
204    @Override
205    public void cancel() {
206        if (this.completed) {
207            return;
208        }
209        this.completed = true;
210        final SelectionKey key = this.key;
211        if (key != null) {
212            key.cancel();
213            final Channel channel = key.channel();
214            if (channel.isOpen()) {
215                try {
216                    channel.close();
217                } catch (final IOException ignore) {}
218            }
219        }
220        synchronized (this) {
221            if (this.callback != null) {
222                this.callback.cancelled(this);
223            }
224            notifyAll();
225        }
226    }
227
228}