*/
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);
+ }
+ }
}