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