*/
package org.opendaylight.protocol.pcep.impl;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.UnpooledByteBufAllocator;
+
import java.util.ArrayList;
import java.util.List;
import org.opendaylight.protocol.framework.DocumentedException;
import org.opendaylight.protocol.framework.ProtocolMessageFactory;
import org.opendaylight.protocol.pcep.PCEPDeserializerException;
-import org.opendaylight.protocol.pcep.PCEPMessage;
-import org.opendaylight.protocol.pcep.spi.RawMessage;
+import org.opendaylight.protocol.pcep.spi.MessageHandlerRegistry;
+import org.opendaylight.protocol.pcep.spi.MessageSerializer;
+import org.opendaylight.protocol.util.ByteArray;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev131005.Message;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
+import com.google.common.primitives.UnsignedBytes;
/**
* A PCEP message parser which also does validation.
*/
-public final class PCEPMessageFactory implements ProtocolMessageFactory<PCEPMessage> {
- private static final RawPCEPMessageFactory rawFactory = new RawPCEPMessageFactory();
+public final class PCEPMessageFactory implements ProtocolMessageFactory<Message> {
+
+ private final static Logger logger = LoggerFactory.getLogger(PCEPMessageFactory.class);
+
+ private final static int TYPE_SIZE = 1; // bytes
+
+ private final static int LENGTH_SIZE = 2; // bytes
+
+ public final static int COMMON_HEADER_LENGTH = 4; // bytes
+
+ /**
+ * Current supported version of PCEP.
+ */
+ public static final int PCEP_VERSION = 1;
+
+ private static final int VERSION_SF_LENGTH = 3;
+
+ private static final int VER_FLAGS_MF_LENGTH = 1;
+ private static final int TYPE_F_LENGTH = 1;
+ private static final int LENGTH_F_LENGTH = 2;
+
+ private static final int VER_FLAGS_MF_OFFSET = 0;
+ private static final int TYPE_F_OFFSET = VER_FLAGS_MF_LENGTH + VER_FLAGS_MF_OFFSET;
+ private static final int LENGTH_F_OFFSET = TYPE_F_LENGTH + TYPE_F_OFFSET;
+
+ private final MessageHandlerRegistry registry;
+
+ public PCEPMessageFactory(final MessageHandlerRegistry registry) {
+ this.registry = Preconditions.checkNotNull(registry);
+ }
@Override
- public List<PCEPMessage> parse(final byte[] bytes) throws DeserializerException, DocumentedException {
- final List<PCEPMessage> parsed = rawFactory.parse(bytes);
- final List<PCEPMessage> validated = new ArrayList<>(parsed.size());
-
- for (PCEPMessage msg : parsed) {
- Preconditions.checkState(msg instanceof RawMessage);
- final RawMessage raw = (RawMessage) msg;
-
- try {
- validated.addAll(PCEPMessageValidator.getValidator(raw.getMsgType()).validate(raw.getAllObjects()));
- } catch (final PCEPDeserializerException e) {
- // FIXME: at validation time we may want to terminate with:
- //logger.error("Malformed message, terminating. ", e);
- // this.terminate(Reason.MALFORMED_MSG);
- throw e;
- }
+ public Message parse(final byte[] bytes) throws DeserializerException, DocumentedException {
+ Preconditions.checkArgument(bytes != null, "Bytes may not be null");
+ Preconditions.checkArgument(bytes.length != 0, "Bytes may not be empty");
+
+ logger.trace("Attempt to parse message from bytes: {}", ByteArray.bytesToHexString(bytes));
+
+ final int type = UnsignedBytes.toInt(bytes[1]);
+
+ final int msgLength = ByteArray.bytesToInt(ByteArray.subByte(bytes, TYPE_SIZE + 1, LENGTH_SIZE));
+
+ final byte[] msgBody = ByteArray.cutBytes(bytes, TYPE_SIZE + 1 + LENGTH_SIZE);
+
+ if (msgBody.length != msgLength - COMMON_HEADER_LENGTH) {
+ throw new DeserializerException("Body size " + msgBody.length + " does not match header size "
+ + (msgLength - COMMON_HEADER_LENGTH));
}
- return validated;
+ final List<Message> errors = new ArrayList<>();
+ Message msg = null;
+
+ try {
+ msg = this.registry.getMessageParser(type).parseMessage(msgBody, errors);
+ } catch (final PCEPDeserializerException e) {
+ logger.debug("Unexpected deserializer problem", e);
+ throw new DeserializerException(e.getMessage(), e);
+ }
+
+ if (!errors.isEmpty()) {
+ // FIXME: we have a bunch of error messages, how can we send them back?
+ }
+
+ logger.debug("Message was parsed. {}", msg);
+ return msg;
}
@Override
- public byte[] put(final PCEPMessage msg) {
- return rawFactory.put(msg);
+ public byte[] put(final Message msg) {
+ if (msg == null) {
+ throw new IllegalArgumentException("PCEPMessage is mandatory.");
+ }
+
+ final ByteBuf buf = new UnpooledByteBufAllocator(false).buffer();
+
+ final MessageSerializer serializer = this.registry.getMessageSerializer(msg);
+
+ serializer.serializeMessage(msg, buf);
+
+ final byte[] msgBody = new byte[buf.readableBytes()];
+
+ buf.getBytes(0, msgBody);
+
+ final byte[] headerBytes = new byte[COMMON_HEADER_LENGTH];
+
+ // msgVer_Flag
+ headerBytes[VER_FLAGS_MF_OFFSET] = (byte) (PCEP_VERSION << (Byte.SIZE - VERSION_SF_LENGTH));
+
+ // msgType
+ headerBytes[TYPE_F_OFFSET] = (byte) serializer.getMessageType();
+
+ // msgLength
+ System.arraycopy(ByteArray.intToBytes(msgBody.length + COMMON_HEADER_LENGTH), Integer.SIZE / Byte.SIZE - LENGTH_F_LENGTH,
+ headerBytes, LENGTH_F_OFFSET, LENGTH_F_LENGTH);
+
+ final byte[] retBytes = new byte[headerBytes.length + msgBody.length];
+
+ ByteArray.copyWhole(headerBytes, retBytes, 0);
+ ByteArray.copyWhole(msgBody, retBytes, COMMON_HEADER_LENGTH);
+
+ return retBytes;
}
}