Committed by
Gerrit Code Review
Drop unused onlab-nio module
Change-Id: I52141335643ad5b62b2a9cebe4d79faf0762e3e0
Showing
23 changed files
with
0 additions
and
3254 deletions
utils/nio/pom.xml
deleted
100644 → 0
1 | -<?xml version="1.0" encoding="UTF-8"?> | ||
2 | -<!-- | ||
3 | - ~ Copyright 2014 Open Networking Laboratory | ||
4 | - ~ | ||
5 | - ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | - ~ you may not use this file except in compliance with the License. | ||
7 | - ~ You may obtain a copy of the License at | ||
8 | - ~ | ||
9 | - ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | - ~ | ||
11 | - ~ Unless required by applicable law or agreed to in writing, software | ||
12 | - ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | - ~ See the License for the specific language governing permissions and | ||
15 | - ~ limitations under the License. | ||
16 | - --> | ||
17 | -<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
18 | - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
19 | - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
20 | - <modelVersion>4.0.0</modelVersion> | ||
21 | - | ||
22 | - <parent> | ||
23 | - <groupId>org.onosproject</groupId> | ||
24 | - <artifactId>onlab-utils</artifactId> | ||
25 | - <version>1.5.0-SNAPSHOT</version> | ||
26 | - <relativePath>../pom.xml</relativePath> | ||
27 | - </parent> | ||
28 | - | ||
29 | - <artifactId>onlab-nio</artifactId> | ||
30 | - <packaging>bundle</packaging> | ||
31 | - | ||
32 | - <description>Fast network I/O using Java NIO</description> | ||
33 | - | ||
34 | - <dependencies> | ||
35 | - <dependency> | ||
36 | - <groupId>com.google.guava</groupId> | ||
37 | - <artifactId>guava-testlib</artifactId> | ||
38 | - <scope>test</scope> | ||
39 | - </dependency> | ||
40 | - <dependency> | ||
41 | - <groupId>commons-pool</groupId> | ||
42 | - <artifactId>commons-pool</artifactId> | ||
43 | - </dependency> | ||
44 | - <dependency> | ||
45 | - <groupId>org.onosproject</groupId> | ||
46 | - <artifactId>onos-api</artifactId> | ||
47 | - </dependency> | ||
48 | - <dependency> | ||
49 | - <groupId>org.onosproject</groupId> | ||
50 | - <artifactId>onlab-misc</artifactId> | ||
51 | - </dependency> | ||
52 | - <dependency> | ||
53 | - <groupId>org.onosproject</groupId> | ||
54 | - <artifactId>onlab-junit</artifactId> | ||
55 | - <scope>test</scope> | ||
56 | - </dependency> | ||
57 | - </dependencies> | ||
58 | -</project> |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -/** | ||
19 | - * Base {@link Message} implementation. | ||
20 | - */ | ||
21 | -public abstract class AbstractMessage implements Message { | ||
22 | - | ||
23 | - protected int length; | ||
24 | - | ||
25 | - @Override | ||
26 | - public int length() { | ||
27 | - return length; | ||
28 | - } | ||
29 | - | ||
30 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import java.io.IOException; | ||
19 | -import java.net.SocketAddress; | ||
20 | -import java.net.StandardSocketOptions; | ||
21 | -import java.nio.channels.SelectionKey; | ||
22 | -import java.nio.channels.ServerSocketChannel; | ||
23 | -import java.util.Iterator; | ||
24 | - | ||
25 | -import static com.google.common.base.Preconditions.checkNotNull; | ||
26 | - | ||
27 | -/** | ||
28 | - * Selector loop derivative tailored to acceptConnection inbound connections. | ||
29 | - */ | ||
30 | -public abstract class AcceptorLoop extends SelectorLoop { | ||
31 | - | ||
32 | - private SocketAddress listenAddress; | ||
33 | - private ServerSocketChannel socketChannel; | ||
34 | - | ||
35 | - /** | ||
36 | - * Creates an acceptor loop with the specified selection timeout and | ||
37 | - * accepting connections on the the given address. | ||
38 | - * | ||
39 | - * @param selectTimeout selection timeout; specified in millis | ||
40 | - * @param listenAddress socket address where to listen for connections | ||
41 | - * @throws IOException if the backing selector cannot be opened | ||
42 | - */ | ||
43 | - public AcceptorLoop(long selectTimeout, SocketAddress listenAddress) | ||
44 | - throws IOException { | ||
45 | - super(selectTimeout); | ||
46 | - this.listenAddress = checkNotNull(listenAddress, "Address cannot be null"); | ||
47 | - } | ||
48 | - | ||
49 | - /** | ||
50 | - * Hook to accept an inbound connection on the specified socket channel. | ||
51 | - * | ||
52 | - * @param channel socketChannel where an accept operation awaits | ||
53 | - * @throws IOException if the accept operation cannot be processed | ||
54 | - */ | ||
55 | - protected abstract void acceptConnection(ServerSocketChannel channel) throws IOException; | ||
56 | - | ||
57 | - /** | ||
58 | - * Opens a new server socket channel configured in non-blocking mode and | ||
59 | - * bound to the loop's listen address. | ||
60 | - * | ||
61 | - * @throws IOException if unable to open or configure the socket channel | ||
62 | - */ | ||
63 | - protected synchronized void openChannel() throws IOException { | ||
64 | - socketChannel = ServerSocketChannel.open(); | ||
65 | - socketChannel.configureBlocking(false); | ||
66 | - socketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); | ||
67 | - socketChannel.register(selector, SelectionKey.OP_ACCEPT); | ||
68 | - socketChannel.bind(listenAddress); | ||
69 | - } | ||
70 | - | ||
71 | - /** | ||
72 | - * Closes the server socket channel. | ||
73 | - * | ||
74 | - * @throws IOException if unable to close the socketChannel | ||
75 | - */ | ||
76 | - protected synchronized void closechannel() throws IOException { | ||
77 | - if (socketChannel != null) { | ||
78 | - socketChannel.close(); | ||
79 | - socketChannel = null; | ||
80 | - } | ||
81 | - } | ||
82 | - | ||
83 | - @Override | ||
84 | - public void shutdown() { | ||
85 | - try { | ||
86 | - closechannel(); | ||
87 | - } catch (IOException e) { | ||
88 | - log.warn("Unable to close the socketChannel", e); | ||
89 | - } | ||
90 | - super.shutdown(); | ||
91 | - } | ||
92 | - | ||
93 | - @Override | ||
94 | - protected void loop() throws IOException { | ||
95 | - openChannel(); | ||
96 | - notifyReady(); | ||
97 | - | ||
98 | - // Keep looping until told otherwise. | ||
99 | - while (isRunning()) { | ||
100 | - // Attempt a selection; if no operations selected or if signalled | ||
101 | - // to shutdown, spin through. | ||
102 | - int count = selector.select(selectTimeout); | ||
103 | - if (count == 0 || !isRunning()) { | ||
104 | - continue; | ||
105 | - } | ||
106 | - | ||
107 | - // Iterate over all keys selected for an operation and process them. | ||
108 | - Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); | ||
109 | - while (keys.hasNext()) { | ||
110 | - // Fetch the key and remove it from the pending list. | ||
111 | - SelectionKey key = keys.next(); | ||
112 | - keys.remove(); | ||
113 | - | ||
114 | - // If the key has a pending acceptConnection operation, process it. | ||
115 | - if (key.isAcceptable()) { | ||
116 | - acceptConnection((ServerSocketChannel) key.channel()); | ||
117 | - } | ||
118 | - } | ||
119 | - } | ||
120 | - } | ||
121 | - | ||
122 | -} | ||
123 | - |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import java.io.IOException; | ||
19 | -import java.nio.channels.ByteChannel; | ||
20 | -import java.nio.channels.CancelledKeyException; | ||
21 | -import java.nio.channels.ClosedChannelException; | ||
22 | -import java.nio.channels.SelectableChannel; | ||
23 | -import java.nio.channels.SelectionKey; | ||
24 | -import java.nio.channels.SocketChannel; | ||
25 | -import java.util.Iterator; | ||
26 | -import java.util.List; | ||
27 | -import java.util.Queue; | ||
28 | -import java.util.Set; | ||
29 | -import java.util.concurrent.ConcurrentLinkedQueue; | ||
30 | -import java.util.concurrent.CopyOnWriteArraySet; | ||
31 | - | ||
32 | -/** | ||
33 | - * I/O loop for driving inbound & outbound {@link Message} transfer via | ||
34 | - * {@link MessageStream}. | ||
35 | - * | ||
36 | - * @param <M> message type | ||
37 | - * @param <S> message stream type | ||
38 | - */ | ||
39 | -public abstract class IOLoop<M extends Message, S extends MessageStream<M>> | ||
40 | - extends SelectorLoop { | ||
41 | - | ||
42 | - // Queue of requests for new message streams to enter the IO loop processing. | ||
43 | - private final Queue<NewStreamRequest> newStreamRequests = new ConcurrentLinkedQueue<>(); | ||
44 | - | ||
45 | - // Carries information required for admitting a new message stream. | ||
46 | - private class NewStreamRequest { | ||
47 | - private final S stream; | ||
48 | - private final SelectableChannel channel; | ||
49 | - private final int op; | ||
50 | - | ||
51 | - public NewStreamRequest(S stream, SelectableChannel channel, int op) { | ||
52 | - this.stream = stream; | ||
53 | - this.channel = channel; | ||
54 | - this.op = op; | ||
55 | - } | ||
56 | - } | ||
57 | - | ||
58 | - // Set of message streams currently admitted into the IO loop. | ||
59 | - private final Set<MessageStream<M>> streams = new CopyOnWriteArraySet<>(); | ||
60 | - | ||
61 | - /** | ||
62 | - * Creates an IO loop with the given selection timeout. | ||
63 | - * | ||
64 | - * @param timeout selection timeout in milliseconds | ||
65 | - * @throws IOException if the backing selector cannot be opened | ||
66 | - */ | ||
67 | - public IOLoop(long timeout) throws IOException { | ||
68 | - super(timeout); | ||
69 | - } | ||
70 | - | ||
71 | - /** | ||
72 | - * Returns the number of message stream in custody of the loop. | ||
73 | - * | ||
74 | - * @return number of message streams | ||
75 | - */ | ||
76 | - public int streamCount() { | ||
77 | - return streams.size(); | ||
78 | - } | ||
79 | - | ||
80 | - /** | ||
81 | - * Creates a new message stream backed by the specified socket channel. | ||
82 | - * | ||
83 | - * @param byteChannel backing byte channel | ||
84 | - * @return newly created message stream | ||
85 | - */ | ||
86 | - protected abstract S createStream(ByteChannel byteChannel); | ||
87 | - | ||
88 | - /** | ||
89 | - * Removes the specified message stream from the IO loop. | ||
90 | - * | ||
91 | - * @param stream message stream to remove | ||
92 | - */ | ||
93 | - protected void removeStream(MessageStream<M> stream) { | ||
94 | - streams.remove(stream); | ||
95 | - } | ||
96 | - | ||
97 | - /** | ||
98 | - * Processes the list of messages extracted from the specified message | ||
99 | - * stream. | ||
100 | - * | ||
101 | - * @param messages non-empty list of received messages | ||
102 | - * @param stream message stream from which the messages were extracted | ||
103 | - */ | ||
104 | - protected abstract void processMessages(List<M> messages, MessageStream<M> stream); | ||
105 | - | ||
106 | - /** | ||
107 | - * Completes connection request pending on the given selection key. | ||
108 | - * | ||
109 | - * @param key selection key holding the pending connect operation. | ||
110 | - * @throws IOException when I/O exception of some sort has occurred | ||
111 | - */ | ||
112 | - protected void connect(SelectionKey key) throws IOException { | ||
113 | - SocketChannel ch = (SocketChannel) key.channel(); | ||
114 | - ch.finishConnect(); | ||
115 | - if (key.isValid()) { | ||
116 | - key.interestOps(SelectionKey.OP_READ); | ||
117 | - } | ||
118 | - } | ||
119 | - | ||
120 | - /** | ||
121 | - * Processes an IO operation pending on the specified key. | ||
122 | - * | ||
123 | - * @param key selection key holding the pending I/O operation. | ||
124 | - */ | ||
125 | - protected void processKeyOperation(SelectionKey key) { | ||
126 | - @SuppressWarnings("unchecked") | ||
127 | - S stream = (S) key.attachment(); | ||
128 | - | ||
129 | - try { | ||
130 | - // If the key is not valid, bail out. | ||
131 | - if (!key.isValid()) { | ||
132 | - stream.close(); | ||
133 | - return; | ||
134 | - } | ||
135 | - | ||
136 | - // If there is a pending connect operation, complete it. | ||
137 | - if (key.isConnectable()) { | ||
138 | - try { | ||
139 | - connect(key); | ||
140 | - } catch (IOException | IllegalStateException e) { | ||
141 | - log.warn("Unable to complete connection", e); | ||
142 | - } | ||
143 | - } | ||
144 | - | ||
145 | - // If there is a read operation, slurp as much data as possible. | ||
146 | - if (key.isReadable()) { | ||
147 | - List<M> messages = stream.read(); | ||
148 | - | ||
149 | - // No messages or failed flush imply disconnect; bail. | ||
150 | - if (messages == null || stream.hadError()) { | ||
151 | - stream.close(); | ||
152 | - return; | ||
153 | - } | ||
154 | - | ||
155 | - // If there were any messages read, process them. | ||
156 | - if (!messages.isEmpty()) { | ||
157 | - try { | ||
158 | - processMessages(messages, stream); | ||
159 | - } catch (RuntimeException e) { | ||
160 | - onError(stream, e); | ||
161 | - } | ||
162 | - } | ||
163 | - } | ||
164 | - | ||
165 | - // If there are pending writes, flush them | ||
166 | - if (key.isWritable()) { | ||
167 | - stream.flushIfPossible(); | ||
168 | - } | ||
169 | - | ||
170 | - // If there were any issued flushing, close the stream. | ||
171 | - if (stream.hadError()) { | ||
172 | - stream.close(); | ||
173 | - } | ||
174 | - | ||
175 | - } catch (CancelledKeyException e) { | ||
176 | - // Key was cancelled, so silently close the stream | ||
177 | - stream.close(); | ||
178 | - } catch (IOException e) { | ||
179 | - if (!stream.isClosed() && !isResetByPeer(e)) { | ||
180 | - log.warn("Unable to process IO", e); | ||
181 | - } | ||
182 | - stream.close(); | ||
183 | - } | ||
184 | - } | ||
185 | - | ||
186 | - // Indicates whether or not this exception is caused by 'reset by peer'. | ||
187 | - private boolean isResetByPeer(IOException e) { | ||
188 | - Throwable cause = e.getCause(); | ||
189 | - return cause != null && cause instanceof IOException && | ||
190 | - cause.getMessage().contains("reset by peer"); | ||
191 | - } | ||
192 | - | ||
193 | - /** | ||
194 | - * Hook to allow intercept of any errors caused during message processing. | ||
195 | - * Default behaviour is to rethrow the error. | ||
196 | - * | ||
197 | - * @param stream message stream involved in the error | ||
198 | - * @param error the runtime exception | ||
199 | - */ | ||
200 | - protected void onError(S stream, RuntimeException error) { | ||
201 | - throw error; | ||
202 | - } | ||
203 | - | ||
204 | - /** | ||
205 | - * Admits a new message stream backed by the specified socket channel | ||
206 | - * with a pending accept operation. | ||
207 | - * | ||
208 | - * @param channel backing socket channel | ||
209 | - * @return newly accepted message stream | ||
210 | - */ | ||
211 | - public S acceptStream(SocketChannel channel) { | ||
212 | - return createAndAdmit(channel, SelectionKey.OP_READ); | ||
213 | - } | ||
214 | - | ||
215 | - | ||
216 | - /** | ||
217 | - * Admits a new message stream backed by the specified socket channel | ||
218 | - * with a pending connect operation. | ||
219 | - * | ||
220 | - * @param channel backing socket channel | ||
221 | - * @return newly connected message stream | ||
222 | - */ | ||
223 | - public S connectStream(SocketChannel channel) { | ||
224 | - return createAndAdmit(channel, SelectionKey.OP_CONNECT); | ||
225 | - } | ||
226 | - | ||
227 | - /** | ||
228 | - * Creates a new message stream backed by the specified socket channel | ||
229 | - * and admits it into the IO loop. | ||
230 | - * | ||
231 | - * @param channel socket channel | ||
232 | - * @param op pending operations mask to be applied to the selection | ||
233 | - * key as a set of initial interestedOps | ||
234 | - * @return newly created message stream | ||
235 | - */ | ||
236 | - private synchronized S createAndAdmit(SocketChannel channel, int op) { | ||
237 | - S stream = createStream(channel); | ||
238 | - streams.add(stream); | ||
239 | - newStreamRequests.add(new NewStreamRequest(stream, channel, op)); | ||
240 | - selector.wakeup(); | ||
241 | - return stream; | ||
242 | - } | ||
243 | - | ||
244 | - /** | ||
245 | - * Safely admits new streams into the IO loop. | ||
246 | - */ | ||
247 | - private void admitNewStreams() { | ||
248 | - Iterator<NewStreamRequest> it = newStreamRequests.iterator(); | ||
249 | - while (isRunning() && it.hasNext()) { | ||
250 | - try { | ||
251 | - NewStreamRequest request = it.next(); | ||
252 | - it.remove(); | ||
253 | - SelectionKey key = request.channel.register(selector, request.op, | ||
254 | - request.stream); | ||
255 | - request.stream.setKey(key); | ||
256 | - } catch (ClosedChannelException e) { | ||
257 | - log.warn("Unable to admit new message stream", e); | ||
258 | - } | ||
259 | - } | ||
260 | - } | ||
261 | - | ||
262 | - @Override | ||
263 | - protected void loop() throws IOException { | ||
264 | - notifyReady(); | ||
265 | - | ||
266 | - // Keep going until told otherwise. | ||
267 | - while (isRunning()) { | ||
268 | - admitNewStreams(); | ||
269 | - | ||
270 | - // Process flushes & write selects on all streams | ||
271 | - for (MessageStream<M> stream : streams) { | ||
272 | - stream.flushIfWriteNotPending(); | ||
273 | - } | ||
274 | - | ||
275 | - // Select keys and process them. | ||
276 | - int count = selector.select(selectTimeout); | ||
277 | - if (count > 0 && isRunning()) { | ||
278 | - Iterator<SelectionKey> it = selector.selectedKeys().iterator(); | ||
279 | - while (it.hasNext()) { | ||
280 | - SelectionKey key = it.next(); | ||
281 | - it.remove(); | ||
282 | - processKeyOperation(key); | ||
283 | - } | ||
284 | - } | ||
285 | - } | ||
286 | - } | ||
287 | - | ||
288 | - /** | ||
289 | - * Prunes the registered streams by discarding any stale ones. | ||
290 | - * | ||
291 | - * @return number of remaining streams | ||
292 | - */ | ||
293 | - public synchronized int pruneStaleStreams() { | ||
294 | - for (MessageStream<M> stream : streams) { | ||
295 | - if (stream.isStale()) { | ||
296 | - stream.close(); | ||
297 | - } | ||
298 | - } | ||
299 | - return streams.size(); | ||
300 | - } | ||
301 | - | ||
302 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -/** | ||
19 | - * Representation of a message transferred via {@link MessageStream}. | ||
20 | - */ | ||
21 | -public interface Message { | ||
22 | - | ||
23 | - /** | ||
24 | - * Gets the message length in bytes. | ||
25 | - * | ||
26 | - * @return number of bytes | ||
27 | - */ | ||
28 | - int length(); | ||
29 | - | ||
30 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.onlab.util.Counter; | ||
19 | -import org.slf4j.Logger; | ||
20 | -import org.slf4j.LoggerFactory; | ||
21 | - | ||
22 | -import java.io.IOException; | ||
23 | -import java.nio.ByteBuffer; | ||
24 | -import java.nio.channels.ByteChannel; | ||
25 | -import java.nio.channels.SelectionKey; | ||
26 | -import java.util.ArrayList; | ||
27 | -import java.util.List; | ||
28 | -import java.util.Objects; | ||
29 | - | ||
30 | -import static com.google.common.base.Preconditions.checkArgument; | ||
31 | -import static com.google.common.base.Preconditions.checkNotNull; | ||
32 | -import static java.lang.System.currentTimeMillis; | ||
33 | -import static java.nio.ByteBuffer.allocateDirect; | ||
34 | - | ||
35 | -/** | ||
36 | - * Bi-directional message stream for transferring messages to & from the | ||
37 | - * network via two byte buffers. | ||
38 | - * | ||
39 | - * @param <M> message type | ||
40 | - */ | ||
41 | -public abstract class MessageStream<M extends Message> { | ||
42 | - | ||
43 | - protected Logger log = LoggerFactory.getLogger(getClass()); | ||
44 | - | ||
45 | - private final IOLoop<M, ?> loop; | ||
46 | - private final ByteChannel channel; | ||
47 | - private final int maxIdleMillis; | ||
48 | - | ||
49 | - private final ByteBuffer inbound; | ||
50 | - private ByteBuffer outbound; | ||
51 | - private SelectionKey key; | ||
52 | - | ||
53 | - private volatile boolean closed = false; | ||
54 | - private volatile boolean writePending; | ||
55 | - private volatile boolean writeOccurred; | ||
56 | - | ||
57 | - private Exception ioError; | ||
58 | - private long lastActiveTime; | ||
59 | - | ||
60 | - private final Counter bytesIn = new Counter(); | ||
61 | - private final Counter messagesIn = new Counter(); | ||
62 | - private final Counter bytesOut = new Counter(); | ||
63 | - private final Counter messagesOut = new Counter(); | ||
64 | - | ||
65 | - /** | ||
66 | - * Creates a message stream associated with the specified IO loop and | ||
67 | - * backed by the given byte channel. | ||
68 | - * | ||
69 | - * @param loop IO loop | ||
70 | - * @param byteChannel backing byte channel | ||
71 | - * @param bufferSize size of the backing byte buffers | ||
72 | - * @param maxIdleMillis maximum number of millis the stream can be idle | ||
73 | - * before it will be closed | ||
74 | - */ | ||
75 | - protected MessageStream(IOLoop<M, ?> loop, ByteChannel byteChannel, | ||
76 | - int bufferSize, int maxIdleMillis) { | ||
77 | - this.loop = checkNotNull(loop, "Loop cannot be null"); | ||
78 | - this.channel = checkNotNull(byteChannel, "Byte channel cannot be null"); | ||
79 | - | ||
80 | - checkArgument(maxIdleMillis > 0, "Idle time must be positive"); | ||
81 | - this.maxIdleMillis = maxIdleMillis; | ||
82 | - | ||
83 | - inbound = allocateDirect(bufferSize); | ||
84 | - outbound = allocateDirect(bufferSize); | ||
85 | - } | ||
86 | - | ||
87 | - /** | ||
88 | - * Gets a single message from the specified byte buffer; this is | ||
89 | - * to be done without manipulating the buffer via flip, reset or clear. | ||
90 | - * | ||
91 | - * @param buffer byte buffer | ||
92 | - * @return read message or null if there are not enough bytes to read | ||
93 | - * a complete message | ||
94 | - */ | ||
95 | - protected abstract M read(ByteBuffer buffer); | ||
96 | - | ||
97 | - /** | ||
98 | - * Puts the specified message into the specified byte buffer; this is | ||
99 | - * to be done without manipulating the buffer via flip, reset or clear. | ||
100 | - * | ||
101 | - * @param message message to be write into the buffer | ||
102 | - * @param buffer byte buffer | ||
103 | - */ | ||
104 | - protected abstract void write(M message, ByteBuffer buffer); | ||
105 | - | ||
106 | - /** | ||
107 | - * Closes the message buffer. | ||
108 | - */ | ||
109 | - public void close() { | ||
110 | - synchronized (this) { | ||
111 | - if (closed) { | ||
112 | - return; | ||
113 | - } | ||
114 | - closed = true; | ||
115 | - } | ||
116 | - | ||
117 | - bytesIn.freeze(); | ||
118 | - bytesOut.freeze(); | ||
119 | - messagesIn.freeze(); | ||
120 | - messagesOut.freeze(); | ||
121 | - | ||
122 | - loop.removeStream(this); | ||
123 | - if (key != null) { | ||
124 | - try { | ||
125 | - key.cancel(); | ||
126 | - key.channel().close(); | ||
127 | - } catch (IOException e) { | ||
128 | - log.warn("Unable to close stream", e); | ||
129 | - } | ||
130 | - } | ||
131 | - } | ||
132 | - | ||
133 | - /** | ||
134 | - * Indicates whether this buffer has been closed. | ||
135 | - * | ||
136 | - * @return true if this stream has been closed | ||
137 | - */ | ||
138 | - public synchronized boolean isClosed() { | ||
139 | - return closed; | ||
140 | - } | ||
141 | - | ||
142 | - /** | ||
143 | - * Returns the stream IO selection key. | ||
144 | - * | ||
145 | - * @return socket channel registration selection key | ||
146 | - */ | ||
147 | - public SelectionKey key() { | ||
148 | - return key; | ||
149 | - } | ||
150 | - | ||
151 | - /** | ||
152 | - * Binds the selection key to be used for driving IO operations on the stream. | ||
153 | - * | ||
154 | - * @param key IO selection key | ||
155 | - */ | ||
156 | - public void setKey(SelectionKey key) { | ||
157 | - this.key = key; | ||
158 | - this.lastActiveTime = currentTimeMillis(); | ||
159 | - } | ||
160 | - | ||
161 | - /** | ||
162 | - * Returns the IO loop to which this stream is bound. | ||
163 | - * | ||
164 | - * @return I/O loop used to drive this stream | ||
165 | - */ | ||
166 | - public IOLoop<M, ?> loop() { | ||
167 | - return loop; | ||
168 | - } | ||
169 | - | ||
170 | - /** | ||
171 | - * Indicates whether the any prior IO encountered an error. | ||
172 | - * | ||
173 | - * @return true if a write failed | ||
174 | - */ | ||
175 | - public boolean hadError() { | ||
176 | - return ioError != null; | ||
177 | - } | ||
178 | - | ||
179 | - /** | ||
180 | - * Gets the prior IO error, if one occurred. | ||
181 | - * | ||
182 | - * @return IO error; null if none occurred | ||
183 | - */ | ||
184 | - public Exception getError() { | ||
185 | - return ioError; | ||
186 | - } | ||
187 | - | ||
188 | - /** | ||
189 | - * Reads, without blocking, a list of messages from the stream. | ||
190 | - * The list will be empty if there were not messages pending. | ||
191 | - * | ||
192 | - * @return list of messages or null if backing channel has been closed | ||
193 | - * @throws IOException if messages could not be read | ||
194 | - */ | ||
195 | - public List<M> read() throws IOException { | ||
196 | - try { | ||
197 | - int read = channel.read(inbound); | ||
198 | - if (read != -1) { | ||
199 | - // Read the messages one-by-one and add them to the list. | ||
200 | - List<M> messages = new ArrayList<>(); | ||
201 | - M message; | ||
202 | - inbound.flip(); | ||
203 | - while ((message = read(inbound)) != null) { | ||
204 | - messages.add(message); | ||
205 | - messagesIn.add(1); | ||
206 | - bytesIn.add(message.length()); | ||
207 | - } | ||
208 | - inbound.compact(); | ||
209 | - | ||
210 | - // Mark the stream with current time to indicate liveness. | ||
211 | - lastActiveTime = currentTimeMillis(); | ||
212 | - return messages; | ||
213 | - } | ||
214 | - return null; | ||
215 | - | ||
216 | - } catch (Exception e) { | ||
217 | - throw new IOException("Unable to read messages", e); | ||
218 | - } | ||
219 | - } | ||
220 | - | ||
221 | - /** | ||
222 | - * Writes the specified list of messages to the stream. | ||
223 | - * | ||
224 | - * @param messages list of messages to write | ||
225 | - * @throws IOException if error occurred while writing the data | ||
226 | - */ | ||
227 | - public void write(List<M> messages) throws IOException { | ||
228 | - synchronized (this) { | ||
229 | - // First write all messages. | ||
230 | - for (M m : messages) { | ||
231 | - append(m); | ||
232 | - } | ||
233 | - flushUnlessAlreadyPlanningTo(); | ||
234 | - } | ||
235 | - } | ||
236 | - | ||
237 | - /** | ||
238 | - * Writes the given message to the stream. | ||
239 | - * | ||
240 | - * @param message message to write | ||
241 | - * @throws IOException if error occurred while writing the data | ||
242 | - */ | ||
243 | - public void write(M message) throws IOException { | ||
244 | - synchronized (this) { | ||
245 | - append(message); | ||
246 | - flushUnlessAlreadyPlanningTo(); | ||
247 | - } | ||
248 | - } | ||
249 | - | ||
250 | - // Appends the specified message into the internal buffer, growing the | ||
251 | - // buffer if required. | ||
252 | - private void append(M message) { | ||
253 | - // If the buffer does not have sufficient length double it. | ||
254 | - while (outbound.remaining() < message.length()) { | ||
255 | - doubleSize(); | ||
256 | - } | ||
257 | - write(message, outbound); | ||
258 | - messagesOut.add(1); | ||
259 | - bytesOut.add(message.length()); | ||
260 | - } | ||
261 | - | ||
262 | - // Forces a flush, unless one is planned already. | ||
263 | - private void flushUnlessAlreadyPlanningTo() throws IOException { | ||
264 | - if (!writeOccurred && !writePending) { | ||
265 | - flush(); | ||
266 | - } | ||
267 | - } | ||
268 | - | ||
269 | - /** | ||
270 | - * Flushes any pending writes. | ||
271 | - * | ||
272 | - * @throws IOException if flush failed | ||
273 | - */ | ||
274 | - public void flush() throws IOException { | ||
275 | - synchronized (this) { | ||
276 | - if (!writeOccurred && !writePending) { | ||
277 | - outbound.flip(); | ||
278 | - try { | ||
279 | - channel.write(outbound); | ||
280 | - } catch (IOException e) { | ||
281 | - if (!closed && !Objects.equals(e.getMessage(), "Broken pipe")) { | ||
282 | - log.warn("Unable to write data", e); | ||
283 | - ioError = e; | ||
284 | - } | ||
285 | - } | ||
286 | - lastActiveTime = currentTimeMillis(); | ||
287 | - writeOccurred = true; | ||
288 | - writePending = outbound.hasRemaining(); | ||
289 | - outbound.compact(); | ||
290 | - } | ||
291 | - } | ||
292 | - } | ||
293 | - | ||
294 | - /** | ||
295 | - * Indicates whether the stream has bytes to be written to the channel. | ||
296 | - * | ||
297 | - * @return true if there are bytes to be written | ||
298 | - */ | ||
299 | - boolean isWritePending() { | ||
300 | - synchronized (this) { | ||
301 | - return writePending; | ||
302 | - } | ||
303 | - } | ||
304 | - | ||
305 | - | ||
306 | - /** | ||
307 | - * Indicates whether data has been written but not flushed yet. | ||
308 | - * | ||
309 | - * @return true if flush is required | ||
310 | - */ | ||
311 | - boolean isFlushRequired() { | ||
312 | - synchronized (this) { | ||
313 | - return outbound.position() > 0; | ||
314 | - } | ||
315 | - } | ||
316 | - | ||
317 | - /** | ||
318 | - * Attempts to flush data, internal stream state and channel availability | ||
319 | - * permitting. Invoked by the driver I/O loop during handling of writable | ||
320 | - * selection key. | ||
321 | - * <p> | ||
322 | - * Resets the internal state flags {@code writeOccurred} and | ||
323 | - * {@code writePending}. | ||
324 | - * </p> | ||
325 | - * @throws IOException if implicit flush failed | ||
326 | - */ | ||
327 | - void flushIfPossible() throws IOException { | ||
328 | - synchronized (this) { | ||
329 | - writePending = false; | ||
330 | - writeOccurred = false; | ||
331 | - if (outbound.position() > 0) { | ||
332 | - flush(); | ||
333 | - } | ||
334 | - } | ||
335 | - key.interestOps(SelectionKey.OP_READ); | ||
336 | - } | ||
337 | - | ||
338 | - /** | ||
339 | - * Attempts to flush data, internal stream state and channel availability | ||
340 | - * permitting and if other writes are not pending. Invoked by the driver | ||
341 | - * I/O loop prior to entering select wait. Resets the internal | ||
342 | - * {@code writeOccurred} state flag. | ||
343 | - * | ||
344 | - * @throws IOException if implicit flush failed | ||
345 | - */ | ||
346 | - void flushIfWriteNotPending() throws IOException { | ||
347 | - synchronized (this) { | ||
348 | - writeOccurred = false; | ||
349 | - if (!writePending && outbound.position() > 0) { | ||
350 | - flush(); | ||
351 | - } | ||
352 | - } | ||
353 | - if (isWritePending()) { | ||
354 | - key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); | ||
355 | - } | ||
356 | - } | ||
357 | - | ||
358 | - /** | ||
359 | - * Doubles the size of the outbound buffer. | ||
360 | - */ | ||
361 | - private void doubleSize() { | ||
362 | - ByteBuffer newBuffer = allocateDirect(outbound.capacity() * 2); | ||
363 | - outbound.flip(); | ||
364 | - newBuffer.put(outbound); | ||
365 | - outbound = newBuffer; | ||
366 | - } | ||
367 | - | ||
368 | - /** | ||
369 | - * Returns the maximum number of milliseconds the stream is allowed | ||
370 | - * without any read/write operations. | ||
371 | - * | ||
372 | - * @return number if millis of permissible idle time | ||
373 | - */ | ||
374 | - protected int maxIdleMillis() { | ||
375 | - return maxIdleMillis; | ||
376 | - } | ||
377 | - | ||
378 | - | ||
379 | - /** | ||
380 | - * Returns true if the given stream has gone stale. | ||
381 | - * | ||
382 | - * @return true if the stream is stale | ||
383 | - */ | ||
384 | - boolean isStale() { | ||
385 | - return currentTimeMillis() - lastActiveTime > maxIdleMillis() && key != null; | ||
386 | - } | ||
387 | - | ||
388 | - /** | ||
389 | - * Returns the inbound bytes counter. | ||
390 | - * | ||
391 | - * @return inbound bytes counter | ||
392 | - */ | ||
393 | - public Counter bytesIn() { | ||
394 | - return bytesIn; | ||
395 | - } | ||
396 | - | ||
397 | - /** | ||
398 | - * Returns the outbound bytes counter. | ||
399 | - * | ||
400 | - * @return outbound bytes counter | ||
401 | - */ | ||
402 | - public Counter bytesOut() { | ||
403 | - return bytesOut; | ||
404 | - } | ||
405 | - | ||
406 | - /** | ||
407 | - * Returns the inbound messages counter. | ||
408 | - * | ||
409 | - * @return inbound messages counter | ||
410 | - */ | ||
411 | - public Counter messagesIn() { | ||
412 | - return messagesIn; | ||
413 | - } | ||
414 | - | ||
415 | - /** | ||
416 | - * Returns the outbound messages counter. | ||
417 | - * | ||
418 | - * @return outbound messages counter | ||
419 | - */ | ||
420 | - public Counter messagesOut() { | ||
421 | - return messagesOut; | ||
422 | - } | ||
423 | - | ||
424 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.slf4j.Logger; | ||
19 | -import org.slf4j.LoggerFactory; | ||
20 | - | ||
21 | -import java.io.IOException; | ||
22 | -import java.nio.channels.Selector; | ||
23 | - | ||
24 | -import static com.google.common.base.Preconditions.checkArgument; | ||
25 | -import static java.lang.System.currentTimeMillis; | ||
26 | - | ||
27 | -/** | ||
28 | - * Abstraction of an I/O processing loop based on an NIO selector. | ||
29 | - */ | ||
30 | -public abstract class SelectorLoop implements Runnable { | ||
31 | - | ||
32 | - protected final Logger log = LoggerFactory.getLogger(getClass()); | ||
33 | - | ||
34 | - /** | ||
35 | - * Selector used by this loop to pace the I/O operations. | ||
36 | - */ | ||
37 | - protected final Selector selector; | ||
38 | - | ||
39 | - /** | ||
40 | - * Selection operations timeout; specified in millis. | ||
41 | - */ | ||
42 | - protected long selectTimeout; | ||
43 | - | ||
44 | - /** | ||
45 | - * Retains the error that caused the loop to exit prematurely. | ||
46 | - */ | ||
47 | - private Throwable error; | ||
48 | - | ||
49 | - // State indicator | ||
50 | - private enum State { STARTING, STARTED, STOPPING, STOPPED }; | ||
51 | - private volatile State state = State.STOPPED; | ||
52 | - | ||
53 | - /** | ||
54 | - * Creates a new selector loop with the given selection timeout. | ||
55 | - * | ||
56 | - * @param selectTimeout selection timeout; specified in millis | ||
57 | - * @throws IOException if the backing selector cannot be opened | ||
58 | - */ | ||
59 | - public SelectorLoop(long selectTimeout) throws IOException { | ||
60 | - checkArgument(selectTimeout > 0, "Timeout must be positive"); | ||
61 | - this.selectTimeout = selectTimeout; | ||
62 | - this.selector = openSelector(); | ||
63 | - } | ||
64 | - | ||
65 | - /** | ||
66 | - * Opens a new selector for the use by the loop. | ||
67 | - * | ||
68 | - * @return newly open selector | ||
69 | - * @throws IOException if the backing selector cannot be opened | ||
70 | - */ | ||
71 | - protected Selector openSelector() throws IOException { | ||
72 | - return Selector.open(); | ||
73 | - } | ||
74 | - | ||
75 | - /** | ||
76 | - * Indicates that the loop is marked to run. | ||
77 | - * @return true if the loop is marked to run | ||
78 | - */ | ||
79 | - protected boolean isRunning() { | ||
80 | - return state == State.STARTED || state == State.STARTING; | ||
81 | - } | ||
82 | - | ||
83 | - /** | ||
84 | - * Returns the error, if there was one, that caused the loop to terminate | ||
85 | - * prematurely. | ||
86 | - * | ||
87 | - * @return error or null if there was none | ||
88 | - */ | ||
89 | - public Throwable getError() { | ||
90 | - return error; | ||
91 | - } | ||
92 | - | ||
93 | - /** | ||
94 | - * Contains the body of the I/O selector loop. | ||
95 | - * | ||
96 | - * @throws IOException if an error is encountered while selecting I/O | ||
97 | - */ | ||
98 | - protected abstract void loop() throws IOException; | ||
99 | - | ||
100 | - @Override | ||
101 | - public void run() { | ||
102 | - error = null; | ||
103 | - state = State.STARTING; | ||
104 | - try { | ||
105 | - loop(); | ||
106 | - } catch (Exception e) { | ||
107 | - error = e; | ||
108 | - log.error("Loop aborted", e); | ||
109 | - } | ||
110 | - notifyDone(); | ||
111 | - } | ||
112 | - | ||
113 | - /** | ||
114 | - * Notifies observers waiting for loop to become ready. | ||
115 | - */ | ||
116 | - protected synchronized void notifyReady() { | ||
117 | - state = State.STARTED; | ||
118 | - notifyAll(); | ||
119 | - } | ||
120 | - | ||
121 | - /** | ||
122 | - * Triggers loop shutdown. | ||
123 | - */ | ||
124 | - public void shutdown() { | ||
125 | - // Mark the loop as no longer running and wake up the selector. | ||
126 | - state = State.STOPPING; | ||
127 | - selector.wakeup(); | ||
128 | - } | ||
129 | - | ||
130 | - /** | ||
131 | - * Notifies observers waiting for loop to fully stop. | ||
132 | - */ | ||
133 | - private synchronized void notifyDone() { | ||
134 | - state = State.STOPPED; | ||
135 | - notifyAll(); | ||
136 | - } | ||
137 | - | ||
138 | - /** | ||
139 | - * Waits for the loop execution to start. | ||
140 | - * | ||
141 | - * @param timeout number of milliseconds to wait | ||
142 | - * @return true if loop started in time | ||
143 | - */ | ||
144 | - public final synchronized boolean awaitStart(long timeout) { | ||
145 | - long max = currentTimeMillis() + timeout; | ||
146 | - while (state != State.STARTED && (currentTimeMillis() < max)) { | ||
147 | - try { | ||
148 | - wait(timeout); | ||
149 | - } catch (InterruptedException e) { | ||
150 | - throw new RuntimeException("Interrupted", e); | ||
151 | - } | ||
152 | - } | ||
153 | - return state == State.STARTED; | ||
154 | - } | ||
155 | - | ||
156 | - /** | ||
157 | - * Waits for the loop execution to stop. | ||
158 | - * | ||
159 | - * @param timeout number of milliseconds to wait | ||
160 | - * @return true if loop finished in time | ||
161 | - */ | ||
162 | - public final synchronized boolean awaitStop(long timeout) { | ||
163 | - long max = currentTimeMillis() + timeout; | ||
164 | - while (state != State.STOPPED && (currentTimeMillis() < max)) { | ||
165 | - try { | ||
166 | - wait(timeout); | ||
167 | - } catch (InterruptedException e) { | ||
168 | - throw new RuntimeException("Interrupted", e); | ||
169 | - } | ||
170 | - } | ||
171 | - return state == State.STOPPED; | ||
172 | - } | ||
173 | - | ||
174 | - | ||
175 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | - | ||
17 | -/** | ||
18 | - * Mechanism to transfer messages over network using IO loop and | ||
19 | - * message stream, backed by NIO byte buffers. | ||
20 | - */ | ||
21 | -package org.onlab.nio; |
1 | -/* | ||
2 | - * Copyright 2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio.service; | ||
17 | - | ||
18 | -import java.io.IOException; | ||
19 | -import java.nio.channels.ByteChannel; | ||
20 | -import java.nio.channels.SelectionKey; | ||
21 | -import java.util.List; | ||
22 | -import java.util.function.Consumer; | ||
23 | - | ||
24 | -import org.onlab.nio.IOLoop; | ||
25 | -import org.onlab.nio.MessageStream; | ||
26 | - | ||
27 | -/** | ||
28 | - * IOLoop for transporting DefaultMessages. | ||
29 | - */ | ||
30 | -public class DefaultIOLoop extends IOLoop<DefaultMessage, DefaultMessageStream> { | ||
31 | - | ||
32 | - public static final int SELECT_TIMEOUT_MILLIS = 500; | ||
33 | - private static final int MAX_IDLE_TIMEOUT_MILLIS = 1000; | ||
34 | - private static final int BUFFER_SIZE = 1024 * 1024; | ||
35 | - private final Consumer<DefaultMessage> consumer; | ||
36 | - | ||
37 | - public DefaultIOLoop(Consumer<DefaultMessage> consumer) throws IOException { | ||
38 | - this(SELECT_TIMEOUT_MILLIS, consumer); | ||
39 | - } | ||
40 | - | ||
41 | - public DefaultIOLoop(long timeout, Consumer<DefaultMessage> consumer) throws IOException { | ||
42 | - super(timeout); | ||
43 | - this.consumer = consumer; | ||
44 | - } | ||
45 | - | ||
46 | - @Override | ||
47 | - protected DefaultMessageStream createStream(ByteChannel byteChannel) { | ||
48 | - return new DefaultMessageStream(this, byteChannel, BUFFER_SIZE, MAX_IDLE_TIMEOUT_MILLIS); | ||
49 | - } | ||
50 | - | ||
51 | - @Override | ||
52 | - protected void processMessages(List<DefaultMessage> messages, MessageStream<DefaultMessage> stream) { | ||
53 | - messages.forEach(consumer); | ||
54 | - } | ||
55 | - | ||
56 | - @Override | ||
57 | - protected void connect(SelectionKey key) throws IOException { | ||
58 | - DefaultMessageStream stream = (DefaultMessageStream) key.attachment(); | ||
59 | - try { | ||
60 | - super.connect(key); | ||
61 | - stream.connected(); | ||
62 | - } catch (Exception e) { | ||
63 | - stream.connectFailed(e); | ||
64 | - } | ||
65 | - } | ||
66 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -/* | ||
2 | - * Copyright 2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio.service; | ||
17 | - | ||
18 | -import static com.google.common.base.Preconditions.checkNotNull; | ||
19 | - | ||
20 | -import org.onlab.nio.AbstractMessage; | ||
21 | -import org.onlab.packet.IpAddress; | ||
22 | -import org.onlab.util.ByteArraySizeHashPrinter; | ||
23 | -import org.onosproject.store.cluster.messaging.Endpoint; | ||
24 | - | ||
25 | -import com.google.common.base.Charsets; | ||
26 | -import com.google.common.base.MoreObjects; | ||
27 | - | ||
28 | -/** | ||
29 | - * Default message. | ||
30 | - */ | ||
31 | -public class DefaultMessage extends AbstractMessage { | ||
32 | - | ||
33 | - private long id; | ||
34 | - private Endpoint sender; | ||
35 | - private String type; | ||
36 | - private byte[] payload; | ||
37 | - | ||
38 | - /** | ||
39 | - * Creates a new message with the specified data. | ||
40 | - * | ||
41 | - * @param id message id | ||
42 | - * @param type message type | ||
43 | - * @param sender sender endpoint | ||
44 | - * @param payload message payload | ||
45 | - */ | ||
46 | - DefaultMessage(long id, Endpoint sender, String type, byte[] payload) { | ||
47 | - this.id = id; | ||
48 | - this.type = checkNotNull(type, "Type cannot be null"); | ||
49 | - this.sender = checkNotNull(sender, "Sender cannot be null"); | ||
50 | - this.payload = checkNotNull(payload, "Payload cannot be null"); | ||
51 | - | ||
52 | - byte[] messageTypeBytes = type.getBytes(Charsets.UTF_8); | ||
53 | - IpAddress senderIp = sender.host(); | ||
54 | - byte[] ipOctets = senderIp.toOctets(); | ||
55 | - | ||
56 | - length = 25 + ipOctets.length + messageTypeBytes.length + payload.length; | ||
57 | - } | ||
58 | - | ||
59 | - /** | ||
60 | - * Returns message id. | ||
61 | - * | ||
62 | - * @return message id | ||
63 | - */ | ||
64 | - public long id() { | ||
65 | - return id; | ||
66 | - } | ||
67 | - | ||
68 | - /** | ||
69 | - * Returns message sender. | ||
70 | - * | ||
71 | - * @return message sender | ||
72 | - */ | ||
73 | - public Endpoint sender() { | ||
74 | - return sender; | ||
75 | - } | ||
76 | - | ||
77 | - /** | ||
78 | - * Returns message type. | ||
79 | - * | ||
80 | - * @return message type | ||
81 | - */ | ||
82 | - public String type() { | ||
83 | - return type; | ||
84 | - } | ||
85 | - | ||
86 | - /** | ||
87 | - * Returns message payload. | ||
88 | - * | ||
89 | - * @return payload | ||
90 | - */ | ||
91 | - public byte[] payload() { | ||
92 | - return payload; | ||
93 | - } | ||
94 | - | ||
95 | - @Override | ||
96 | - public String toString() { | ||
97 | - return MoreObjects.toStringHelper(this) | ||
98 | - .add("id", id) | ||
99 | - .add("type", type) | ||
100 | - .add("sender", sender) | ||
101 | - .add("payload", ByteArraySizeHashPrinter.of(payload)) | ||
102 | - .toString(); | ||
103 | - } | ||
104 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -/* | ||
2 | - * Copyright 2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio.service; | ||
17 | - | ||
18 | -import java.nio.ByteBuffer; | ||
19 | -import java.nio.channels.ByteChannel; | ||
20 | -import java.util.concurrent.CompletableFuture; | ||
21 | -import java.util.concurrent.atomic.AtomicInteger; | ||
22 | - | ||
23 | -import org.onlab.nio.IOLoop; | ||
24 | -import org.onlab.nio.MessageStream; | ||
25 | -import org.onlab.packet.IpAddress; | ||
26 | -import org.onlab.packet.IpAddress.Version; | ||
27 | -import org.onosproject.store.cluster.messaging.Endpoint; | ||
28 | - | ||
29 | -import com.google.common.base.Charsets; | ||
30 | - | ||
31 | -/** | ||
32 | - * Default bi-directional message stream for transferring messages to & from the | ||
33 | - * network via two byte buffers. | ||
34 | - */ | ||
35 | -public class DefaultMessageStream extends MessageStream<DefaultMessage> { | ||
36 | - | ||
37 | - private final CompletableFuture<Void> connectFuture = new CompletableFuture<>(); | ||
38 | - | ||
39 | - public DefaultMessageStream( | ||
40 | - IOLoop<DefaultMessage, ?> loop, | ||
41 | - ByteChannel byteChannel, | ||
42 | - int bufferSize, | ||
43 | - int maxIdleMillis) { | ||
44 | - super(loop, byteChannel, bufferSize, maxIdleMillis); | ||
45 | - } | ||
46 | - | ||
47 | - public CompletableFuture<DefaultMessageStream> connectedFuture() { | ||
48 | - return connectFuture.thenApply(v -> this); | ||
49 | - } | ||
50 | - | ||
51 | - private final AtomicInteger messageLength = new AtomicInteger(-1); | ||
52 | - | ||
53 | - @Override | ||
54 | - protected DefaultMessage read(ByteBuffer buffer) { | ||
55 | - if (messageLength.get() == -1) { | ||
56 | - // check if we can read the message length. | ||
57 | - if (buffer.remaining() < Integer.BYTES) { | ||
58 | - return null; | ||
59 | - } else { | ||
60 | - messageLength.set(buffer.getInt()); | ||
61 | - } | ||
62 | - } | ||
63 | - | ||
64 | - if (buffer.remaining() < messageLength.get()) { | ||
65 | - return null; | ||
66 | - } | ||
67 | - | ||
68 | - long id = buffer.getLong(); | ||
69 | - Version ipVersion = buffer.get() == 0x0 ? Version.INET : Version.INET6; | ||
70 | - byte[] octects = new byte[IpAddress.byteLength(ipVersion)]; | ||
71 | - buffer.get(octects); | ||
72 | - IpAddress senderIp = IpAddress.valueOf(ipVersion, octects); | ||
73 | - int senderPort = buffer.getInt(); | ||
74 | - int messageTypeByteLength = buffer.getInt(); | ||
75 | - byte[] messageTypeBytes = new byte[messageTypeByteLength]; | ||
76 | - buffer.get(messageTypeBytes); | ||
77 | - String messageType = new String(messageTypeBytes, Charsets.UTF_8); | ||
78 | - int payloadLength = buffer.getInt(); | ||
79 | - byte[] payloadBytes = new byte[payloadLength]; | ||
80 | - buffer.get(payloadBytes); | ||
81 | - | ||
82 | - // reset for next message | ||
83 | - messageLength.set(-1); | ||
84 | - | ||
85 | - return new DefaultMessage(id, new Endpoint(senderIp, senderPort), messageType, payloadBytes); | ||
86 | - } | ||
87 | - | ||
88 | - @Override | ||
89 | - protected void write(DefaultMessage message, ByteBuffer buffer) { | ||
90 | - Endpoint sender = message.sender(); | ||
91 | - byte[] messageTypeBytes = message.type().getBytes(Charsets.UTF_8); | ||
92 | - IpAddress senderIp = sender.host(); | ||
93 | - byte[] ipOctets = senderIp.toOctets(); | ||
94 | - byte[] payload = message.payload(); | ||
95 | - | ||
96 | - int messageLength = 21 + ipOctets.length + messageTypeBytes.length + payload.length; | ||
97 | - | ||
98 | - buffer.putInt(messageLength); | ||
99 | - | ||
100 | - buffer.putLong(message.id()); | ||
101 | - | ||
102 | - if (senderIp.version() == Version.INET) { | ||
103 | - buffer.put((byte) 0x0); | ||
104 | - } else { | ||
105 | - buffer.put((byte) 0x1); | ||
106 | - } | ||
107 | - buffer.put(ipOctets); | ||
108 | - | ||
109 | - // write sender port | ||
110 | - buffer.putInt(sender.port()); | ||
111 | - | ||
112 | - // write length of message type | ||
113 | - buffer.putInt(messageTypeBytes.length); | ||
114 | - | ||
115 | - // write message type bytes | ||
116 | - buffer.put(messageTypeBytes); | ||
117 | - | ||
118 | - // write payload length | ||
119 | - buffer.putInt(payload.length); | ||
120 | - | ||
121 | - // write payload. | ||
122 | - buffer.put(payload); | ||
123 | - } | ||
124 | - | ||
125 | - /** | ||
126 | - * Callback invoked when the stream is successfully connected. | ||
127 | - */ | ||
128 | - public void connected() { | ||
129 | - connectFuture.complete(null); | ||
130 | - } | ||
131 | - | ||
132 | - /** | ||
133 | - * Callback invoked when the stream fails to connect. | ||
134 | - * @param cause failure cause | ||
135 | - */ | ||
136 | - public void connectFailed(Throwable cause) { | ||
137 | - connectFuture.completeExceptionally(cause); | ||
138 | - } | ||
139 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -/* | ||
2 | - * Copyright 2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio.service; | ||
17 | - | ||
18 | -import static org.onlab.util.Tools.groupedThreads; | ||
19 | - | ||
20 | -import java.io.IOException; | ||
21 | -import java.net.InetSocketAddress; | ||
22 | -import java.net.Socket; | ||
23 | -import java.net.SocketAddress; | ||
24 | -import java.nio.channels.ServerSocketChannel; | ||
25 | -import java.nio.channels.SocketChannel; | ||
26 | -import java.util.List; | ||
27 | -import java.util.concurrent.CompletableFuture; | ||
28 | -import java.util.concurrent.ConcurrentHashMap; | ||
29 | -import java.util.concurrent.ConcurrentMap; | ||
30 | -import java.util.concurrent.Executor; | ||
31 | -import java.util.concurrent.ExecutorService; | ||
32 | -import java.util.concurrent.Executors; | ||
33 | -import java.util.concurrent.TimeoutException; | ||
34 | -import java.util.concurrent.atomic.AtomicBoolean; | ||
35 | -import java.util.concurrent.atomic.AtomicLong; | ||
36 | -import java.util.function.BiConsumer; | ||
37 | -import java.util.function.BiFunction; | ||
38 | -import java.util.function.Consumer; | ||
39 | - | ||
40 | -import org.apache.commons.pool.KeyedPoolableObjectFactory; | ||
41 | -import org.apache.commons.pool.impl.GenericKeyedObjectPool; | ||
42 | -import org.onlab.nio.AcceptorLoop; | ||
43 | -import org.onlab.nio.SelectorLoop; | ||
44 | -import org.onosproject.store.cluster.messaging.Endpoint; | ||
45 | -import org.onosproject.store.cluster.messaging.MessagingService; | ||
46 | -import org.slf4j.Logger; | ||
47 | -import org.slf4j.LoggerFactory; | ||
48 | - | ||
49 | -import com.google.common.cache.Cache; | ||
50 | -import com.google.common.cache.CacheBuilder; | ||
51 | -import com.google.common.cache.RemovalListener; | ||
52 | -import com.google.common.cache.RemovalNotification; | ||
53 | -import com.google.common.collect.Lists; | ||
54 | -import com.google.common.util.concurrent.MoreExecutors; | ||
55 | - | ||
56 | -/** | ||
57 | - * MessagingService implementation based on IOLoop. | ||
58 | - */ | ||
59 | -public class IOLoopMessaging implements MessagingService { | ||
60 | - | ||
61 | - private final Logger log = LoggerFactory.getLogger(getClass()); | ||
62 | - | ||
63 | - private static final String REPLY_MESSAGE_TYPE = "ONOS_REQUEST_REPLY"; | ||
64 | - | ||
65 | - static final long TIMEOUT = 1000; | ||
66 | - | ||
67 | - static final boolean SO_NO_DELAY = false; | ||
68 | - static final int SO_SEND_BUFFER_SIZE = 128 * 1024; | ||
69 | - static final int SO_RCV_BUFFER_SIZE = 128 * 1024; | ||
70 | - | ||
71 | - private static final int NUM_WORKERS = 8; | ||
72 | - | ||
73 | - private AcceptorLoop acceptorLoop; | ||
74 | - private final ExecutorService acceptorThreadPool = | ||
75 | - Executors.newSingleThreadExecutor(groupedThreads("onos/nio/messaging", "acceptor")); | ||
76 | - private final ExecutorService ioThreadPool = | ||
77 | - Executors.newFixedThreadPool(NUM_WORKERS, groupedThreads("onos/nio/messaging", "io-loop-worker-%d")); | ||
78 | - | ||
79 | - private final List<DefaultIOLoop> ioLoops = Lists.newArrayList(); | ||
80 | - | ||
81 | - private int lastWorker = -1; | ||
82 | - | ||
83 | - private final AtomicBoolean started = new AtomicBoolean(false); | ||
84 | - private Endpoint localEp; | ||
85 | - | ||
86 | - private GenericKeyedObjectPool<Endpoint, DefaultMessageStream> streams = | ||
87 | - new GenericKeyedObjectPool<>(new DefaultMessageStreamFactory()); | ||
88 | - | ||
89 | - private final ConcurrentMap<String, Consumer<DefaultMessage>> handlers = new ConcurrentHashMap<>(); | ||
90 | - private final AtomicLong messageIdGenerator = new AtomicLong(0); | ||
91 | - private final Cache<Long, Callback> responseFutures = CacheBuilder.newBuilder() | ||
92 | - .removalListener(new RemovalListener<Long, Callback>() { | ||
93 | - @Override | ||
94 | - public void onRemoval(RemovalNotification<Long, Callback> entry) { | ||
95 | - if (entry.wasEvicted()) { | ||
96 | - entry.getValue().completeExceptionally(new TimeoutException("Timedout waiting for reply")); | ||
97 | - } | ||
98 | - } | ||
99 | - }) | ||
100 | - .build(); | ||
101 | - | ||
102 | - /** | ||
103 | - * Activates IO Loops. | ||
104 | - * | ||
105 | - * @param localEp local end-point | ||
106 | - * @throws IOException is activation fails | ||
107 | - */ | ||
108 | - public void start(Endpoint localEp) throws IOException { | ||
109 | - if (started.get()) { | ||
110 | - log.warn("IOMessaging is already running at {}", localEp); | ||
111 | - return; | ||
112 | - } | ||
113 | - this.localEp = localEp; | ||
114 | - streams.setLifo(false); | ||
115 | - this.acceptorLoop = new DefaultAcceptorLoop(new InetSocketAddress(localEp.host().toString(), localEp.port())); | ||
116 | - | ||
117 | - for (int i = 0; i < NUM_WORKERS; i++) { | ||
118 | - ioLoops.add(new DefaultIOLoop(this::dispatchLocally)); | ||
119 | - } | ||
120 | - | ||
121 | - ioLoops.forEach(ioThreadPool::execute); | ||
122 | - acceptorThreadPool.execute(acceptorLoop); | ||
123 | - ioLoops.forEach(loop -> loop.awaitStart(TIMEOUT)); | ||
124 | - acceptorLoop.awaitStart(TIMEOUT); | ||
125 | - started.set(true); | ||
126 | - } | ||
127 | - | ||
128 | - /** | ||
129 | - * Shuts down IO loops. | ||
130 | - */ | ||
131 | - public void stop() { | ||
132 | - if (started.get()) { | ||
133 | - ioLoops.forEach(SelectorLoop::shutdown); | ||
134 | - acceptorLoop.shutdown(); | ||
135 | - ioThreadPool.shutdown(); | ||
136 | - acceptorThreadPool.shutdown(); | ||
137 | - started.set(false); | ||
138 | - } | ||
139 | - } | ||
140 | - | ||
141 | - | ||
142 | - @Override | ||
143 | - public CompletableFuture<Void> sendAsync(Endpoint ep, String type, byte[] payload) { | ||
144 | - DefaultMessage message = new DefaultMessage( | ||
145 | - messageIdGenerator.incrementAndGet(), | ||
146 | - localEp, | ||
147 | - type, | ||
148 | - payload); | ||
149 | - return sendAsync(ep, message); | ||
150 | - } | ||
151 | - | ||
152 | - protected CompletableFuture<Void> sendAsync(Endpoint ep, DefaultMessage message) { | ||
153 | - CompletableFuture<Void> future = new CompletableFuture<>(); | ||
154 | - if (ep.equals(localEp)) { | ||
155 | - dispatchLocally(message); | ||
156 | - future.complete(null); | ||
157 | - return future; | ||
158 | - } | ||
159 | - | ||
160 | - DefaultMessageStream stream = null; | ||
161 | - try { | ||
162 | - stream = streams.borrowObject(ep); | ||
163 | - stream.write(message); | ||
164 | - future.complete(null); | ||
165 | - } catch (Exception e) { | ||
166 | - future.completeExceptionally(e); | ||
167 | - } finally { | ||
168 | - try { | ||
169 | - streams.returnObject(ep, stream); | ||
170 | - } catch (Exception e) { | ||
171 | - log.warn("Failed to return stream to pool"); | ||
172 | - } | ||
173 | - } | ||
174 | - return future; | ||
175 | - } | ||
176 | - | ||
177 | - @Override | ||
178 | - public CompletableFuture<byte[]> sendAndReceive( | ||
179 | - Endpoint ep, | ||
180 | - String type, | ||
181 | - byte[] payload, | ||
182 | - Executor executor) { | ||
183 | - CompletableFuture<byte[]> response = new CompletableFuture<>(); | ||
184 | - Callback callback = new Callback(response, executor); | ||
185 | - Long messageId = messageIdGenerator.incrementAndGet(); | ||
186 | - responseFutures.put(messageId, callback); | ||
187 | - DefaultMessage message = new DefaultMessage(messageId, localEp, type, payload); | ||
188 | - return sendAsync(ep, message).whenComplete((r, e) -> { | ||
189 | - if (e != null) { | ||
190 | - responseFutures.invalidate(messageId); | ||
191 | - } | ||
192 | - }).thenCompose(v -> response); | ||
193 | - } | ||
194 | - | ||
195 | - @Override | ||
196 | - public CompletableFuture<byte[]> sendAndReceive( | ||
197 | - Endpoint ep, | ||
198 | - String type, | ||
199 | - byte[] payload) { | ||
200 | - return sendAndReceive(ep, type, payload, MoreExecutors.directExecutor()); | ||
201 | - } | ||
202 | - | ||
203 | - @Override | ||
204 | - public void registerHandler(String type, BiConsumer<Endpoint, byte[]> handler, Executor executor) { | ||
205 | - handlers.put(type, message -> executor.execute(() -> handler.accept(message.sender(), message.payload()))); | ||
206 | - } | ||
207 | - | ||
208 | - @Override | ||
209 | - public void registerHandler(String type, BiFunction<Endpoint, byte[], byte[]> handler, Executor executor) { | ||
210 | - handlers.put(type, message -> executor.execute(() -> { | ||
211 | - byte[] responsePayload = handler.apply(message.sender(), message.payload()); | ||
212 | - if (responsePayload != null) { | ||
213 | - DefaultMessage response = new DefaultMessage(message.id(), | ||
214 | - localEp, | ||
215 | - REPLY_MESSAGE_TYPE, | ||
216 | - responsePayload); | ||
217 | - sendAsync(message.sender(), response).whenComplete((result, error) -> { | ||
218 | - log.debug("Failed to respond", error); | ||
219 | - }); | ||
220 | - } | ||
221 | - })); | ||
222 | - } | ||
223 | - | ||
224 | - @Override | ||
225 | - public void registerHandler(String type, BiFunction<Endpoint, byte[], CompletableFuture<byte[]>> handler) { | ||
226 | - handlers.put(type, message -> { | ||
227 | - handler.apply(message.sender(), message.payload()).whenComplete((result, error) -> { | ||
228 | - if (error == null) { | ||
229 | - DefaultMessage response = new DefaultMessage(message.id(), | ||
230 | - localEp, | ||
231 | - REPLY_MESSAGE_TYPE, | ||
232 | - result); | ||
233 | - sendAsync(message.sender(), response).whenComplete((r, e) -> { | ||
234 | - if (e != null) { | ||
235 | - log.debug("Failed to respond", e); | ||
236 | - } | ||
237 | - }); | ||
238 | - } | ||
239 | - }); | ||
240 | - }); | ||
241 | - } | ||
242 | - | ||
243 | - @Override | ||
244 | - public void unregisterHandler(String type) { | ||
245 | - handlers.remove(type); | ||
246 | - } | ||
247 | - | ||
248 | - protected void dispatchLocally(DefaultMessage message) { | ||
249 | - String type = message.type(); | ||
250 | - if (REPLY_MESSAGE_TYPE.equals(type)) { | ||
251 | - try { | ||
252 | - Callback callback = | ||
253 | - responseFutures.getIfPresent(message.id()); | ||
254 | - if (callback != null) { | ||
255 | - callback.complete(message.payload()); | ||
256 | - } else { | ||
257 | - log.warn("Received a reply for message id:[{}]. " | ||
258 | - + " from {}. But was unable to locate the" | ||
259 | - + " request handle", message.id(), message.sender()); | ||
260 | - } | ||
261 | - } finally { | ||
262 | - responseFutures.invalidate(message.id()); | ||
263 | - } | ||
264 | - return; | ||
265 | - } | ||
266 | - Consumer<DefaultMessage> handler = handlers.get(type); | ||
267 | - if (handler != null) { | ||
268 | - handler.accept(message); | ||
269 | - } else { | ||
270 | - log.debug("No handler registered for {}", type); | ||
271 | - } | ||
272 | - } | ||
273 | - | ||
274 | - // Get the next worker to which a client should be assigned | ||
275 | - private synchronized DefaultIOLoop nextWorker() { | ||
276 | - lastWorker = (lastWorker + 1) % NUM_WORKERS; | ||
277 | - return ioLoops.get(lastWorker); | ||
278 | - } | ||
279 | - | ||
280 | - /** | ||
281 | - * Initiates open connection request and registers the pending socket | ||
282 | - * channel with the given IO loop. | ||
283 | - * | ||
284 | - * @param loop loop with which the channel should be registered | ||
285 | - * @throws java.io.IOException if the socket could not be open or connected | ||
286 | - */ | ||
287 | - private DefaultMessageStream createConnection(Endpoint ep, DefaultIOLoop loop) throws IOException { | ||
288 | - SocketAddress sa = new InetSocketAddress(ep.host().toString(), ep.port()); | ||
289 | - SocketChannel ch = SocketChannel.open(); | ||
290 | - ch.configureBlocking(false); | ||
291 | - DefaultMessageStream stream = loop.connectStream(ch); | ||
292 | - ch.connect(sa); | ||
293 | - return stream; | ||
294 | - } | ||
295 | - | ||
296 | - // Loop for accepting client connections | ||
297 | - private class DefaultAcceptorLoop extends AcceptorLoop { | ||
298 | - | ||
299 | - public DefaultAcceptorLoop(SocketAddress address) throws IOException { | ||
300 | - super(DefaultIOLoop.SELECT_TIMEOUT_MILLIS, address); | ||
301 | - } | ||
302 | - | ||
303 | - @Override | ||
304 | - protected void acceptConnection(ServerSocketChannel channel) throws IOException { | ||
305 | - SocketChannel sc = channel.accept(); | ||
306 | - sc.configureBlocking(false); | ||
307 | - | ||
308 | - Socket so = sc.socket(); | ||
309 | - so.setTcpNoDelay(SO_NO_DELAY); | ||
310 | - so.setReceiveBufferSize(SO_RCV_BUFFER_SIZE); | ||
311 | - so.setSendBufferSize(SO_SEND_BUFFER_SIZE); | ||
312 | - | ||
313 | - nextWorker().acceptStream(sc); | ||
314 | - } | ||
315 | - } | ||
316 | - | ||
317 | - private class DefaultMessageStreamFactory implements KeyedPoolableObjectFactory<Endpoint, DefaultMessageStream> { | ||
318 | - | ||
319 | - @Override | ||
320 | - public void activateObject(Endpoint endpoint, DefaultMessageStream stream) throws Exception { | ||
321 | - } | ||
322 | - | ||
323 | - @Override | ||
324 | - public void destroyObject(Endpoint ep, DefaultMessageStream stream) throws Exception { | ||
325 | - stream.close(); | ||
326 | - } | ||
327 | - | ||
328 | - @Override | ||
329 | - public DefaultMessageStream makeObject(Endpoint ep) throws Exception { | ||
330 | - DefaultMessageStream stream = createConnection(ep, nextWorker()).connectedFuture().get(); | ||
331 | - log.info("Established a new connection to {}", ep); | ||
332 | - return stream; | ||
333 | - } | ||
334 | - | ||
335 | - @Override | ||
336 | - public void passivateObject(Endpoint ep, DefaultMessageStream stream) throws Exception { | ||
337 | - } | ||
338 | - | ||
339 | - @Override | ||
340 | - public boolean validateObject(Endpoint ep, DefaultMessageStream stream) { | ||
341 | - return stream.isClosed(); | ||
342 | - } | ||
343 | - } | ||
344 | - | ||
345 | - | ||
346 | - private final class Callback { | ||
347 | - private final CompletableFuture<byte[]> future; | ||
348 | - private final Executor executor; | ||
349 | - | ||
350 | - public Callback(CompletableFuture<byte[]> future, Executor executor) { | ||
351 | - this.future = future; | ||
352 | - this.executor = executor; | ||
353 | - } | ||
354 | - | ||
355 | - public void complete(byte[] value) { | ||
356 | - executor.execute(() -> future.complete(value)); | ||
357 | - } | ||
358 | - | ||
359 | - public void completeExceptionally(Throwable error) { | ||
360 | - executor.execute(() -> future.completeExceptionally(error)); | ||
361 | - } | ||
362 | - } | ||
363 | -} |
1 | -/* | ||
2 | - * Copyright 2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | - | ||
17 | -/** | ||
18 | - * Assembly for sending and receiving messages using the I/O loop mechanism. | ||
19 | - */ | ||
20 | -package org.onlab.nio.service; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.junit.Before; | ||
19 | - | ||
20 | -import java.util.concurrent.CountDownLatch; | ||
21 | -import java.util.concurrent.ExecutorService; | ||
22 | -import java.util.concurrent.TimeUnit; | ||
23 | - | ||
24 | -import static java.util.concurrent.Executors.newSingleThreadExecutor; | ||
25 | -import static org.junit.Assert.fail; | ||
26 | -import static org.onlab.util.Tools.namedThreads; | ||
27 | - | ||
28 | -/** | ||
29 | - * Base class for various NIO loop unit tests. | ||
30 | - */ | ||
31 | -public abstract class AbstractLoopTest { | ||
32 | - | ||
33 | - protected static final long MAX_MS_WAIT = 1500; | ||
34 | - | ||
35 | - /** Block on specified countdown latch. Return when countdown reaches | ||
36 | - * zero, or fail the test if the {@value #MAX_MS_WAIT} ms timeout expires. | ||
37 | - * | ||
38 | - * @param latch the latch | ||
39 | - * @param label an identifying label | ||
40 | - */ | ||
41 | - protected void waitForLatch(CountDownLatch latch, String label) { | ||
42 | - try { | ||
43 | - boolean ok = latch.await(MAX_MS_WAIT, TimeUnit.MILLISECONDS); | ||
44 | - if (!ok) { | ||
45 | - fail("Latch await timeout! [" + label + "]"); | ||
46 | - } | ||
47 | - } catch (InterruptedException e) { | ||
48 | - System.out.println("Latch interrupt [" + label + "] : " + e); | ||
49 | - fail("Unexpected interrupt"); | ||
50 | - } | ||
51 | - } | ||
52 | - | ||
53 | - protected ExecutorService exec; | ||
54 | - | ||
55 | - @Before | ||
56 | - public void setUp() { | ||
57 | - exec = newSingleThreadExecutor(namedThreads("test")); | ||
58 | - } | ||
59 | - | ||
60 | -} |
1 | -/* | ||
2 | - * Copyright 2014-2015 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.junit.Test; | ||
19 | - | ||
20 | -import java.io.IOException; | ||
21 | -import java.net.InetSocketAddress; | ||
22 | -import java.net.SocketAddress; | ||
23 | -import java.nio.channels.ServerSocketChannel; | ||
24 | -import java.util.concurrent.CountDownLatch; | ||
25 | - | ||
26 | -import static org.junit.Assert.assertEquals; | ||
27 | -import static org.onlab.junit.TestTools.delay; | ||
28 | - | ||
29 | -/** | ||
30 | - * Unit tests for AcceptLoop. | ||
31 | - */ | ||
32 | -public class AcceptorLoopTest extends AbstractLoopTest { | ||
33 | - | ||
34 | - private static final int PICK_EPHEMERAL = 0; | ||
35 | - | ||
36 | - private static final SocketAddress SOCK_ADDR = new InetSocketAddress("127.0.0.1", PICK_EPHEMERAL); | ||
37 | - | ||
38 | - private static class MyAcceptLoop extends AcceptorLoop { | ||
39 | - private final CountDownLatch loopStarted = new CountDownLatch(1); | ||
40 | - private final CountDownLatch loopFinished = new CountDownLatch(1); | ||
41 | - private final CountDownLatch runDone = new CountDownLatch(1); | ||
42 | - private final CountDownLatch ceaseLatch = new CountDownLatch(1); | ||
43 | - | ||
44 | - private int acceptCount = 0; | ||
45 | - | ||
46 | - MyAcceptLoop() throws IOException { | ||
47 | - super(500, SOCK_ADDR); | ||
48 | - } | ||
49 | - | ||
50 | - @Override | ||
51 | - protected void acceptConnection(ServerSocketChannel ssc) throws IOException { | ||
52 | - acceptCount++; | ||
53 | - } | ||
54 | - | ||
55 | - @Override | ||
56 | - public void loop() throws IOException { | ||
57 | - loopStarted.countDown(); | ||
58 | - super.loop(); | ||
59 | - loopFinished.countDown(); | ||
60 | - } | ||
61 | - | ||
62 | - @Override | ||
63 | - public void run() { | ||
64 | - super.run(); | ||
65 | - runDone.countDown(); | ||
66 | - } | ||
67 | - | ||
68 | - @Override | ||
69 | - public void shutdown() { | ||
70 | - super.shutdown(); | ||
71 | - ceaseLatch.countDown(); | ||
72 | - } | ||
73 | - } | ||
74 | - | ||
75 | - @Test | ||
76 | - public void basic() throws IOException { | ||
77 | - MyAcceptLoop myAccLoop = new MyAcceptLoop(); | ||
78 | - AcceptorLoop accLoop = myAccLoop; | ||
79 | - exec.execute(accLoop); | ||
80 | - waitForLatch(myAccLoop.loopStarted, "loopStarted"); | ||
81 | - delay(200); // take a quick nap | ||
82 | - accLoop.shutdown(); | ||
83 | - waitForLatch(myAccLoop.loopFinished, "loopFinished"); | ||
84 | - waitForLatch(myAccLoop.runDone, "runDone"); | ||
85 | - assertEquals(0, myAccLoop.acceptCount); | ||
86 | - } | ||
87 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.junit.Before; | ||
19 | -import org.junit.Ignore; | ||
20 | -import org.junit.Test; | ||
21 | - | ||
22 | -import java.net.InetAddress; | ||
23 | -import java.util.Random; | ||
24 | -import java.util.logging.Level; | ||
25 | -import java.util.logging.Logger; | ||
26 | - | ||
27 | -import static org.onlab.junit.TestTools.delay; | ||
28 | - | ||
29 | -/** | ||
30 | - * Integration test for the select, accept and IO loops. | ||
31 | - */ | ||
32 | -public class IOLoopIntegrationTest { | ||
33 | - | ||
34 | - private static final int THREADS = 6; | ||
35 | - private static final int TIMEOUT = 60; | ||
36 | - private static final int MESSAGE_LENGTH = 128; | ||
37 | - | ||
38 | - private static final int MILLION = 1000000; | ||
39 | - private static final int MSG_COUNT = 40 * MILLION; | ||
40 | - | ||
41 | - @Before | ||
42 | - public void warmUp() throws Exception { | ||
43 | - Logger.getLogger("").setLevel(Level.SEVERE); | ||
44 | - try { | ||
45 | - runTest(MILLION, MESSAGE_LENGTH, 15); | ||
46 | - } catch (Throwable e) { | ||
47 | - System.err.println("Failed warmup but moving on."); | ||
48 | - e.printStackTrace(); | ||
49 | - } | ||
50 | - } | ||
51 | - | ||
52 | - // TODO: this test can not pass in some environments, need to be improved | ||
53 | - @Ignore | ||
54 | - @Test | ||
55 | - public void basic() throws Exception { | ||
56 | - runTest(MILLION, MESSAGE_LENGTH, TIMEOUT); | ||
57 | - } | ||
58 | - | ||
59 | - public void longHaul() throws Exception { | ||
60 | - runTest(MSG_COUNT, MESSAGE_LENGTH, TIMEOUT); | ||
61 | - } | ||
62 | - | ||
63 | - private void runTest(int count, int size, int timeout) throws Exception { | ||
64 | - // Use a random port to prevent conflicts. | ||
65 | - int port = IOLoopTestServer.PORT + new Random().nextInt(100); | ||
66 | - | ||
67 | - InetAddress ip = InetAddress.getLoopbackAddress(); | ||
68 | - IOLoopTestServer server = new IOLoopTestServer(ip, THREADS, size, port); | ||
69 | - IOLoopTestClient client = new IOLoopTestClient(ip, THREADS, count, size, port); | ||
70 | - | ||
71 | - server.start(); | ||
72 | - client.start(); | ||
73 | - delay(100); // Pause to allow loops to get going | ||
74 | - | ||
75 | - client.await(timeout); | ||
76 | - client.report(); | ||
77 | - | ||
78 | - server.stop(); | ||
79 | - server.report(); | ||
80 | - } | ||
81 | - | ||
82 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import com.google.common.collect.Lists; | ||
19 | -import org.onlab.util.Counter; | ||
20 | -import org.slf4j.Logger; | ||
21 | -import org.slf4j.LoggerFactory; | ||
22 | - | ||
23 | -import java.io.IOException; | ||
24 | -import java.net.InetAddress; | ||
25 | -import java.net.InetSocketAddress; | ||
26 | -import java.net.SocketAddress; | ||
27 | -import java.nio.channels.ByteChannel; | ||
28 | -import java.nio.channels.SelectionKey; | ||
29 | -import java.nio.channels.SocketChannel; | ||
30 | -import java.text.DecimalFormat; | ||
31 | -import java.util.ArrayList; | ||
32 | -import java.util.List; | ||
33 | -import java.util.concurrent.ExecutionException; | ||
34 | -import java.util.concurrent.ExecutorService; | ||
35 | -import java.util.concurrent.Executors; | ||
36 | -import java.util.concurrent.FutureTask; | ||
37 | -import java.util.concurrent.Semaphore; | ||
38 | -import java.util.concurrent.TimeUnit; | ||
39 | -import java.util.concurrent.TimeoutException; | ||
40 | - | ||
41 | -import static java.lang.String.format; | ||
42 | -import static java.lang.System.nanoTime; | ||
43 | -import static java.lang.System.out; | ||
44 | -import static org.onlab.nio.IOLoopTestServer.PORT; | ||
45 | -import static org.onlab.util.Tools.delay; | ||
46 | -import static org.onlab.util.Tools.namedThreads; | ||
47 | - | ||
48 | -/** | ||
49 | - * Auxiliary test fixture to measure speed of NIO-based channels. | ||
50 | - */ | ||
51 | -public class IOLoopTestClient { | ||
52 | - | ||
53 | - private static Logger log = LoggerFactory.getLogger(IOLoopTestClient.class); | ||
54 | - | ||
55 | - private final InetAddress ip; | ||
56 | - private final int port; | ||
57 | - private final int msgCount; | ||
58 | - private final int msgLength; | ||
59 | - | ||
60 | - private final List<CustomIOLoop> iloops = new ArrayList<>(); | ||
61 | - private final ExecutorService ipool; | ||
62 | - private final ExecutorService wpool; | ||
63 | - | ||
64 | - Counter messages; | ||
65 | - Counter bytes; | ||
66 | - long latencyTotal = 0; | ||
67 | - long latencyCount = 0; | ||
68 | - | ||
69 | - | ||
70 | - /** | ||
71 | - * Main entry point to launch the client. | ||
72 | - * | ||
73 | - * @param args command-line arguments | ||
74 | - * @throws java.io.IOException if unable to connect to server | ||
75 | - * @throws InterruptedException if latch wait gets interrupted | ||
76 | - * @throws java.util.concurrent.ExecutionException if wait gets interrupted | ||
77 | - * @throws java.util.concurrent.TimeoutException if timeout occurred while waiting for completion | ||
78 | - */ | ||
79 | - public static void main(String[] args) | ||
80 | - throws IOException, InterruptedException, ExecutionException, TimeoutException { | ||
81 | - startStandalone(args); | ||
82 | - | ||
83 | - System.exit(0); | ||
84 | - } | ||
85 | - | ||
86 | - /** | ||
87 | - * Starts a standalone IO loop test client. | ||
88 | - * | ||
89 | - * @param args command-line arguments | ||
90 | - */ | ||
91 | - public static void startStandalone(String[] args) | ||
92 | - throws IOException, InterruptedException, ExecutionException, TimeoutException { | ||
93 | - InetAddress ip = InetAddress.getByName(args.length > 0 ? args[0] : "127.0.0.1"); | ||
94 | - int wc = args.length > 1 ? Integer.parseInt(args[1]) : 6; | ||
95 | - int mc = args.length > 2 ? Integer.parseInt(args[2]) : 50 * 1000000; | ||
96 | - int ml = args.length > 3 ? Integer.parseInt(args[3]) : 128; | ||
97 | - int to = args.length > 4 ? Integer.parseInt(args[4]) : 60; | ||
98 | - | ||
99 | - log.info("Setting up client with {} workers sending {} {}-byte messages to {} server... ", | ||
100 | - wc, mc, ml, ip); | ||
101 | - IOLoopTestClient client = new IOLoopTestClient(ip, wc, mc, ml, PORT); | ||
102 | - | ||
103 | - client.start(); | ||
104 | - delay(500); | ||
105 | - | ||
106 | - client.await(to); | ||
107 | - client.report(); | ||
108 | - } | ||
109 | - | ||
110 | - /** | ||
111 | - * Creates a speed client. | ||
112 | - * | ||
113 | - * @param ip ip address of server | ||
114 | - * @param wc worker count | ||
115 | - * @param mc message count to send per client | ||
116 | - * @param ml message length in bytes | ||
117 | - * @param port socket port | ||
118 | - * @throws java.io.IOException if unable to create IO loops | ||
119 | - */ | ||
120 | - public IOLoopTestClient(InetAddress ip, int wc, int mc, int ml, int port) throws IOException { | ||
121 | - this.ip = ip; | ||
122 | - this.port = port; | ||
123 | - this.msgCount = mc; | ||
124 | - this.msgLength = ml; | ||
125 | - this.wpool = Executors.newFixedThreadPool(wc, namedThreads("worker")); | ||
126 | - this.ipool = Executors.newFixedThreadPool(wc, namedThreads("io-loop")); | ||
127 | - | ||
128 | - for (int i = 0; i < wc; i++) { | ||
129 | - iloops.add(new CustomIOLoop()); | ||
130 | - } | ||
131 | - } | ||
132 | - | ||
133 | - /** | ||
134 | - * Starts the client workers. | ||
135 | - * | ||
136 | - * @throws java.io.IOException if unable to open connection | ||
137 | - */ | ||
138 | - public void start() throws IOException { | ||
139 | - messages = new Counter(); | ||
140 | - bytes = new Counter(); | ||
141 | - | ||
142 | - // First start up all the IO loops | ||
143 | - for (CustomIOLoop l : iloops) { | ||
144 | - ipool.execute(l); | ||
145 | - } | ||
146 | - | ||
147 | - // Wait for all of them to get going | ||
148 | - for (CustomIOLoop l : iloops) { | ||
149 | - l.awaitStart(1000); | ||
150 | - } | ||
151 | - | ||
152 | - // ... and Next open all connections; one-per-loop | ||
153 | - for (CustomIOLoop l : iloops) { | ||
154 | - openConnection(l); | ||
155 | - } | ||
156 | - } | ||
157 | - | ||
158 | - | ||
159 | - /** | ||
160 | - * Initiates open connection request and registers the pending socket | ||
161 | - * channel with the given IO loop. | ||
162 | - * | ||
163 | - * @param loop loop with which the channel should be registered | ||
164 | - * @throws java.io.IOException if the socket could not be open or connected | ||
165 | - */ | ||
166 | - private void openConnection(CustomIOLoop loop) throws IOException { | ||
167 | - SocketAddress sa = new InetSocketAddress(ip, port); | ||
168 | - SocketChannel ch = SocketChannel.open(); | ||
169 | - ch.configureBlocking(false); | ||
170 | - loop.connectStream(ch); | ||
171 | - ch.connect(sa); | ||
172 | - } | ||
173 | - | ||
174 | - | ||
175 | - /** | ||
176 | - * Waits for the client workers to complete. | ||
177 | - * | ||
178 | - * @param secs timeout in seconds | ||
179 | - * @throws java.util.concurrent.ExecutionException if execution failed | ||
180 | - * @throws InterruptedException if interrupt occurred while waiting | ||
181 | - * @throws java.util.concurrent.TimeoutException if timeout occurred | ||
182 | - */ | ||
183 | - public void await(int secs) throws InterruptedException, | ||
184 | - ExecutionException, TimeoutException { | ||
185 | - for (CustomIOLoop l : iloops) { | ||
186 | - if (l.worker.task != null) { | ||
187 | - l.worker.task.get(secs, TimeUnit.SECONDS); | ||
188 | - latencyTotal += l.latencyTotal; | ||
189 | - latencyCount += l.latencyCount; | ||
190 | - } | ||
191 | - } | ||
192 | - messages.freeze(); | ||
193 | - bytes.freeze(); | ||
194 | - } | ||
195 | - | ||
196 | - /** | ||
197 | - * Reports on the accumulated throughput and latency. | ||
198 | - */ | ||
199 | - public void report() { | ||
200 | - DecimalFormat f = new DecimalFormat("#,##0"); | ||
201 | - out.println(format("Client: %s messages; %s bytes; %s mps; %s MBs; %s ns latency", | ||
202 | - f.format(messages.total()), f.format(bytes.total()), | ||
203 | - f.format(messages.throughput()), | ||
204 | - f.format(bytes.throughput() / (1024 * msgLength)), | ||
205 | - f.format(latencyTotal / latencyCount))); | ||
206 | - } | ||
207 | - | ||
208 | - | ||
209 | - // Loop for transfer of fixed-length messages | ||
210 | - private class CustomIOLoop extends IOLoop<TestMessage, TestMessageStream> { | ||
211 | - | ||
212 | - Worker worker = new Worker(); | ||
213 | - long latencyTotal = 0; | ||
214 | - long latencyCount = 0; | ||
215 | - | ||
216 | - | ||
217 | - public CustomIOLoop() throws IOException { | ||
218 | - super(500); | ||
219 | - } | ||
220 | - | ||
221 | - | ||
222 | - @Override | ||
223 | - protected TestMessageStream createStream(ByteChannel channel) { | ||
224 | - return new TestMessageStream(msgLength, channel, this); | ||
225 | - } | ||
226 | - | ||
227 | - @Override | ||
228 | - protected synchronized void removeStream(MessageStream<TestMessage> stream) { | ||
229 | - super.removeStream(stream); | ||
230 | - messages.add(stream.messagesIn().total()); | ||
231 | - bytes.add(stream.bytesIn().total()); | ||
232 | - stream.messagesOut().reset(); | ||
233 | - stream.bytesOut().reset(); | ||
234 | - } | ||
235 | - | ||
236 | - @Override | ||
237 | - protected void processMessages(List<TestMessage> messages, | ||
238 | - MessageStream<TestMessage> stream) { | ||
239 | - for (TestMessage message : messages) { | ||
240 | - // TODO: summarize latency data better | ||
241 | - latencyTotal += nanoTime() - message.requestorTime(); | ||
242 | - latencyCount++; | ||
243 | - } | ||
244 | - worker.release(messages.size()); | ||
245 | - } | ||
246 | - | ||
247 | - @Override | ||
248 | - protected void connect(SelectionKey key) throws IOException { | ||
249 | - super.connect(key); | ||
250 | - TestMessageStream b = (TestMessageStream) key.attachment(); | ||
251 | - Worker w = ((CustomIOLoop) b.loop()).worker; | ||
252 | - w.pump(b); | ||
253 | - } | ||
254 | - | ||
255 | - } | ||
256 | - | ||
257 | - /** | ||
258 | - * Auxiliary worker to connect and pump batched messages using blocking I/O. | ||
259 | - */ | ||
260 | - private class Worker implements Runnable { | ||
261 | - | ||
262 | - private static final int BATCH_SIZE = 50; | ||
263 | - private static final int PERMITS = 2 * BATCH_SIZE; | ||
264 | - | ||
265 | - private TestMessageStream stream; | ||
266 | - private FutureTask<Worker> task; | ||
267 | - | ||
268 | - // Stuff to throttle pump | ||
269 | - private final Semaphore semaphore = new Semaphore(PERMITS); | ||
270 | - private int msgWritten; | ||
271 | - | ||
272 | - void pump(TestMessageStream stream) { | ||
273 | - this.stream = stream; | ||
274 | - task = new FutureTask<>(this, this); | ||
275 | - wpool.execute(task); | ||
276 | - } | ||
277 | - | ||
278 | - @Override | ||
279 | - public void run() { | ||
280 | - try { | ||
281 | - log.info("Worker started..."); | ||
282 | - | ||
283 | - while (msgWritten < msgCount) { | ||
284 | - int size = Math.min(BATCH_SIZE, msgCount - msgWritten); | ||
285 | - writeBatch(size); | ||
286 | - msgWritten += size; | ||
287 | - } | ||
288 | - | ||
289 | - // Now try to get all the permits back before sending poison pill | ||
290 | - semaphore.acquireUninterruptibly(PERMITS); | ||
291 | - stream.close(); | ||
292 | - | ||
293 | - log.info("Worker done..."); | ||
294 | - | ||
295 | - } catch (IOException e) { | ||
296 | - log.error("Worker unable to perform I/O", e); | ||
297 | - } | ||
298 | - } | ||
299 | - | ||
300 | - | ||
301 | - private void writeBatch(int size) throws IOException { | ||
302 | - // Build a batch of messages | ||
303 | - List<TestMessage> batch = Lists.newArrayListWithCapacity(size); | ||
304 | - for (int i = 0; i < size; i++) { | ||
305 | - batch.add(new TestMessage(msgLength, nanoTime(), 0, stream.padding())); | ||
306 | - } | ||
307 | - acquire(size); | ||
308 | - stream.write(batch); | ||
309 | - } | ||
310 | - | ||
311 | - | ||
312 | - // Release permits based on the specified number of message credits | ||
313 | - private void release(int permits) { | ||
314 | - semaphore.release(permits); | ||
315 | - } | ||
316 | - | ||
317 | - // Acquire permit for a single batch | ||
318 | - private void acquire(int permits) { | ||
319 | - semaphore.acquireUninterruptibly(permits); | ||
320 | - } | ||
321 | - | ||
322 | - } | ||
323 | - | ||
324 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import com.google.common.collect.Lists; | ||
19 | -import org.onlab.util.Counter; | ||
20 | -import org.slf4j.Logger; | ||
21 | -import org.slf4j.LoggerFactory; | ||
22 | - | ||
23 | -import java.io.IOException; | ||
24 | -import java.net.InetAddress; | ||
25 | -import java.net.InetSocketAddress; | ||
26 | -import java.net.Socket; | ||
27 | -import java.net.SocketAddress; | ||
28 | -import java.nio.channels.ByteChannel; | ||
29 | -import java.nio.channels.ServerSocketChannel; | ||
30 | -import java.nio.channels.SocketChannel; | ||
31 | -import java.text.DecimalFormat; | ||
32 | -import java.util.ArrayList; | ||
33 | -import java.util.List; | ||
34 | -import java.util.concurrent.ExecutorService; | ||
35 | -import java.util.concurrent.Executors; | ||
36 | - | ||
37 | -import static java.lang.String.format; | ||
38 | -import static java.lang.System.out; | ||
39 | -import static org.onlab.util.Tools.delay; | ||
40 | -import static org.onlab.util.Tools.namedThreads; | ||
41 | - | ||
42 | -/** | ||
43 | - * Auxiliary test fixture to measure speed of NIO-based channels. | ||
44 | - */ | ||
45 | -public class IOLoopTestServer { | ||
46 | - | ||
47 | - private static Logger log = LoggerFactory.getLogger(IOLoopTestServer.class); | ||
48 | - | ||
49 | - private static final int PRUNE_FREQUENCY = 1000; | ||
50 | - | ||
51 | - static final int PORT = 9876; | ||
52 | - static final long TIMEOUT = 1000; | ||
53 | - | ||
54 | - static final boolean SO_NO_DELAY = false; | ||
55 | - static final int SO_SEND_BUFFER_SIZE = 128 * 1024; | ||
56 | - static final int SO_RCV_BUFFER_SIZE = 128 * 1024; | ||
57 | - | ||
58 | - static final DecimalFormat FORMAT = new DecimalFormat("#,##0"); | ||
59 | - | ||
60 | - private final AcceptorLoop aloop; | ||
61 | - private final ExecutorService apool = Executors.newSingleThreadExecutor(namedThreads("accept")); | ||
62 | - | ||
63 | - private final List<CustomIOLoop> iloops = new ArrayList<>(); | ||
64 | - private final ExecutorService ipool; | ||
65 | - | ||
66 | - private final int workerCount; | ||
67 | - private final int msgLength; | ||
68 | - private int lastWorker = -1; | ||
69 | - | ||
70 | - Counter messages; | ||
71 | - Counter bytes; | ||
72 | - | ||
73 | - /** | ||
74 | - * Main entry point to launch the server. | ||
75 | - * | ||
76 | - * @param args command-line arguments | ||
77 | - * @throws java.io.IOException if unable to crate IO loops | ||
78 | - */ | ||
79 | - public static void main(String[] args) throws IOException { | ||
80 | - startStandalone(args); | ||
81 | - System.exit(0); | ||
82 | - } | ||
83 | - | ||
84 | - /** | ||
85 | - * Starts a standalone IO loop test server. | ||
86 | - * | ||
87 | - * @param args command-line arguments | ||
88 | - */ | ||
89 | - public static void startStandalone(String[] args) throws IOException { | ||
90 | - InetAddress ip = InetAddress.getByName(args.length > 0 ? args[0] : "127.0.0.1"); | ||
91 | - int wc = args.length > 1 ? Integer.parseInt(args[1]) : 6; | ||
92 | - int ml = args.length > 2 ? Integer.parseInt(args[2]) : 128; | ||
93 | - | ||
94 | - log.info("Setting up the server with {} workers, {} byte messages on {}... ", | ||
95 | - wc, ml, ip); | ||
96 | - IOLoopTestServer server = new IOLoopTestServer(ip, wc, ml, PORT); | ||
97 | - server.start(); | ||
98 | - | ||
99 | - // Start pruning clients and keep going until their number goes to 0. | ||
100 | - int remaining = -1; | ||
101 | - while (remaining == -1 || remaining > 0) { | ||
102 | - delay(PRUNE_FREQUENCY); | ||
103 | - int r = server.prune(); | ||
104 | - remaining = remaining == -1 && r == 0 ? remaining : r; | ||
105 | - } | ||
106 | - server.stop(); | ||
107 | - } | ||
108 | - | ||
109 | - /** | ||
110 | - * Creates a speed server. | ||
111 | - * | ||
112 | - * @param ip optional ip of the adapter where to bind | ||
113 | - * @param wc worker count | ||
114 | - * @param ml message length in bytes | ||
115 | - * @param port listen port | ||
116 | - * @throws java.io.IOException if unable to create IO loops | ||
117 | - */ | ||
118 | - public IOLoopTestServer(InetAddress ip, int wc, int ml, int port) throws IOException { | ||
119 | - this.workerCount = wc; | ||
120 | - this.msgLength = ml; | ||
121 | - this.ipool = Executors.newFixedThreadPool(workerCount, namedThreads("io-loop")); | ||
122 | - | ||
123 | - this.aloop = new CustomAcceptLoop(new InetSocketAddress(ip, port)); | ||
124 | - for (int i = 0; i < workerCount; i++) { | ||
125 | - iloops.add(new CustomIOLoop()); | ||
126 | - } | ||
127 | - } | ||
128 | - | ||
129 | - /** | ||
130 | - * Start the server IO loops and kicks off throughput tracking. | ||
131 | - */ | ||
132 | - public void start() { | ||
133 | - messages = new Counter(); | ||
134 | - bytes = new Counter(); | ||
135 | - | ||
136 | - for (CustomIOLoop l : iloops) { | ||
137 | - ipool.execute(l); | ||
138 | - } | ||
139 | - apool.execute(aloop); | ||
140 | - | ||
141 | - for (CustomIOLoop l : iloops) { | ||
142 | - l.awaitStart(TIMEOUT); | ||
143 | - } | ||
144 | - aloop.awaitStart(TIMEOUT); | ||
145 | - } | ||
146 | - | ||
147 | - /** | ||
148 | - * Stop the server IO loops and freezes throughput tracking. | ||
149 | - */ | ||
150 | - public void stop() { | ||
151 | - aloop.shutdown(); | ||
152 | - for (CustomIOLoop l : iloops) { | ||
153 | - l.shutdown(); | ||
154 | - } | ||
155 | - | ||
156 | - for (CustomIOLoop l : iloops) { | ||
157 | - l.awaitStop(TIMEOUT); | ||
158 | - } | ||
159 | - aloop.awaitStop(TIMEOUT); | ||
160 | - | ||
161 | - messages.freeze(); | ||
162 | - bytes.freeze(); | ||
163 | - } | ||
164 | - | ||
165 | - /** | ||
166 | - * Reports on the accumulated throughput and latency. | ||
167 | - */ | ||
168 | - public void report() { | ||
169 | - DecimalFormat f = new DecimalFormat("#,##0"); | ||
170 | - out.println(format("Server: %s messages; %s bytes; %s mps; %s MBs", | ||
171 | - f.format(messages.total()), f.format(bytes.total()), | ||
172 | - f.format(messages.throughput()), | ||
173 | - f.format(bytes.throughput() / (1024 * msgLength)))); | ||
174 | - } | ||
175 | - | ||
176 | - /** | ||
177 | - * Prunes the IO loops of stale message buffers. | ||
178 | - * | ||
179 | - * @return number of remaining IO loops among all workers. | ||
180 | - */ | ||
181 | - public int prune() { | ||
182 | - int count = 0; | ||
183 | - for (CustomIOLoop l : iloops) { | ||
184 | - count += l.pruneStaleStreams(); | ||
185 | - } | ||
186 | - return count; | ||
187 | - } | ||
188 | - | ||
189 | - // Get the next worker to which a client should be assigned | ||
190 | - private synchronized CustomIOLoop nextWorker() { | ||
191 | - lastWorker = (lastWorker + 1) % workerCount; | ||
192 | - return iloops.get(lastWorker); | ||
193 | - } | ||
194 | - | ||
195 | - // Loop for transfer of fixed-length messages | ||
196 | - private class CustomIOLoop extends IOLoop<TestMessage, TestMessageStream> { | ||
197 | - | ||
198 | - public CustomIOLoop() throws IOException { | ||
199 | - super(500); | ||
200 | - } | ||
201 | - | ||
202 | - @Override | ||
203 | - protected TestMessageStream createStream(ByteChannel channel) { | ||
204 | - return new TestMessageStream(msgLength, channel, this); | ||
205 | - } | ||
206 | - | ||
207 | - @Override | ||
208 | - protected void removeStream(MessageStream<TestMessage> stream) { | ||
209 | - super.removeStream(stream); | ||
210 | - messages.add(stream.messagesIn().total()); | ||
211 | - bytes.add(stream.bytesIn().total()); | ||
212 | - } | ||
213 | - | ||
214 | - @Override | ||
215 | - protected void processMessages(List<TestMessage> messages, | ||
216 | - MessageStream<TestMessage> stream) { | ||
217 | - try { | ||
218 | - stream.write(createResponses(messages)); | ||
219 | - } catch (IOException e) { | ||
220 | - log.error("Unable to echo messages", e); | ||
221 | - } | ||
222 | - } | ||
223 | - | ||
224 | - private List<TestMessage> createResponses(List<TestMessage> messages) { | ||
225 | - List<TestMessage> responses = Lists.newArrayListWithCapacity(messages.size()); | ||
226 | - for (TestMessage message : messages) { | ||
227 | - responses.add(new TestMessage(message.length(), message.requestorTime(), | ||
228 | - System.nanoTime(), message.padding())); | ||
229 | - } | ||
230 | - return responses; | ||
231 | - } | ||
232 | - } | ||
233 | - | ||
234 | - // Loop for accepting client connections | ||
235 | - private class CustomAcceptLoop extends AcceptorLoop { | ||
236 | - | ||
237 | - public CustomAcceptLoop(SocketAddress address) throws IOException { | ||
238 | - super(500, address); | ||
239 | - } | ||
240 | - | ||
241 | - @Override | ||
242 | - protected void acceptConnection(ServerSocketChannel channel) throws IOException { | ||
243 | - SocketChannel sc = channel.accept(); | ||
244 | - sc.configureBlocking(false); | ||
245 | - | ||
246 | - Socket so = sc.socket(); | ||
247 | - so.setTcpNoDelay(SO_NO_DELAY); | ||
248 | - so.setReceiveBufferSize(SO_RCV_BUFFER_SIZE); | ||
249 | - so.setSendBufferSize(SO_SEND_BUFFER_SIZE); | ||
250 | - | ||
251 | - nextWorker().acceptStream(sc); | ||
252 | - log.info("Connected client"); | ||
253 | - } | ||
254 | - } | ||
255 | - | ||
256 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import org.junit.After; | ||
19 | -import org.junit.Before; | ||
20 | -import org.junit.Test; | ||
21 | - | ||
22 | -import java.io.IOException; | ||
23 | -import java.nio.ByteBuffer; | ||
24 | -import java.nio.channels.ByteChannel; | ||
25 | -import java.nio.channels.ClosedChannelException; | ||
26 | -import java.nio.channels.SelectableChannel; | ||
27 | -import java.nio.channels.SelectionKey; | ||
28 | -import java.nio.channels.Selector; | ||
29 | -import java.nio.channels.spi.SelectorProvider; | ||
30 | -import java.util.ArrayList; | ||
31 | -import java.util.List; | ||
32 | - | ||
33 | -import static org.junit.Assert.assertEquals; | ||
34 | -import static org.junit.Assert.assertNull; | ||
35 | - | ||
36 | -/** | ||
37 | - * Tests of the message message stream implementation. | ||
38 | - */ | ||
39 | -public class MessageStreamTest { | ||
40 | - | ||
41 | - private static final int SIZE = 64; | ||
42 | - private static final int BIG_SIZE = 32 * 1024; | ||
43 | - | ||
44 | - private TestMessage message; | ||
45 | - | ||
46 | - private TestIOLoop loop; | ||
47 | - private TestByteChannel channel; | ||
48 | - private TestMessageStream stream; | ||
49 | - private TestKey key; | ||
50 | - | ||
51 | - @Before | ||
52 | - public void setUp() throws IOException { | ||
53 | - loop = new TestIOLoop(); | ||
54 | - channel = new TestByteChannel(); | ||
55 | - key = new TestKey(channel); | ||
56 | - stream = loop.createStream(channel); | ||
57 | - stream.setKey(key); | ||
58 | - stream.setNonStrict(); | ||
59 | - message = new TestMessage(SIZE, 0, 0, stream.padding()); | ||
60 | - } | ||
61 | - | ||
62 | - @After | ||
63 | - public void tearDown() { | ||
64 | - loop.shutdown(); | ||
65 | - stream.close(); | ||
66 | - } | ||
67 | - | ||
68 | - // Validates the state of the message stream | ||
69 | - private void validate(boolean wp, boolean fr, int read, int written) { | ||
70 | - assertEquals(wp, stream.isWritePending()); | ||
71 | - assertEquals(fr, stream.isFlushRequired()); | ||
72 | - assertEquals(read, channel.readBytes); | ||
73 | - assertEquals(written, channel.writtenBytes); | ||
74 | - } | ||
75 | - | ||
76 | - @Test | ||
77 | - public void endOfStream() throws IOException { | ||
78 | - channel.close(); | ||
79 | - List<TestMessage> messages = stream.read(); | ||
80 | - assertNull(messages); | ||
81 | - } | ||
82 | - | ||
83 | - @Test | ||
84 | - public void bufferGrowth() throws IOException { | ||
85 | - // Create a stream for big messages and test the growth. | ||
86 | - stream = new TestMessageStream(BIG_SIZE, channel, loop); | ||
87 | - TestMessage bigMessage = new TestMessage(BIG_SIZE, 0, 0, stream.padding()); | ||
88 | - | ||
89 | - stream.write(bigMessage); | ||
90 | - stream.write(bigMessage); | ||
91 | - stream.write(bigMessage); | ||
92 | - stream.write(bigMessage); | ||
93 | - stream.write(bigMessage); | ||
94 | - } | ||
95 | - | ||
96 | - @Test | ||
97 | - public void discardBeforeKey() { | ||
98 | - // Create a stream that does not yet have the key set and discard it. | ||
99 | - stream = loop.createStream(channel); | ||
100 | - assertNull(stream.key()); | ||
101 | - stream.close(); | ||
102 | - // There is not key, so nothing to check; we just expect no problem. | ||
103 | - } | ||
104 | - | ||
105 | - @Test | ||
106 | - public void bufferedRead() throws IOException { | ||
107 | - channel.bytesToRead = SIZE + 4; | ||
108 | - List<TestMessage> messages = stream.read(); | ||
109 | - assertEquals(1, messages.size()); | ||
110 | - validate(false, false, SIZE + 4, 0); | ||
111 | - | ||
112 | - channel.bytesToRead = SIZE - 4; | ||
113 | - messages = stream.read(); | ||
114 | - assertEquals(1, messages.size()); | ||
115 | - validate(false, false, SIZE * 2, 0); | ||
116 | - } | ||
117 | - | ||
118 | - @Test | ||
119 | - public void bufferedWrite() throws IOException { | ||
120 | - validate(false, false, 0, 0); | ||
121 | - | ||
122 | - // First write is immediate... | ||
123 | - stream.write(message); | ||
124 | - validate(false, false, 0, SIZE); | ||
125 | - | ||
126 | - // Second and third get buffered... | ||
127 | - stream.write(message); | ||
128 | - validate(false, true, 0, SIZE); | ||
129 | - stream.write(message); | ||
130 | - validate(false, true, 0, SIZE); | ||
131 | - | ||
132 | - // Reset write, which will flush if needed; the next write is again buffered | ||
133 | - stream.flushIfWriteNotPending(); | ||
134 | - validate(false, false, 0, SIZE * 3); | ||
135 | - stream.write(message); | ||
136 | - validate(false, true, 0, SIZE * 3); | ||
137 | - | ||
138 | - // Select reset, which will flush if needed; the next write is again buffered | ||
139 | - stream.flushIfPossible(); | ||
140 | - validate(false, false, 0, SIZE * 4); | ||
141 | - stream.write(message); | ||
142 | - validate(false, true, 0, SIZE * 4); | ||
143 | - stream.flush(); | ||
144 | - validate(false, true, 0, SIZE * 4); | ||
145 | - } | ||
146 | - | ||
147 | - @Test | ||
148 | - public void bufferedWriteList() throws IOException { | ||
149 | - validate(false, false, 0, 0); | ||
150 | - | ||
151 | - // First write is immediate... | ||
152 | - List<TestMessage> messages = new ArrayList<>(); | ||
153 | - messages.add(message); | ||
154 | - messages.add(message); | ||
155 | - messages.add(message); | ||
156 | - messages.add(message); | ||
157 | - | ||
158 | - stream.write(messages); | ||
159 | - validate(false, false, 0, SIZE * 4); | ||
160 | - | ||
161 | - stream.write(messages); | ||
162 | - validate(false, true, 0, SIZE * 4); | ||
163 | - | ||
164 | - stream.flushIfPossible(); | ||
165 | - validate(false, false, 0, SIZE * 8); | ||
166 | - } | ||
167 | - | ||
168 | - @Test | ||
169 | - public void bufferedPartialWrite() throws IOException { | ||
170 | - validate(false, false, 0, 0); | ||
171 | - | ||
172 | - // First write is immediate... | ||
173 | - stream.write(message); | ||
174 | - validate(false, false, 0, SIZE); | ||
175 | - | ||
176 | - // Tell test channel to accept only half. | ||
177 | - channel.bytesToWrite = SIZE / 2; | ||
178 | - | ||
179 | - // Second and third get buffered... | ||
180 | - stream.write(message); | ||
181 | - validate(false, true, 0, SIZE); | ||
182 | - stream.flushIfPossible(); | ||
183 | - validate(true, true, 0, SIZE + SIZE / 2); | ||
184 | - } | ||
185 | - | ||
186 | - @Test | ||
187 | - public void bufferedPartialWrite2() throws IOException { | ||
188 | - validate(false, false, 0, 0); | ||
189 | - | ||
190 | - // First write is immediate... | ||
191 | - stream.write(message); | ||
192 | - validate(false, false, 0, SIZE); | ||
193 | - | ||
194 | - // Tell test channel to accept only half. | ||
195 | - channel.bytesToWrite = SIZE / 2; | ||
196 | - | ||
197 | - // Second and third get buffered... | ||
198 | - stream.write(message); | ||
199 | - validate(false, true, 0, SIZE); | ||
200 | - stream.flushIfWriteNotPending(); | ||
201 | - validate(true, true, 0, SIZE + SIZE / 2); | ||
202 | - } | ||
203 | - | ||
204 | - @Test | ||
205 | - public void bufferedReadWrite() throws IOException { | ||
206 | - channel.bytesToRead = SIZE + 4; | ||
207 | - List<TestMessage> messages = stream.read(); | ||
208 | - assertEquals(1, messages.size()); | ||
209 | - validate(false, false, SIZE + 4, 0); | ||
210 | - | ||
211 | - stream.write(message); | ||
212 | - validate(false, false, SIZE + 4, SIZE); | ||
213 | - | ||
214 | - channel.bytesToRead = SIZE - 4; | ||
215 | - messages = stream.read(); | ||
216 | - assertEquals(1, messages.size()); | ||
217 | - validate(false, false, SIZE * 2, SIZE); | ||
218 | - } | ||
219 | - | ||
220 | - // Fake IO driver loop | ||
221 | - private static class TestIOLoop extends IOLoop<TestMessage, TestMessageStream> { | ||
222 | - | ||
223 | - public TestIOLoop() throws IOException { | ||
224 | - super(500); | ||
225 | - } | ||
226 | - | ||
227 | - @Override | ||
228 | - protected TestMessageStream createStream(ByteChannel channel) { | ||
229 | - return new TestMessageStream(SIZE, channel, this); | ||
230 | - } | ||
231 | - | ||
232 | - @Override | ||
233 | - protected void processMessages(List<TestMessage> messages, | ||
234 | - MessageStream<TestMessage> stream) { | ||
235 | - } | ||
236 | - | ||
237 | - } | ||
238 | - | ||
239 | - // Byte channel test fixture | ||
240 | - private static class TestByteChannel extends SelectableChannel implements ByteChannel { | ||
241 | - | ||
242 | - private static final int BUFFER_LENGTH = 1024; | ||
243 | - byte[] bytes = new byte[BUFFER_LENGTH]; | ||
244 | - int bytesToWrite = BUFFER_LENGTH; | ||
245 | - int bytesToRead = BUFFER_LENGTH; | ||
246 | - int writtenBytes = 0; | ||
247 | - int readBytes = 0; | ||
248 | - | ||
249 | - @Override | ||
250 | - public int read(ByteBuffer dst) throws IOException { | ||
251 | - int l = Math.min(dst.remaining(), bytesToRead); | ||
252 | - if (bytesToRead > 0) { | ||
253 | - readBytes += l; | ||
254 | - dst.put(bytes, 0, l); | ||
255 | - } | ||
256 | - return l; | ||
257 | - } | ||
258 | - | ||
259 | - @Override | ||
260 | - public int write(ByteBuffer src) throws IOException { | ||
261 | - int l = Math.min(src.remaining(), bytesToWrite); | ||
262 | - writtenBytes += l; | ||
263 | - src.get(bytes, 0, l); | ||
264 | - return l; | ||
265 | - } | ||
266 | - | ||
267 | - @Override | ||
268 | - public Object blockingLock() { | ||
269 | - return null; | ||
270 | - } | ||
271 | - | ||
272 | - @Override | ||
273 | - public SelectableChannel configureBlocking(boolean arg0) throws IOException { | ||
274 | - return null; | ||
275 | - } | ||
276 | - | ||
277 | - @Override | ||
278 | - public boolean isBlocking() { | ||
279 | - return false; | ||
280 | - } | ||
281 | - | ||
282 | - @Override | ||
283 | - public boolean isRegistered() { | ||
284 | - return false; | ||
285 | - } | ||
286 | - | ||
287 | - @Override | ||
288 | - public SelectionKey keyFor(Selector arg0) { | ||
289 | - return null; | ||
290 | - } | ||
291 | - | ||
292 | - @Override | ||
293 | - public SelectorProvider provider() { | ||
294 | - return null; | ||
295 | - } | ||
296 | - | ||
297 | - @Override | ||
298 | - public SelectionKey register(Selector arg0, int arg1, Object arg2) | ||
299 | - throws ClosedChannelException { | ||
300 | - return null; | ||
301 | - } | ||
302 | - | ||
303 | - @Override | ||
304 | - public int validOps() { | ||
305 | - return 0; | ||
306 | - } | ||
307 | - | ||
308 | - @Override | ||
309 | - protected void implCloseChannel() throws IOException { | ||
310 | - bytesToRead = -1; | ||
311 | - } | ||
312 | - | ||
313 | - } | ||
314 | - | ||
315 | - // Selection key text fixture | ||
316 | - private static class TestKey extends SelectionKey { | ||
317 | - | ||
318 | - private SelectableChannel channel; | ||
319 | - | ||
320 | - public TestKey(TestByteChannel channel) { | ||
321 | - this.channel = channel; | ||
322 | - } | ||
323 | - | ||
324 | - @Override | ||
325 | - public void cancel() { | ||
326 | - } | ||
327 | - | ||
328 | - @Override | ||
329 | - public SelectableChannel channel() { | ||
330 | - return channel; | ||
331 | - } | ||
332 | - | ||
333 | - @Override | ||
334 | - public int interestOps() { | ||
335 | - return 0; | ||
336 | - } | ||
337 | - | ||
338 | - @Override | ||
339 | - public SelectionKey interestOps(int ops) { | ||
340 | - return null; | ||
341 | - } | ||
342 | - | ||
343 | - @Override | ||
344 | - public boolean isValid() { | ||
345 | - return true; | ||
346 | - } | ||
347 | - | ||
348 | - @Override | ||
349 | - public int readyOps() { | ||
350 | - return 0; | ||
351 | - } | ||
352 | - | ||
353 | - @Override | ||
354 | - public Selector selector() { | ||
355 | - return null; | ||
356 | - } | ||
357 | - } | ||
358 | - | ||
359 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import java.io.IOException; | ||
19 | -import java.nio.channels.SelectionKey; | ||
20 | -import java.nio.channels.Selector; | ||
21 | -import java.nio.channels.spi.AbstractSelectableChannel; | ||
22 | -import java.nio.channels.spi.AbstractSelector; | ||
23 | -import java.util.Set; | ||
24 | - | ||
25 | -/** | ||
26 | - * A selector instrumented for unit tests. | ||
27 | - */ | ||
28 | -public class MockSelector extends AbstractSelector { | ||
29 | - | ||
30 | - int wakeUpCount = 0; | ||
31 | - | ||
32 | - /** | ||
33 | - * Creates a mock selector, specifying null as the SelectorProvider. | ||
34 | - */ | ||
35 | - public MockSelector() { | ||
36 | - super(null); | ||
37 | - } | ||
38 | - | ||
39 | - @Override | ||
40 | - public String toString() { | ||
41 | - return "{MockSelector: wake=" + wakeUpCount + "}"; | ||
42 | - } | ||
43 | - | ||
44 | - @Override | ||
45 | - protected void implCloseSelector() throws IOException { | ||
46 | - } | ||
47 | - | ||
48 | - @Override | ||
49 | - protected SelectionKey register(AbstractSelectableChannel ch, int ops, | ||
50 | - Object att) { | ||
51 | - return null; | ||
52 | - } | ||
53 | - | ||
54 | - @Override | ||
55 | - public Set<SelectionKey> keys() { | ||
56 | - return null; | ||
57 | - } | ||
58 | - | ||
59 | - @Override | ||
60 | - public Set<SelectionKey> selectedKeys() { | ||
61 | - return null; | ||
62 | - } | ||
63 | - | ||
64 | - @Override | ||
65 | - public int selectNow() throws IOException { | ||
66 | - return 0; | ||
67 | - } | ||
68 | - | ||
69 | - @Override | ||
70 | - public int select(long timeout) throws IOException { | ||
71 | - return 0; | ||
72 | - } | ||
73 | - | ||
74 | - @Override | ||
75 | - public int select() throws IOException { | ||
76 | - return 0; | ||
77 | - } | ||
78 | - | ||
79 | - @Override | ||
80 | - public Selector wakeup() { | ||
81 | - wakeUpCount++; | ||
82 | - return null; | ||
83 | - } | ||
84 | - | ||
85 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import static com.google.common.base.Preconditions.checkNotNull; | ||
19 | - | ||
20 | -/** | ||
21 | - * Test message for measuring rate and round-trip latency. | ||
22 | - */ | ||
23 | -public class TestMessage extends AbstractMessage { | ||
24 | - | ||
25 | - private final byte[] padding; | ||
26 | - | ||
27 | - private final long requestorTime; | ||
28 | - private final long responderTime; | ||
29 | - | ||
30 | - /** | ||
31 | - * Creates a new message with the specified data. | ||
32 | - * | ||
33 | - * @param requestorTime requester time | ||
34 | - * @param responderTime responder time | ||
35 | - * @param padding message padding | ||
36 | - */ | ||
37 | - TestMessage(int length, long requestorTime, long responderTime, byte[] padding) { | ||
38 | - this.length = length; | ||
39 | - this.requestorTime = requestorTime; | ||
40 | - this.responderTime = responderTime; | ||
41 | - this.padding = checkNotNull(padding, "Padding cannot be null"); | ||
42 | - } | ||
43 | - | ||
44 | - public long requestorTime() { | ||
45 | - return requestorTime; | ||
46 | - } | ||
47 | - | ||
48 | - public long responderTime() { | ||
49 | - return responderTime; | ||
50 | - } | ||
51 | - | ||
52 | - public byte[] padding() { | ||
53 | - return padding; | ||
54 | - } | ||
55 | - | ||
56 | -} |
1 | -/* | ||
2 | - * Copyright 2014 Open Networking Laboratory | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.onlab.nio; | ||
17 | - | ||
18 | -import java.nio.ByteBuffer; | ||
19 | -import java.nio.channels.ByteChannel; | ||
20 | - | ||
21 | -import static com.google.common.base.Preconditions.checkArgument; | ||
22 | -import static com.google.common.base.Preconditions.checkState; | ||
23 | - | ||
24 | -/** | ||
25 | - * Fixed-length message transfer buffer. | ||
26 | - */ | ||
27 | -public class TestMessageStream extends MessageStream<TestMessage> { | ||
28 | - | ||
29 | - private static final String E_WRONG_LEN = "Illegal message length: "; | ||
30 | - private static final long START_TAG = 0xfeedcafedeaddeedL; | ||
31 | - private static final long END_TAG = 0xbeadcafedeaddeedL; | ||
32 | - private static final int META_LENGTH = 40; | ||
33 | - | ||
34 | - private final int length; | ||
35 | - private boolean isStrict = true; | ||
36 | - | ||
37 | - public TestMessageStream(int length, ByteChannel ch, IOLoop<TestMessage, ?> loop) { | ||
38 | - super(loop, ch, 64 * 1024, 500); | ||
39 | - checkArgument(length >= META_LENGTH, "Length must be greater than header length of 40"); | ||
40 | - this.length = length; | ||
41 | - } | ||
42 | - | ||
43 | - void setNonStrict() { | ||
44 | - isStrict = false; | ||
45 | - } | ||
46 | - | ||
47 | - @Override | ||
48 | - protected TestMessage read(ByteBuffer rb) { | ||
49 | - if (rb.remaining() < length) { | ||
50 | - return null; | ||
51 | - } | ||
52 | - | ||
53 | - long startTag = rb.getLong(); | ||
54 | - if (isStrict) { | ||
55 | - checkState(startTag == START_TAG, "Incorrect message start"); | ||
56 | - } | ||
57 | - | ||
58 | - long size = rb.getLong(); | ||
59 | - long requestorTime = rb.getLong(); | ||
60 | - long responderTime = rb.getLong(); | ||
61 | - byte[] padding = padding(); | ||
62 | - rb.get(padding); | ||
63 | - | ||
64 | - long endTag = rb.getLong(); | ||
65 | - if (isStrict) { | ||
66 | - checkState(endTag == END_TAG, "Incorrect message end"); | ||
67 | - } | ||
68 | - | ||
69 | - return new TestMessage((int) size, requestorTime, responderTime, padding); | ||
70 | - } | ||
71 | - | ||
72 | - @Override | ||
73 | - protected void write(TestMessage message, ByteBuffer wb) { | ||
74 | - if (message.length() != length) { | ||
75 | - throw new IllegalArgumentException(E_WRONG_LEN + message.length()); | ||
76 | - } | ||
77 | - | ||
78 | - wb.putLong(START_TAG); | ||
79 | - wb.putLong(message.length()); | ||
80 | - wb.putLong(message.requestorTime()); | ||
81 | - wb.putLong(message.responderTime()); | ||
82 | - wb.put(message.padding(), 0, length - META_LENGTH); | ||
83 | - wb.putLong(END_TAG); | ||
84 | - } | ||
85 | - | ||
86 | - public byte[] padding() { | ||
87 | - return new byte[length - META_LENGTH]; | ||
88 | - } | ||
89 | -} |
... | @@ -34,7 +34,6 @@ | ... | @@ -34,7 +34,6 @@ |
34 | <modules> | 34 | <modules> |
35 | <module>junit</module> | 35 | <module>junit</module> |
36 | <module>misc</module> | 36 | <module>misc</module> |
37 | - <module>nio</module> | ||
38 | <module>yangutils</module> | 37 | <module>yangutils</module> |
39 | <module>osgi</module> | 38 | <module>osgi</module> |
40 | <module>rest</module> | 39 | <module>rest</module> | ... | ... |
-
Please register or login to post a comment