1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Copyright 2013 Red Hat, Inc. and/or its affiliates.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.xnio;
20
21 import java.io.IOException;
22 import java.net.SocketAddress;
23 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
24 import org.xnio.channels.CloseableChannel;
25 import org.xnio.channels.ConnectedChannel;
26
27 import static org.xnio.Bits.allAreClear;
28 import static org.xnio.Bits.allAreSet;
29 import static org.xnio.Bits.anyAreClear;
30
31 /**
32  * The base for all connections.
33  *
34  * @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
35  */

36 public abstract class Connection implements CloseableChannel, ConnectedChannel {
37
38     protected final XnioIoThread thread;
39     @SuppressWarnings("unused")
40     private volatile int state;
41
42     private static final int FLAG_READ_CLOSED           = 0b0001;
43     private static final int FLAG_WRITE_CLOSED          = 0b0010;
44
45     private static final AtomicIntegerFieldUpdater<Connection> stateUpdater = AtomicIntegerFieldUpdater.newUpdater(Connection.class"state");
46
47     /**
48      * Construct a new instance.
49      *
50      * @param thread the I/O thread of this connection
51      */

52     protected Connection(final XnioIoThread thread) {
53         this.thread = thread;
54     }
55
56     private static <A extends SocketAddress> A castAddress(final Class<A> type, SocketAddress address) {
57         return type.isInstance(address) ? type.cast(address) : null;
58     }
59
60     public final <A extends SocketAddress> A getPeerAddress(final Class<A> type) {
61         return castAddress(type, getPeerAddress());
62     }
63
64     public final <A extends SocketAddress> A getLocalAddress(final Class<A> type) {
65         return castAddress(type, getLocalAddress());
66     }
67
68     public final XnioWorker getWorker() {
69         return thread.getWorker();
70     }
71
72     public XnioIoThread getIoThread() {
73         return thread;
74     }
75
76     /**
77      * Indicate that reads have been closed on this connection.
78      *
79      * @return {@code trueif read closure was successfully indicated; {@code falseif this method has already been called
80      */

81     protected boolean readClosed() {
82         int oldVal, newVal;
83         do {
84             oldVal = state;
85             if (allAreSet(oldVal, FLAG_READ_CLOSED)) {
86                 return false;
87             }
88             newVal = oldVal | FLAG_READ_CLOSED;
89         } while (! stateUpdater.compareAndSet(this, oldVal, newVal));
90         if (allAreSet(newVal, FLAG_READ_CLOSED | FLAG_WRITE_CLOSED)) {
91             try {
92                 closeAction();
93             } catch (Throwable ignored) {}
94             invokeCloseListener();
95         }
96         return true;
97     }
98
99     /**
100      * Indicate that writes have been closed on this connection.
101      *
102      * @return {@code trueif write closure was successfully indicated; {@code falseif this method has already been called
103      */

104     protected boolean writeClosed() {
105         int oldVal, newVal;
106         do {
107             oldVal = state;
108             if (allAreSet(oldVal, FLAG_WRITE_CLOSED)) {
109                 return false;
110             }
111             newVal = oldVal | FLAG_WRITE_CLOSED;
112         } while (! stateUpdater.compareAndSet(this, oldVal, newVal));
113         if (allAreSet(newVal, FLAG_READ_CLOSED | FLAG_WRITE_CLOSED)) {
114             try {
115                 closeAction();
116             } catch (Throwable ignored) {}
117             invokeCloseListener();
118         }
119         return true;
120     }
121
122     public final void close() throws IOException {
123         int oldVal, newVal;
124         do {
125             oldVal = state;
126             if (allAreSet(oldVal, FLAG_WRITE_CLOSED | FLAG_READ_CLOSED)) {
127                 return;
128             }
129             newVal = oldVal | FLAG_READ_CLOSED | FLAG_WRITE_CLOSED;
130         } while (! stateUpdater.compareAndSet(this, oldVal, newVal));
131         try {
132             closeAction();
133         } finally {
134             if (allAreClear(oldVal, FLAG_WRITE_CLOSED)) try {
135                 notifyWriteClosed();
136             } catch (Throwable ignored) {
137             }
138             if (allAreClear(oldVal, FLAG_READ_CLOSED)) try {
139                 notifyReadClosed();
140             } catch (Throwable ignored) {
141             }
142             invokeCloseListener();
143         }
144     }
145
146     /**
147      * Determine whether reads have been shut down on this connection.
148      *
149      * @return {@code trueif reads were shut down
150      */

151     public boolean isReadShutdown() {
152         return allAreSet(state, FLAG_READ_CLOSED);
153     }
154
155     /**
156      * Determine whether writes have been shut down on this connection.
157      *
158      * @return {@code trueif writes were shut down
159      */

160     public boolean isWriteShutdown() {
161         return allAreSet(state, FLAG_WRITE_CLOSED);
162     }
163
164     public boolean isOpen() {
165         return anyAreClear(state, FLAG_READ_CLOSED | FLAG_WRITE_CLOSED);
166     }
167
168     /**
169      * Indicate to conduit handlers that writes have been closed.
170      */

171     protected abstract void notifyWriteClosed();
172
173     /**
174      * Indicate to conduit handlers that reads have been closed.
175      */

176     protected abstract void notifyReadClosed();
177
178     abstract void invokeCloseListener();
179
180     /**
181      * The close action to perform on this connection.
182      *
183      * @throws IOException if close fails
184      */

185     protected void closeAction() throws IOException {}
186
187     public boolean supportsOption(final Option<?> option) {
188         return false;
189     }
190
191     public <T> T getOption(final Option<T> option) throws IOException {
192         return null;
193     }
194
195     public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException {
196         return null;
197     }
198 }
199