sonar code issues addressed
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / rpc / CreateSubscription.java
1 /*
2  * ============LICENSE_START========================================================================
3  * ONAP : tr-069-adapter
4  * =================================================================================================
5  * Copyright (C) 2020 CommScope Inc Intellectual Property.
6  * =================================================================================================
7  * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
9  * may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14  * either express or implied. See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ===============LICENSE_END=======================================================================
17  */
18
19 package org.commscope.tr069adapter.netconf.rpc;
20
21 import com.google.common.base.Preconditions;
22 import com.google.common.collect.Maps;
23
24 import java.io.File;
25 import java.io.IOException;
26 import java.text.SimpleDateFormat;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ScheduledExecutorService;
36 import java.util.concurrent.TimeUnit;
37
38 import javax.xml.bind.JAXBContext;
39 import javax.xml.bind.JAXBException;
40 import javax.xml.bind.Unmarshaller;
41 import javax.xml.bind.annotation.XmlRootElement;
42 import org.commscope.tr069adapter.netconf.error.NetconfNotificationException;
43 import org.opendaylight.netconf.api.NetconfMessage;
44 import org.opendaylight.netconf.api.xml.XmlElement;
45 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
46 import org.opendaylight.netconf.api.xml.XmlUtil;
47 import org.opendaylight.netconf.impl.NetconfServerSession;
48 import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;
49 import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.Element;
54 import org.xml.sax.SAXException;
55
56 public class CreateSubscription extends AbstractLastNetconfOperation
57     implements DefaultNetconfOperation {
58
59   private static final Logger logger = LoggerFactory.getLogger(CreateSubscription.class);
60   private final Map<Notification, NetconfMessage> notifications;
61   private NetconfServerSession session;
62   private ScheduledExecutorService scheduledExecutorService;
63   private static Map<String, NetconfServerSession> netconfServerSessionMap = new HashMap<>();
64   private String deviceID;
65   private String swVersion;
66   private String hwVersion;
67
68   public CreateSubscription(final String id, final Optional<File> notificationsFile,
69       String deviceID, String swVersion, String hwVersion) {
70
71     super(id);
72
73     logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
74     this.deviceID = deviceID;
75     this.swVersion = swVersion;
76     this.hwVersion = hwVersion;
77
78     final Optional<Notifications> notifs;
79
80     if (notificationsFile.isPresent()) {
81       notifs = Optional.of(loadNotifications(notificationsFile.get()));
82       scheduledExecutorService = Executors.newScheduledThreadPool(1);
83     } else {
84       notifs = Optional.empty();
85     }
86
87     if (notifs.isPresent()) {
88       final Collection<Notification> toCopy = notifs.get().getNotificationList();
89       final Map<Notification, NetconfMessage> preparedMessages =
90           Maps.newHashMapWithExpectedSize(toCopy.size());
91       for (final Notification notification : toCopy) {
92         final NetconfMessage parsedNotification =
93             parseNetconfNotification(notification.getContent());
94         preparedMessages.put(notification, parsedNotification);
95       }
96       this.notifications = preparedMessages;
97     } else {
98       this.notifications = Collections.emptyMap();
99     }
100   }
101
102   private static Notifications loadNotifications(final File file) {
103     try {
104       final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
105       final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
106       return (Notifications) jaxbUnmarshaller.unmarshal(file);
107     } catch (final JAXBException e) {
108       throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",
109           e);
110     }
111   }
112
113   @Override
114   protected String getOperationName() {
115     return "create-subscription";
116   }
117
118   @Override
119   protected String getOperationNamespace() {
120     return "urn:ietf:params:xml:ns:netconf:notification:1.0";
121   }
122   
123   public String getSwVersion() {
124     return swVersion;
125   }
126
127   public String getHwVersion() {
128     return hwVersion;
129   }
130
131   @Override
132   protected Element handleWithNoSubsequentOperations(final Document document,
133       final XmlElement operationElement) {
134     long delayAggregator = 0;
135     for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
136       for (int i = 0; i <= notification.getKey().getTimes(); i++) {
137
138         delayAggregator += notification.getKey().getDelayInSeconds();
139
140         scheduledExecutorService.schedule(() -> {
141           Preconditions.checkState(session != null,
142               "Session is not set, cannot process notifications");
143           session.sendMessage(notification.getValue());
144         }, delayAggregator, TimeUnit.SECONDS);
145       }
146     }
147     return document.createElement(XmlNetconfConstants.OK);
148   }
149
150   private static NetconfMessage parseNetconfNotification(String content) {
151     final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
152     final int endEventTime = content.indexOf("</eventTime>");
153     final String eventTime = content.substring(startEventTime, endEventTime);
154     if (eventTime.equals("XXXX")) {
155       content = content.replace(eventTime,
156           new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
157     }
158
159     try {
160       return new NetconfMessage(XmlUtil.readXmlToDocument(content));
161     } catch (SAXException | IOException e) {
162       throw new IllegalArgumentException("Cannot parse notifications", e);
163     }
164   }
165
166   @Override
167   public void setNetconfSession(final NetconfServerSession newSession) {
168     logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
169     logger.debug("This CreateSubscription is setup to support  macID= {}", deviceID);
170     this.session = newSession;
171     netconfServerSessionMap.put(deviceID, newSession);
172   }
173
174   public static void sendNotification(NetconfMessage netconfMessage, String deviceID)
175       throws NetconfNotificationException {
176     logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
177     NetconfServerSession session = netconfServerSessionMap.get(deviceID);
178     if (session != null && session.isUp()) {
179       try {
180         session.sendMessage(netconfMessage);
181         logger.debug("Successfully send notification for deviceID: {}", deviceID);
182       } catch (Exception e) {
183         logger.error("Failed to send notification. while posting got error. {}", e.toString());
184         throw new NetconfNotificationException("Exception while posting the netconf message", e);
185       }
186
187     } else {
188       logger.debug(
189           "Failed to send notification. None of valid netconf session is available for {}.",
190           deviceID);
191       logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
192       throw new NetconfNotificationException("NetConf active session deosn't not exist");
193     }
194   }
195
196   @XmlRootElement(name = "notifications")
197   public static final class Notifications {
198
199     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
200     private List<Notification> notificationList;
201
202     public List<Notification> getNotificationList() {
203       return notificationList;
204     }
205
206     @Override
207     public String toString() {
208       final StringBuilder sb = new StringBuilder("Notifications{");
209       sb.append("notificationList=").append(notificationList);
210       sb.append('}');
211       return sb.toString();
212     }
213   }
214
215   public static final class Notification {
216
217     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
218     private long delayInSeconds;
219
220     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
221     private long times;
222
223     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
224     private String content;
225
226     public long getDelayInSeconds() {
227       return delayInSeconds;
228     }
229
230     public long getTimes() {
231       return times;
232     }
233
234     public String getContent() {
235       return content;
236     }
237
238     @Override
239     public String toString() {
240       final StringBuilder sb = new StringBuilder("Notification{");
241       sb.append("delayInSeconds=").append(delayInSeconds);
242       sb.append(", times=").append(times);
243       sb.append(", content='").append(content).append('\'');
244       sb.append('}');
245       return sb.toString();
246     }
247   }
248 }