09ebc434207a34bb901beb4eb40d2da9ca3e2a8d
[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.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.iotivity.cloud.base.OICConstants;
39 import org.iotivity.cloud.base.protocols.coap.*;
40 import org.iotivity.cloud.base.protocols.coap.PingMessage;
41
42 import javax.net.ssl.SSLException;
43 import java.io.File;
44 import java.net.InetSocketAddress;
45 import java.util.*;
46
47 public class CoapConnector {
48     private final static Logger Log             = LoggerFactory.getLogger(CoapConnector.class);
49     public CoapConnector() {
50
51         mBootstrap.group(mConnectorGroup);
52         mBootstrap.channel(NioSocketChannel.class);
53         mBootstrap.option(ChannelOption.TCP_NODELAY, true);
54         mBootstrap.option(ChannelOption.SO_KEEPALIVE, true);
55         mBootstrap.option(ChannelOption.SO_REUSEADDR, true);
56     }
57
58     @Sharable
59     private class CoapPacketHandler
60             extends SimpleChannelInboundHandler<CoapResponse> {
61
62         @Override
63         protected void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
64                 throws Exception {
65             mChannelMap.get(ctx.channel()).onResponseReceived(msg);
66         }
67     }
68
69     public static class KeepAliveHandler extends ChannelDuplexHandler {
70         @Override
71         public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
72                 throws Exception {
73             if (evt instanceof IdleStateEvent) {
74                 IdleStateEvent event = (IdleStateEvent) evt;
75                 if (event.state() == IdleState.WRITER_IDLE) {
76                     mChannelMap.get(ctx.channel()).sendRequest(PingMessage.build(),null);
77                 }
78                 if (event.state() == IdleState.READER_IDLE) {
79                     Log.debug("Connection with" +  ctx.channel().remoteAddress().toString() + "is idle. Closing connection.");
80                     ctx.close();
81                 }
82             }
83         }
84     }
85
86     public static class CoapConnectorInitializer
87             extends ChannelInitializer<SocketChannel> {
88
89         private List<ChannelHandler> additionalHandlers = new ArrayList<>();
90
91         private Boolean              mTlsMode           = false;
92         private Boolean              mKeepAlive         = false;
93         InetSocketAddress            mInetSocketAddress = null;
94         String                       mRootCertFiePath   = null;
95
96         public void setTlsMode(Boolean tlsMode) {
97             this.mTlsMode = tlsMode;
98         }
99
100         public void setKeepAlive(Boolean keepAlive) {
101             this.mKeepAlive = keepAlive;
102         }
103
104         public void setInetSocketAddress(InetSocketAddress address) {
105             this.mInetSocketAddress = address;
106         }
107
108         public void setRootCertFilePath(String path) {
109             this.mRootCertFiePath = path;
110         }
111
112         public void addHandler(ChannelHandler handler) {
113             additionalHandlers.add(handler);
114         }
115
116         @Override
117         public void initChannel(SocketChannel ch) {
118             ChannelPipeline p = ch.pipeline();
119
120             SslContext sslContext = null;
121
122             if (mTlsMode.equals(true)) {
123
124                 File rootCert = new File(mRootCertFiePath);
125
126                 try {
127                     sslContext = SslContextBuilder.forClient()
128                             .sslProvider(SslProvider.JDK).trustManager(rootCert)
129                             .build();
130                 } catch (SSLException e) {
131                     e.printStackTrace();
132                 }
133
134                 final SslContext sslCtx = sslContext;
135                 p.addLast(sslCtx.newHandler(ch.alloc(),
136                         mInetSocketAddress.getHostString(),
137                         mInetSocketAddress.getPort()));
138             }
139
140             p.addLast(new CoapDecoder());
141             p.addLast(new CoapEncoder());
142             p.addLast(new CoapLogHandler());
143
144             if (mKeepAlive.equals(true)) {
145                 p.addLast(new IdleStateHandler(100, 45, 0));
146                 p.addLast(new KeepAliveHandler());
147             }
148
149             for (ChannelHandler handler : additionalHandlers) {
150                 p.addLast(handler);
151             }
152         }
153     }
154
155     private static Map<Channel, CoapClient> mChannelMap     = new HashMap<>();
156     Bootstrap                    mBootstrap      = new Bootstrap();
157     EventLoopGroup               mConnectorGroup = new NioEventLoopGroup();
158     Timer                        mTimer          = new Timer();
159
160     public void connect(final String connectionName, final InetSocketAddress inetSocketAddress,
161             boolean tlsMode, boolean keepAlive) {
162
163         CoapConnectorInitializer initializer = new CoapConnectorInitializer();
164
165         if (tlsMode == true) {
166             initializer.setTlsMode(true);
167             initializer.setInetSocketAddress(inetSocketAddress);
168             initializer.setRootCertFilePath(OICConstants.ROOT_CERT_FILE);
169         }
170
171         initializer.setKeepAlive(keepAlive);
172         initializer.addHandler(new CoapPacketHandler());
173         mBootstrap.handler(initializer);
174         doConnect(connectionName, inetSocketAddress, tlsMode);
175     }
176
177     private void doConnect(final String connectionName, final InetSocketAddress inetSocketAddress, final boolean tlsMode) {
178         mBootstrap.connect(inetSocketAddress).addListener(new ChannelFutureListener() {
179                 @Override public void operationComplete(ChannelFuture future) throws Exception {
180                     if(!future.isSuccess()) {
181                         Log.debug("Connection to " + inetSocketAddress.getHostString() + " was not successful. Retrying...");
182                         future.channel().close();
183                         scheduleConnect(connectionName, inetSocketAddress, tlsMode, 5000);
184                     } else {
185                         connectionEstablished(connectionName, future.channel());
186                         addCloseDetectListener(future.channel());
187                     }
188                 }
189
190             private void addCloseDetectListener(Channel channel) {
191                 channel.closeFuture().addListener((ChannelFutureListener) future -> {
192                     Log.debug("Connection to " + inetSocketAddress.getHostString() + " was lost. Retrying...");
193                     scheduleConnect(connectionName, inetSocketAddress, tlsMode, 5);
194                 });
195             }
196         });
197     }
198
199     private void scheduleConnect(String connectionName, InetSocketAddress inetSocketAddress, boolean tlsMode, long millis) {
200         mTimer.schedule( new TimerTask() {
201             @Override
202             public void run() {
203                 doConnect(connectionName, inetSocketAddress, tlsMode);
204             }
205         }, millis );
206     }
207
208     public void connectionEstablished(String connectionName, Channel channel) {
209         CoapClient coapClient = new CoapClient(channel);
210         mChannelMap.put(channel, coapClient);
211         ConnectorPool.addConnection(connectionName, coapClient);
212     }
213
214     public void disconenct() throws Exception {
215         mConnectorGroup.shutdownGracefully().await();
216     }
217 }