281082ade0f0f48523f746e8cd0236969f581c5d
[iotivity.git] / cloud / stack / src / main / java / org / iotivity / cloud / base / connector / CoapConnector.java
1 /*
2  * //******************************************************************
3  * //
4  * // Copyright 2016 Samsung Electronics All Rights Reserved.
5  * //
6  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7  * //
8  * // Licensed under the Apache License, Version 2.0 (the "License");
9  * // you may not use this file except in compliance with the License.
10  * // You may obtain a copy of the License at
11  * //
12  * //      http://www.apache.org/licenses/LICENSE-2.0
13  * //
14  * // Unless required by applicable law or agreed to in writing, software
15  * // distributed under the License is distributed on an "AS IS" BASIS,
16  * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * // See the License for the specific language governing permissions and
18  * // limitations under the License.
19  * //
20  * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21  */
22 package org.iotivity.cloud.base.connector;
23
24 import io.netty.bootstrap.Bootstrap;
25 import io.netty.channel.*;
26 import io.netty.channel.ChannelHandler.Sharable;
27 import io.netty.channel.nio.NioEventLoopGroup;
28 import io.netty.channel.socket.SocketChannel;
29 import io.netty.channel.socket.nio.NioSocketChannel;
30 import io.netty.handler.ssl.SslContext;
31 import io.netty.handler.ssl.SslContextBuilder;
32 import io.netty.handler.ssl.SslProvider;
33 import io.netty.handler.timeout.IdleState;
34 import io.netty.handler.timeout.IdleStateEvent;
35 import io.netty.handler.timeout.IdleStateHandler;
36 import org.iotivity.cloud.base.OICConstants;
37 import org.iotivity.cloud.base.protocols.coap.*;
38 import org.iotivity.cloud.base.protocols.coap.PingMessage;
39 import org.iotivity.cloud.util.Log;
40
41 import javax.net.ssl.SSLException;
42 import java.io.File;
43 import java.net.InetSocketAddress;
44 import java.util.*;
45
46 public class CoapConnector {
47
48     public CoapConnector() {
49
50         mBootstrap.group(mConnectorGroup);
51         mBootstrap.channel(NioSocketChannel.class);
52         mBootstrap.option(ChannelOption.TCP_NODELAY, true);
53         mBootstrap.option(ChannelOption.SO_KEEPALIVE, true);
54         mBootstrap.option(ChannelOption.SO_REUSEADDR, true);
55     }
56
57     @Sharable
58     private class CoapPacketHandler
59             extends SimpleChannelInboundHandler<CoapResponse> {
60
61         @Override
62         protected void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
63                 throws Exception {
64             mChannelMap.get(ctx.channel()).onResponseReceived(msg);
65         }
66     }
67
68     public static class KeepAliveHandler extends ChannelDuplexHandler {
69         @Override
70         public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
71                 throws Exception {
72             if (evt instanceof IdleStateEvent) {
73                 IdleStateEvent event = (IdleStateEvent) evt;
74                 if (event.state() == IdleState.WRITER_IDLE) {
75                     ctx.writeAndFlush(PingMessage.build());
76                 }
77                 if (event.state() == IdleState.READER_IDLE) {
78                     Log.d("Connection with" +  ctx.channel().remoteAddress().toString() + "is idle. Closing connection.");
79                     ctx.close();
80                 }
81             }
82         }
83     }
84
85     public static class CoapConnectorInitializer
86             extends ChannelInitializer<SocketChannel> {
87
88         private List<ChannelHandler> additionalHandlers = new ArrayList<>();
89
90         private Boolean              mTlsMode           = false;
91         private Boolean              mKeepAlive         = false;
92         InetSocketAddress            mInetSocketAddress = null;
93         String                       mRootCertFiePath   = null;
94
95         public void setTlsMode(Boolean tlsMode) {
96             this.mTlsMode = tlsMode;
97         }
98
99         public void setKeepAlive(Boolean keepAlive) {
100             this.mKeepAlive = keepAlive;
101         }
102
103         public void setInetSocketAddress(InetSocketAddress address) {
104             this.mInetSocketAddress = address;
105         }
106
107         public void setRootCertFilePath(String path) {
108             this.mRootCertFiePath = path;
109         }
110
111         public void addHandler(ChannelHandler handler) {
112             additionalHandlers.add(handler);
113         }
114
115         @Override
116         public void initChannel(SocketChannel ch) {
117             ChannelPipeline p = ch.pipeline();
118
119             SslContext sslContext = null;
120
121             if (mTlsMode.equals(true)) {
122
123                 File rootCert = new File(mRootCertFiePath);
124
125                 try {
126                     sslContext = SslContextBuilder.forClient()
127                             .sslProvider(SslProvider.JDK).trustManager(rootCert)
128                             .build();
129                 } catch (SSLException e) {
130                     e.printStackTrace();
131                 }
132
133                 final SslContext sslCtx = sslContext;
134                 p.addLast(sslCtx.newHandler(ch.alloc(),
135                         mInetSocketAddress.getHostString(),
136                         mInetSocketAddress.getPort()));
137             }
138
139             p.addLast(new CoapDecoder());
140             p.addLast(new CoapEncoder());
141             p.addLast(new CoapLogHandler());
142
143             if (mKeepAlive.equals(true)) {
144                 p.addLast(new IdleStateHandler(100, 45, 0));
145                 p.addLast(new KeepAliveHandler());
146             }
147
148             for (ChannelHandler handler : additionalHandlers) {
149                 p.addLast(handler);
150             }
151         }
152     }
153
154     HashMap<Channel, CoapClient> mChannelMap     = new HashMap<>();
155     Bootstrap                    mBootstrap      = new Bootstrap();
156     EventLoopGroup               mConnectorGroup = new NioEventLoopGroup();
157     Timer                        mTimer          = new Timer();
158
159     public void connect(final String connectionName, final InetSocketAddress inetSocketAddress,
160             boolean tlsMode, boolean keepAlive) {
161
162         CoapConnectorInitializer initializer = new CoapConnectorInitializer();
163
164         if (tlsMode == true) {
165             initializer.setTlsMode(true);
166             initializer.setInetSocketAddress(inetSocketAddress);
167             initializer.setRootCertFilePath(OICConstants.ROOT_CERT_FILE);
168         }
169
170         initializer.setKeepAlive(keepAlive);
171         initializer.addHandler(new CoapPacketHandler());
172         mBootstrap.handler(initializer);
173         doConnect(connectionName, inetSocketAddress, tlsMode);
174     }
175
176     private void doConnect(final String connectionName, final InetSocketAddress inetSocketAddress, final boolean tlsMode) {
177         mBootstrap.connect(inetSocketAddress).addListener(new ChannelFutureListener() {
178                 @Override public void operationComplete(ChannelFuture future) throws Exception {
179                     if(!future.isSuccess()) {
180                         Log.d("Connection to " + inetSocketAddress.getHostString() + " was not successful. Retrying...");
181                         future.channel().close();
182                         scheduleConnect(connectionName, inetSocketAddress, tlsMode, 5000);
183                     } else {
184                         connectionEstablished(connectionName, future.channel());
185                         addCloseDetectListener(future.channel());
186                     }
187                 }
188
189             private void addCloseDetectListener(Channel channel) {
190                 channel.closeFuture().addListener((ChannelFutureListener) future -> {
191                     Log.d("Connection to " + inetSocketAddress.getHostString() + " was lost. Retrying...");
192                     scheduleConnect(connectionName, inetSocketAddress, tlsMode, 5);
193                 });
194             }
195         });
196     }
197
198     private void scheduleConnect(String connectionName, InetSocketAddress inetSocketAddress, boolean tlsMode, long millis) {
199         mTimer.schedule( new TimerTask() {
200             @Override
201             public void run() {
202                 doConnect(connectionName, inetSocketAddress, tlsMode);
203             }
204         }, millis );
205     }
206
207     public void connectionEstablished(String connectionName, Channel channel) {
208         CoapClient coapClient = new CoapClient(channel);
209         mChannelMap.put(channel, coapClient);
210         ConnectorPool.addConnection(connectionName, coapClient);
211     }
212
213     public void disconenct() throws Exception {
214         mConnectorGroup.shutdownGracefully().await();
215     }
216 }