Activate code generation
[bgpcep.git] / framework / src / main / java / org / opendaylight / protocol / framework / SSLServerSocketChannel.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.framework;
9
10 import java.io.IOException;
11 import java.net.ServerSocket;
12 import java.net.SocketAddress;
13 import java.net.SocketOption;
14 import java.nio.channels.SelectionKey;
15 import java.nio.channels.Selector;
16 import java.nio.channels.ServerSocketChannel;
17 import java.nio.channels.SocketChannel;
18 import java.util.Queue;
19 import java.util.Set;
20 import java.util.concurrent.Executor;
21
22 import javax.net.ssl.SSLContext;
23
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import com.google.common.collect.Queues;
28
29 /**
30  * SSL-enabled equivalent of ServerSocketChannel. This class uses a backend
31  * ServerSocketChannel to implement network functionality. Each instance is
32  * bound to a SSLContext, which is used to create a per-connection SSLEngine,
33  * which is encapsulated into the returned SSLSocketChannel.
34  */
35 final class SSLServerSocketChannel extends ServerSocketChannel implements SSLSelectableChannel {
36         private static final Logger logger = LoggerFactory.getLogger(SSLServerSocketChannel.class);
37         private final Queue<SocketChannel> newChannels = Queues.newArrayDeque();
38         private final SSLContext context;
39         private final Executor executor;
40         private final Selector selector;
41         private boolean closed = false;
42
43         protected final ServerSocketChannel channel;
44
45         private SSLServerSocketChannel(final Selector selector, final ServerSocketChannel channel, final SSLContext context, final Executor executor) {
46                 super(channel.provider());
47                 this.selector = selector;
48                 this.executor = executor;
49                 this.channel = channel;
50                 this.context = context;
51         }
52
53         public static SSLServerSocketChannel open(final Selector selector, final SSLContext context, final Executor executor) throws IOException {
54                 return new SSLServerSocketChannel(selector, ServerSocketChannel.open(), context, executor);
55         }
56
57         @Override
58         public final synchronized SocketChannel accept() {
59                 return newChannels.poll();
60         }
61
62         @Override
63         public ServerSocket socket() {
64                 // We do not support this operation, everyone should use Java 7 interfaces
65                 throw new UnsupportedOperationException("SSLSocketChannel does not provide a fake Socket implementation");
66         }
67
68         @Override
69         protected synchronized void implCloseSelectableChannel() throws IOException {
70                 closed = true;
71                 while (!newChannels.isEmpty()) {
72                         final SocketChannel c = newChannels.poll();
73                         try {
74                                 c.close();
75                         } catch (IOException e) {
76                                 logger.trace("Failed to close a queued channel", e);
77                         }
78                 }
79                 channel.close();
80         }
81
82         @Override
83         protected void implConfigureBlocking(final boolean block) throws IOException {
84                 channel.configureBlocking(block);
85         }
86
87         @Override
88         public final synchronized int computeInterestOps(final int ops) {
89                 // We are always interested in accepting stuff
90                 return SelectionKey.OP_ACCEPT;
91         }
92
93         private void performIO() {
94                 while (true) {
95                         final SocketChannel newchan;
96                         try {
97                                 newchan = channel.accept();
98                                 if (newchan == null)
99                                         break;
100                         } catch (IOException e) {
101                                 logger.trace("Underlying accept() failed", e);
102                                 return;
103                         }
104
105                         try {
106                                 final SocketChannel sc;
107                                 try {
108                                         sc = SSLSocketChannel.open(newchan, context, executor, this);
109                                 } catch (IOException e) {
110                                         logger.trace("Failed to create SSL channel", e);
111                                         newchan.close();
112                                         continue;
113                                 }
114
115                                 try {
116                                         sc.configureBlocking(false);
117                                         sc.register(selector, SelectionKey.OP_CONNECT, null);
118                                 } catch (IOException e) {
119                                         logger.trace("Failed to register SSL channel", e);
120                                         sc.close();
121                                         continue;
122                                 }
123
124                                 logger.trace("Accepted new connection, channel is {} backend {}", sc, newchan);
125                         } catch (IOException e1) {
126                                 logger.trace("Failed to close failed channel", e1);
127                         }
128                 }
129         }
130
131         @Override
132         public final synchronized int computeReadyOps() {
133                 if (closed)
134                         return 0;
135
136                 performIO();
137
138                 // We need to be non-closed and have enqueue channels to be ready
139                 if (!closed && !newChannels.isEmpty())
140                         return SelectionKey.OP_ACCEPT;
141                 return 0;
142         }
143
144         /**
145          * Enqueue a freshly-established child channel for reporting as
146          * a ready-to-accept connection.
147          *
148          * @param channel Fresh channel, expected to be in connected state
149          */
150         final synchronized void addNewChannel(final SocketChannel channel) {
151                 if (closed) {
152                         try {
153                                 channel.close();
154                         } catch (IOException e) {
155                                 logger.trace("Failed to close a queued channel", e);
156                         }
157                 } else
158                         newChannels.add(channel);
159         }
160
161         @Override
162         public SocketAddress getLocalAddress() throws IOException {
163                 return channel.getLocalAddress();
164         }
165
166         @Override
167         public <T> T getOption(SocketOption<T> name) throws IOException {
168                 return channel.getOption(name);
169         }
170
171         @Override
172         public Set<SocketOption<?>> supportedOptions() {
173                 return channel.supportedOptions();
174         }
175
176         @Override
177         public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {
178                 channel.bind(local, backlog);
179                 return this;
180         }
181
182         @Override
183         public <T> ServerSocketChannel setOption(SocketOption<T> name, T value) throws IOException {
184                 channel.setOption(name, value);
185                 return this;
186         }
187 }
188