Reduce ByteBuf.writeZero() usage
[bgpcep.git] / bgp / parser-spi / src / main / java / org / opendaylight / protocol / bgp / parser / spi / pojo / SimpleNlriRegistry.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.bgp.parser.spi.pojo;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.Iterables;
14 import io.netty.buffer.ByteBuf;
15 import io.netty.buffer.Unpooled;
16 import java.util.AbstractMap.SimpleEntry;
17 import java.util.Map.Entry;
18 import java.util.Optional;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.ConcurrentMap;
21 import org.opendaylight.bgp.concepts.NextHopUtil;
22 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
23 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
24 import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
25 import org.opendaylight.protocol.bgp.parser.spi.NextHopParserSerializer;
26 import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
27 import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
28 import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
29 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
30 import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.next.hop.CNextHop;
40 import org.opendaylight.yangtools.concepts.AbstractRegistration;
41 import org.opendaylight.yangtools.concepts.Registration;
42 import org.opendaylight.yangtools.yang.binding.DataObject;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 final class SimpleNlriRegistry implements NlriRegistry {
47
48     private static final int RESERVED = 1;
49     private static final String PARSER_NOT_FOUND = "Nlri parser not found for table type {}";
50     private static final Logger LOG = LoggerFactory.getLogger(SimpleNlriRegistry.class);
51
52     private final ConcurrentMap<BgpTableType, NlriParser> handlers = new ConcurrentHashMap<>();
53     private final ConcurrentMap<Class<? extends DataObject>, NlriSerializer> serializers = new ConcurrentHashMap<>();
54     private final ConcurrentMap<BgpTableType, NextHopParserSerializer> nextHopParsers = new ConcurrentHashMap<>();
55     private final ConcurrentMap<Entry<Class<? extends CNextHop>, BgpTableType>,
56             NextHopParserSerializer> nextHopSerializers = new ConcurrentHashMap<>();
57     private final SubsequentAddressFamilyRegistry safiReg;
58     private final AddressFamilyRegistry afiReg;
59
60     SimpleNlriRegistry(final AddressFamilyRegistry afiReg, final SubsequentAddressFamilyRegistry safiReg) {
61         this.afiReg = requireNonNull(afiReg);
62         this.safiReg = requireNonNull(safiReg);
63     }
64
65     private static BgpTableType createKey(final Class<? extends AddressFamily> afi,
66         final Class<? extends SubsequentAddressFamily> safi) {
67         requireNonNull(afi);
68         requireNonNull(safi);
69         return new BgpTableTypeImpl(afi, safi);
70     }
71
72     synchronized Registration registerNlriSerializer(final Class<? extends DataObject> nlriClass,
73             final NlriSerializer serializer) {
74         final NlriSerializer prev = this.serializers.get(nlriClass);
75         checkState(prev == null, "Serializer already bound to class " + prev);
76
77         this.serializers.put(nlriClass, serializer);
78
79         final Object lock = this;
80         return new AbstractRegistration() {
81             @Override
82             protected void removeRegistration() {
83                 synchronized (lock) {
84                     SimpleNlriRegistry.this.serializers.remove(nlriClass);
85                 }
86             }
87         };
88     }
89
90     synchronized Registration registerNlriParser(final Class<? extends AddressFamily> afi,
91         final Class<? extends SubsequentAddressFamily> safi, final NlriParser parser,
92         final NextHopParserSerializer nextHopSerializer, final Class<? extends CNextHop> cnextHopClass,
93         final Class<? extends CNextHop>... cnextHopClassList) {
94         final BgpTableType key = createKey(afi, safi);
95         final NlriParser prev = this.handlers.get(key);
96         checkState(prev == null, "AFI/SAFI is already bound to parser " + prev);
97
98         this.handlers.put(key, parser);
99         this.nextHopParsers.put(key,nextHopSerializer);
100
101         if (cnextHopClass != null) {
102             final Entry<Class<? extends CNextHop>, BgpTableType> nhKey = new SimpleEntry<>(cnextHopClass, key);
103             this.nextHopSerializers.put(nhKey, nextHopSerializer);
104             for (final Class<? extends CNextHop> cnextHop : cnextHopClassList) {
105                 final Entry<Class<? extends CNextHop>, BgpTableType> nhKeys = new SimpleEntry<>(cnextHop, key);
106                 this.nextHopSerializers.put(nhKeys, nextHopSerializer);
107             }
108         }
109
110         final Object lock = this;
111         return new AbstractRegistration() {
112             @Override
113             protected void removeRegistration() {
114                 synchronized (lock) {
115                     SimpleNlriRegistry.this.handlers.remove(key);
116                     SimpleNlriRegistry.this.nextHopParsers.remove(key);
117                     if (cnextHopClass != null) {
118                         final Entry<Class<? extends CNextHop>, BgpTableType> nhKey
119                                 = new SimpleEntry<>(cnextHopClass, key);
120                         SimpleNlriRegistry.this.nextHopSerializers.remove(nhKey);
121                         for (final Class<? extends CNextHop> cnextHop : cnextHopClassList) {
122                             final Entry<Class<? extends CNextHop>, BgpTableType> nhKeys
123                                     = new SimpleEntry<>(cnextHop, key);
124                             SimpleNlriRegistry.this.nextHopSerializers.remove(nhKeys);
125                         }
126                     }
127                 }
128             }
129         };
130     }
131
132     private Class<? extends AddressFamily> getAfi(final ByteBuf buffer) throws BGPParsingException {
133         final int afiVal = buffer.readUnsignedShort();
134         final Class<? extends AddressFamily> afi = this.afiReg.classForFamily(afiVal);
135         if (afi == null) {
136             throw new BGPParsingException("Address Family Identifier: '" + afiVal + "' not supported.");
137         }
138         return afi;
139     }
140
141     private Class<? extends SubsequentAddressFamily> getSafi(final ByteBuf buffer) throws BGPParsingException {
142         final int safiVal = buffer.readUnsignedByte();
143         final Class<? extends SubsequentAddressFamily> safi = this.safiReg.classForFamily(safiVal);
144         if (safi == null) {
145             throw new BGPParsingException("Subsequent Address Family Identifier: '" + safiVal + "' not supported.");
146         }
147         return safi;
148     }
149
150     @Override
151     public MpUnreachNlri parseMpUnreach(final ByteBuf buffer, final PeerSpecificParserConstraint constraint)
152             throws BGPParsingException {
153         final MpUnreachNlriBuilder builder = new MpUnreachNlriBuilder();
154         builder.setAfi(getAfi(buffer));
155         builder.setSafi(getSafi(buffer));
156
157         if (buffer.isReadable()) {
158             final ByteBuf nlri = buffer.slice();
159             final BgpTableType key = createKey(builder.getAfi(), builder.getSafi());
160             final NlriParser parser = this.handlers.get(key);
161             if (parser == null) {
162                 LOG.warn(PARSER_NOT_FOUND, key);
163             } else {
164                 parser.parseNlri(nlri, builder, constraint);
165             }
166         }
167         return builder.build();
168     }
169
170     @Override
171     public void serializeMpReach(final MpReachNlri mpReachNlri, final ByteBuf byteAggregator) {
172         final Class<? extends AddressFamily> afi = mpReachNlri.getAfi();
173         final Class<? extends SubsequentAddressFamily> safi = mpReachNlri.getSafi();
174         byteAggregator.writeShort(this.afiReg.numberForClass(afi));
175         byteAggregator.writeByte(this.safiReg.numberForClass(safi));
176
177         final CNextHop cNextHop = mpReachNlri.getCNextHop();
178         if (cNextHop != null) {
179             final Entry<Class<? extends CNextHop>, BgpTableType> key = new SimpleEntry(
180                     cNextHop.implementedInterface(), new BgpTableTypeImpl(afi, safi));
181             final NextHopParserSerializer nextHopSerializer = this.nextHopSerializers.get(key);
182             final ByteBuf nextHopBuffer = Unpooled.buffer();
183             nextHopSerializer.serializeNextHop(cNextHop, nextHopBuffer);
184             byteAggregator.writeByte(nextHopBuffer.writerIndex());
185             byteAggregator.writeBytes(nextHopBuffer);
186
187         } else {
188             byteAggregator.writeByte(0);
189         }
190         byteAggregator.writeZero(RESERVED);
191     }
192
193     @Override
194     public void serializeMpUnReach(final MpUnreachNlri mpUnreachNlri, final ByteBuf byteAggregator) {
195         byteAggregator.writeShort(this.afiReg.numberForClass(mpUnreachNlri.getAfi()));
196         byteAggregator.writeByte(this.safiReg.numberForClass(mpUnreachNlri.getSafi()));
197     }
198
199     @Override
200     public Iterable<NlriSerializer> getSerializers() {
201         return Iterables.unmodifiableIterable(this.serializers.values());
202     }
203
204     @Override
205     public MpReachNlri parseMpReach(final ByteBuf buffer, final PeerSpecificParserConstraint constraint)
206             throws BGPParsingException {
207         final MpReachNlriBuilder builder = new MpReachNlriBuilder();
208         final Class<? extends AddressFamily> afi = getAfi(buffer);
209         final Class<? extends SubsequentAddressFamily> safi = getSafi(buffer);
210         builder.setAfi(afi);
211         builder.setSafi(safi);
212
213         final BgpTableType key = createKey(builder.getAfi(), builder.getSafi());
214
215         final int nextHopLength = buffer.readUnsignedByte();
216         if (nextHopLength != 0) {
217             final NextHopParserSerializer nextHopParser = this.nextHopParsers.get(key);
218             if (nextHopParser != null) {
219                 builder.setCNextHop(nextHopParser.parseNextHop(buffer.readSlice(nextHopLength)));
220             } else {
221                 builder.setCNextHop(NextHopUtil.parseNextHop(buffer.readSlice(nextHopLength)));
222                 LOG.warn("NexHop Parser/Serializer for AFI/SAFI ({},{}) not bound",afi,safi);
223             }
224         }
225         buffer.skipBytes(RESERVED);
226
227         final ByteBuf nlri = buffer.slice();
228         final NlriParser parser = this.handlers.get(key);
229         if (parser == null) {
230             LOG.warn(PARSER_NOT_FOUND, key);
231         } else {
232             parser.parseNlri(nlri, builder, constraint);
233         }
234         return builder.build();
235     }
236
237     @Override
238     public Optional<MpUnreachNlri> convertMpReachToMpUnReach(final MpReachNlri mpReachNlri,
239             final MpUnreachNlri mpUnreachNlri) {
240         if (mpUnreachNlri == null) {
241             return Optional.of(new MpUnreachNlriBuilder()
242                     .setWithdrawnRoutes(new WithdrawnRoutesBuilder()
243                             .setDestinationType(mpReachNlri.getAdvertizedRoutes().getDestinationType())
244                             .build())
245                     .build());
246         }
247
248         final BgpTableType key = createKey(mpUnreachNlri.getAfi(), mpUnreachNlri.getSafi());
249         final NlriParser parser = this.handlers.get(key);
250         if (parser == null) {
251             LOG.debug("Parser for {} not found", key);
252             return Optional.empty();
253         }
254
255         final MpUnreachNlriBuilder builder = new MpUnreachNlriBuilder(mpUnreachNlri);
256         return parser.convertMpReachToMpUnReach(mpReachNlri, builder) ? Optional.of(builder.build()) : Optional.empty();
257     }
258 }