BUG-2982 : moved path-attributes container to grouping
[bgpcep.git] / bgp / parser-spi / src / main / java / org / opendaylight / protocol / bgp / parser / spi / pojo / SimpleAttributeRegistry.java
index 7fda5a50b1ad0701a3400654f1d59c1483381ad8..923bb575096210ee118f47a7d5f540b18c5184e4 100644 (file)
  */
 package org.opendaylight.protocol.bgp.parser.spi.pojo;
 
+import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicReference;
 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
 import org.opendaylight.protocol.bgp.parser.BGPError;
 import org.opendaylight.protocol.bgp.parser.BGPParsingException;
 import org.opendaylight.protocol.bgp.parser.spi.AttributeParser;
 import org.opendaylight.protocol.bgp.parser.spi.AttributeRegistry;
 import org.opendaylight.protocol.bgp.parser.spi.AttributeSerializer;
+import org.opendaylight.protocol.concepts.AbstractRegistration;
 import org.opendaylight.protocol.concepts.HandlerRegistry;
+import org.opendaylight.protocol.util.BitArray;
 import org.opendaylight.protocol.util.ByteArray;
-import org.opendaylight.protocol.util.Util;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.PathAttributes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130918.update.PathAttributesBuilder;
+import org.opendaylight.protocol.util.Values;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.AttributesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.UnrecognizedAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.UnrecognizedAttributesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.attributes.UnrecognizedAttributesKey;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
-
-import com.google.common.base.Preconditions;
-import com.google.common.primitives.UnsignedBytes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 final class SimpleAttributeRegistry implements AttributeRegistry {
-       private final HandlerRegistry<DataContainer, AttributeParser, AttributeSerializer> handlers = new HandlerRegistry<>();
 
-       AutoCloseable registerAttributeParser(final int attributeType, final AttributeParser parser) {
-               Preconditions.checkArgument(attributeType >= 0 && attributeType <= Util.UNSIGNED_BYTE_MAX_VALUE);
-               return this.handlers.registerParser(attributeType, parser);
-       }
+    private static final class RawAttribute {
+        private final AttributeParser parser;
+        private final ByteBuf buffer;
+
+        public RawAttribute(final AttributeParser parser, final ByteBuf buffer) {
+            this.parser = Preconditions.checkNotNull(parser);
+            this.buffer = Preconditions.checkNotNull(buffer);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(SimpleAttributeRegistry.class);
+    private static final int OPTIONAL_BIT = 0;
+    private static final int TRANSITIVE_BIT = 1;
+    private static final int PARTIAL_BIT = 2;
+    private static final int EXTENDED_LENGTH_BIT = 3;
+    private final HandlerRegistry<DataContainer, AttributeParser, AttributeSerializer> handlers = new HandlerRegistry<>();
+    private final Map<AbstractRegistration, AttributeSerializer> serializers = new LinkedHashMap<>();
+    private final AtomicReference<Iterable<AttributeSerializer>> roSerializers =
+        new AtomicReference<Iterable<AttributeSerializer>>(this.serializers.values());
+    private final List<UnrecognizedAttributes> unrecognizedAttributes = new ArrayList<>();
+
 
-       AutoCloseable registerAttributeSerializer(final Class<? extends DataObject> paramClass, final AttributeSerializer serializer) {
-               return this.handlers.registerSerializer(paramClass, serializer);
-       }
+    AutoCloseable registerAttributeParser(final int attributeType, final AttributeParser parser) {
+        Preconditions.checkArgument(attributeType >= 0 && attributeType <= Values.UNSIGNED_BYTE_MAX_VALUE);
+        return this.handlers.registerParser(attributeType, parser);
+    }
 
-       private int parseAttribute(final byte[] bytes, final int offset, final PathAttributesBuilder builder) throws BGPDocumentedException,
-                       BGPParsingException {
-               // FIXME: validate minimum length
-               final boolean[] flags = ByteArray.parseBits(bytes[offset]);
-               final int type = UnsignedBytes.toInt(bytes[offset + 1]);
-               final int hdrlen;
-               final int len;
-               if (flags[3]) {
-                       len = UnsignedBytes.toInt(bytes[offset + 2]) * 256 + UnsignedBytes.toInt(bytes[offset + 3]);
-                       hdrlen = 4;
-               } else {
-                       len = UnsignedBytes.toInt(bytes[offset + 2]);
-                       hdrlen = 3;
-               }
+    synchronized AutoCloseable registerAttributeSerializer(final Class<? extends DataObject> paramClass, final AttributeSerializer serializer) {
+        final AbstractRegistration reg = this.handlers.registerSerializer(paramClass, serializer);
 
-               final AttributeParser parser = this.handlers.getParser(type);
-               if (parser == null) {
-                       if (!flags[0]) {
-                               throw new BGPDocumentedException("Well known attribute not recognized.", BGPError.WELL_KNOWN_ATTR_NOT_RECOGNIZED);
-                       }
-               } else {
-                       parser.parseAttribute(ByteArray.subByte(bytes, offset + hdrlen, len), builder);
-               }
+        this.serializers.put(reg, serializer);
+        return new AbstractRegistration() {
+            @Override
+            protected void removeRegistration() {
+                synchronized (SimpleAttributeRegistry.this) {
+                    SimpleAttributeRegistry.this.serializers.remove(reg);
+                    SimpleAttributeRegistry.this.roSerializers.set(SimpleAttributeRegistry.this.serializers.values());
+                }
+                reg.close();
+            }
+        };
+    }
 
-               return hdrlen + len;
-       }
+    private void addAttribute(final ByteBuf buffer, final Map<Integer, RawAttribute> attributes) throws BGPDocumentedException {
+        final BitArray flags = BitArray.valueOf(buffer.readByte());
+        final int type = buffer.readUnsignedByte();
+        final int len = (flags.get(EXTENDED_LENGTH_BIT)) ? buffer.readUnsignedShort() : buffer.readUnsignedByte();
+        if (!attributes.containsKey(type)) {
+            final AttributeParser parser = this.handlers.getParser(type);
+            if (parser == null) {
+                if (!flags.get(OPTIONAL_BIT)) {
+                    throw new BGPDocumentedException("Well known attribute not recognized.", BGPError.WELL_KNOWN_ATTR_NOT_RECOGNIZED);
+                }
+                final UnrecognizedAttributes unrecognizedAttribute = new UnrecognizedAttributesBuilder()
+                    .setKey(new UnrecognizedAttributesKey((short)type))
+                    .setPartial(flags.get(PARTIAL_BIT))
+                    .setTransitive(flags.get(TRANSITIVE_BIT))
+                    .setType((short)type)
+                    .setValue(ByteArray.readBytes(buffer, len)).build();
+                this.unrecognizedAttributes.add(unrecognizedAttribute);
+                LOG.debug("Unrecognized attribute were parsed: {}", unrecognizedAttribute);
+            } else {
+                attributes.put(type, new RawAttribute(parser, buffer.readSlice(len)));
+            }
+        } else {
+            LOG.debug("Ignoring duplicate attribute type {}", type);
+        }
+    }
 
-       @Override
-       public PathAttributes parseAttributes(final byte[] bytes) throws BGPDocumentedException, BGPParsingException {
-               int byteOffset = 0;
-               final PathAttributesBuilder builder = new PathAttributesBuilder();
-               while (byteOffset < bytes.length) {
-                       byteOffset += parseAttribute(bytes, byteOffset, builder);
-               }
-               return builder.build();
-       }
+    @Override
+    public Attributes parseAttributes(final ByteBuf buffer) throws BGPDocumentedException, BGPParsingException {
+        final Map<Integer, RawAttribute> attributes = new TreeMap<>();
+        while (buffer.isReadable()) {
+            addAttribute(buffer, attributes);
+        }
+        /*
+         * TreeMap guarantees that we will be invoking the parser in the order
+         * of increasing attribute type.
+         */
+        final AttributesBuilder builder = new AttributesBuilder();
+        for (final Entry<Integer, RawAttribute> e : attributes.entrySet()) {
+            LOG.debug("Parsing attribute type {}", e.getKey());
 
-       @Override
-       public byte[] serializeAttribute(final DataObject attribute) {
-               final AttributeSerializer serializer = this.handlers.getSerializer(attribute.getImplementedInterface());
-               if (serializer == null) {
-                       return null;
-               }
+            final RawAttribute a = e.getValue();
+            a.parser.parseAttribute(a.buffer, builder);
+        }
+        builder.setUnrecognizedAttributes(this.unrecognizedAttributes);
+        return builder.build();
+    }
 
-               return serializer.serializeAttribute(attribute);
-       }
+    @Override
+    public void serializeAttribute(final DataObject attribute,final ByteBuf byteAggregator) {
+        for (final AttributeSerializer serializer : this.roSerializers.get()) {
+            serializer.serializeAttribute(attribute, byteAggregator);
+        }
+    }
 }