MessageEndEncoder   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 5
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 5
ccs 0
cts 2
cp 0
rs 10
eloc 5
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A encode(ChannelHandlerContext,Object,List) 0 3 1
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.core.network.netty;
21
22
import fr.quatrevieux.araknemu.core.network.Server;
23
import fr.quatrevieux.araknemu.core.network.SessionIdle;
24
import fr.quatrevieux.araknemu.core.network.session.Session;
25
import fr.quatrevieux.araknemu.core.network.session.SessionFactory;
26
import io.netty.bootstrap.ServerBootstrap;
27
import io.netty.buffer.Unpooled;
28
import io.netty.channel.Channel;
29
import io.netty.channel.ChannelHandler;
30
import io.netty.channel.ChannelHandlerContext;
31
import io.netty.channel.ChannelInitializer;
32
import io.netty.channel.EventLoopGroup;
33
import io.netty.channel.nio.NioEventLoopGroup;
34
import io.netty.channel.socket.nio.NioServerSocketChannel;
35
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
36
import io.netty.handler.codec.MessageToMessageEncoder;
37
import io.netty.handler.codec.string.StringDecoder;
38
import io.netty.handler.codec.string.StringEncoder;
39
import io.netty.handler.timeout.IdleStateEvent;
40
import io.netty.handler.timeout.IdleStateHandler;
41
import io.netty.util.CharsetUtil;
42
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
43
44
import java.time.Duration;
45
import java.util.Collection;
46
import java.util.Collections;
47
import java.util.List;
48
import java.util.concurrent.TimeUnit;
49
50
/**
51
 * Server adapter for Netty
52
 */
53
public final class NettyServer<S extends Session> implements Server<S> {
54
    private final SessionFactory<S> factory;
55
    private final int port;
56
    private final Duration readTimeout;
57
58
    private @MonotonicNonNull Channel serverChannel;
59
    private @MonotonicNonNull EventLoopGroup loopGroup;
60
    private @MonotonicNonNull SessionHandlerAdapter<S> handlerAdapter;
61
62 1
    public NettyServer(SessionFactory<S> factory, int port, Duration readTimeout) {
63 1
        this.factory = factory;
64 1
        this.port = port;
65 1
        this.readTimeout = readTimeout;
66 1
    }
67
68
    @Override
69
    public void start() {
70
        final ServerBootstrap bootstrap = new ServerBootstrap();
71
72
        handlerAdapter = new SessionHandlerAdapter<>(factory);
73
74
        final StringDecoder decoder = new StringDecoder(CharsetUtil.UTF_8);
75
        final StringEncoder encoder = new StringEncoder(CharsetUtil.UTF_8);
76
        final MessageToMessageEncoder<Object> messageEncoder = new MessageEndEncoder();
77
78
        bootstrap
79
            .group(loopGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors()))
80
            .channel(NioServerSocketChannel.class)
81
            .childHandler(new ChannelInitializer<Channel>() {
82
                protected void initChannel(Channel channel) {
83
                    channel
84
                        .pipeline()
85
                        .addLast(new DelimiterBasedFrameDecoder(4096, Unpooled.wrappedBuffer(new byte[]{10, 0})))
86
                        .addLast(encoder)
87
                        .addLast(decoder)
88
                        .addLast(messageEncoder)
89
                        .addLast(new IdleStateHandler(readTimeout.toMillis(), 0, 0, TimeUnit.MILLISECONDS) {
90
                            @Override
91
                            protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
92
                                ctx.fireChannelRead(new SessionIdle(readTimeout));
93
                            }
94
                        })
95
                        .addLast(handlerAdapter)
96
                    ;
97
                }
98
            })
99
        ;
100
101
        serverChannel = bootstrap
102
            .localAddress(port)
103
            .bind()
104
            .channel()
105
        ;
106
    }
107
108
    @Override
109
    public void stop() throws Exception {
110
        if (loopGroup == null || serverChannel == null) {
111
            return;
112
        }
113
114
        loopGroup.shutdownGracefully().sync();
115
        serverChannel.closeFuture().sync();
116
    }
117
118
    @Override
119
    public Collection<S> sessions() {
120
        return handlerAdapter != null ? handlerAdapter.sessions() : Collections.emptyList();
121
    }
122
123
    @ChannelHandler.Sharable
124
    public static final class MessageEndEncoder extends MessageToMessageEncoder<Object> {
125
        @Override
126
        protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) {
127
            out.add(msg + "\000");
128
        }
129
    }
130
}
131