Do not instantiate constant lists on each message
[controller.git] / opendaylight / netconf / netconf-util / src / main / java / org / opendaylight / controller / netconf / util / messages / NetconfMessageFactory.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
9 package org.opendaylight.controller.netconf.util.messages;
10
11 import java.io.ByteArrayInputStream;
12 import java.io.IOException;
13 import java.nio.ByteBuffer;
14 import java.util.Arrays;
15 import java.util.List;
16
17 import org.opendaylight.controller.netconf.api.NetconfDeserializerException;
18 import org.opendaylight.controller.netconf.api.NetconfMessage;
19 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
20 import org.opendaylight.protocol.framework.DeserializerException;
21 import org.opendaylight.protocol.framework.DocumentedException;
22 import org.opendaylight.protocol.framework.ProtocolMessageFactory;
23 import org.opendaylight.protocol.util.ByteArray;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.w3c.dom.Comment;
27 import org.w3c.dom.Document;
28 import org.xml.sax.SAXException;
29
30 import com.google.common.base.Charsets;
31 import com.google.common.base.Optional;
32 import com.google.common.collect.ImmutableList;
33
34 /**
35  * NetconfMessageFactory for (de)serializing DOM documents.
36  */
37 public final class NetconfMessageFactory implements ProtocolMessageFactory<NetconfMessage> {
38
39     private static final Logger logger = LoggerFactory.getLogger(NetconfMessageFactory.class);
40     private static final List<byte[]> POSSIBLE_STARTS = ImmutableList.of(
41         "[".getBytes(Charsets.UTF_8), "\r\n[".getBytes(Charsets.UTF_8), "\n[".getBytes(Charsets.UTF_8));
42     private static final List<byte[]> POSSIBLE_ENDS = ImmutableList.of(
43         "]\n".getBytes(Charsets.UTF_8), "]\r\n".getBytes(Charsets.UTF_8));
44
45     private final Optional<String> clientId;
46
47     public NetconfMessageFactory() {
48         clientId = Optional.absent();
49     }
50
51     public NetconfMessageFactory(Optional<String> clientId) {
52         this.clientId = clientId;
53     }
54
55     @Override
56     public NetconfMessage parse(byte[] bytes) throws DeserializerException, DocumentedException {
57         logMessage(bytes);
58
59         String additionalHeader = null;
60
61         if (startsWithAdditionalHeader(bytes)) {
62             // Auth information containing username, ip address... extracted for monitoring
63             int endOfAuthHeader = getAdditionalHeaderEndIndex(bytes);
64             if (endOfAuthHeader > -1) {
65                 byte[] additionalHeaderBytes = Arrays.copyOfRange(bytes, 0, endOfAuthHeader + 2);
66                 additionalHeader = additionalHeaderToString(additionalHeaderBytes);
67                 bytes = Arrays.copyOfRange(bytes, endOfAuthHeader + 2, bytes.length);
68             }
69         }
70         NetconfMessage message;
71         try {
72             Document doc = XmlUtil.readXmlToDocument(new ByteArrayInputStream(bytes));
73             message = new NetconfMessage(doc, additionalHeader);
74         } catch (final SAXException | IOException | IllegalStateException e) {
75             throw new NetconfDeserializerException("Could not parse message from " + new String(bytes), e);
76         }
77         return message;
78     }
79
80     private int getAdditionalHeaderEndIndex(byte[] bytes) {
81         for (byte[] possibleEnd : POSSIBLE_ENDS) {
82             int idx = ByteArray.findByteSequence(bytes, possibleEnd);
83
84             if (idx != -1) {
85                 return idx;
86             }
87         }
88
89         return -1;
90     }
91
92     private boolean startsWithAdditionalHeader(byte[] bytes) {
93         for (byte[] possibleStart : POSSIBLE_STARTS) {
94             int i = 0;
95             for (byte b : possibleStart) {
96                 if(bytes[i] != b)
97                     break;
98
99                 return true;
100             }
101         }
102
103         return false;
104     };
105
106     private void logMessage(byte[] bytes) {
107         String s = Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
108         logger.debug("Parsing message \n{}", s);
109     }
110
111     private String additionalHeaderToString(byte[] bytes) {
112         return Charsets.UTF_8.decode(ByteBuffer.wrap(bytes)).toString();
113     }
114
115     @Override
116     public byte[] put(NetconfMessage netconfMessage) {
117         if (clientId.isPresent()) {
118             Comment comment = netconfMessage.getDocument().createComment("clientId:" + clientId.get());
119             netconfMessage.getDocument().appendChild(comment);
120         }
121         ByteBuffer msgBytes;
122         if(netconfMessage.getAdditionalHeader().isPresent()) {
123             String header = netconfMessage.getAdditionalHeader().get();
124             logger.trace("Header of netconf message parsed \n{}", header);
125             msgBytes = Charsets.UTF_8.encode(header + xmlToString(netconfMessage.getDocument()));
126         } else {
127             msgBytes = Charsets.UTF_8.encode(xmlToString(netconfMessage.getDocument()));
128         }
129         String content = xmlToString(netconfMessage.getDocument());
130
131         logger.trace("Putting message \n{}", content);
132         byte[] b = new byte[msgBytes.limit()];
133         msgBytes.get(b);
134         return b;
135     }
136
137     private String xmlToString(Document doc) {
138         return XmlUtil.toString(doc, false);
139     }
140 }