Initial source code
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / rpc / CreateSubscription.java
diff --git a/netconf-server/src/main/java/org/commscope/tr069adapter/netconf/rpc/CreateSubscription.java b/netconf-server/src/main/java/org/commscope/tr069adapter/netconf/rpc/CreateSubscription.java
new file mode 100644 (file)
index 0000000..151fa5d
--- /dev/null
@@ -0,0 +1,227 @@
+/*\r
+ * ============LICENSE_START========================================================================\r
+ * ONAP : tr-069-adapter\r
+ * =================================================================================================\r
+ * Copyright (C) 2020 CommScope Inc Intellectual Property.\r
+ * =================================================================================================\r
+ * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,\r
+ * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You\r
+ * may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,\r
+ * either express or implied. See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ * ===============LICENSE_END=======================================================================\r
+ */\r
+\r
+package org.commscope.tr069adapter.netconf.rpc;\r
+\r
+import com.google.common.base.Preconditions;\r
+import com.google.common.collect.Maps;\r
+\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.text.SimpleDateFormat;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Optional;\r
+import java.util.concurrent.Executors;\r
+import java.util.concurrent.ScheduledExecutorService;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import javax.xml.bind.JAXBContext;\r
+import javax.xml.bind.JAXBException;\r
+import javax.xml.bind.Unmarshaller;\r
+import javax.xml.bind.annotation.XmlRootElement;\r
+\r
+import org.opendaylight.netconf.api.NetconfMessage;\r
+import org.opendaylight.netconf.api.xml.XmlElement;\r
+import org.opendaylight.netconf.api.xml.XmlNetconfConstants;\r
+import org.opendaylight.netconf.api.xml.XmlUtil;\r
+import org.opendaylight.netconf.impl.NetconfServerSession;\r
+import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;\r
+import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.xml.sax.SAXException;\r
+\r
+public class CreateSubscription extends AbstractLastNetconfOperation\r
+    implements DefaultNetconfOperation {\r
+\r
+  private static final Logger logger = LoggerFactory.getLogger(CreateSubscription.class);\r
+  private final Map<Notification, NetconfMessage> notifications;\r
+  private NetconfServerSession session;\r
+  private ScheduledExecutorService scheduledExecutorService;\r
+  private static Map<String, NetconfServerSession> netconfServerSessionMap = new HashMap<>();\r
+  private String deviceID;\r
+\r
+  public CreateSubscription(final String id, final Optional<File> notificationsFile,\r
+      String deviceID) {\r
+\r
+    super(id);\r
+\r
+    logger.debug("CreateSubscription RPC is created with macID {}", deviceID);\r
+    this.deviceID = deviceID;\r
+\r
+    final Optional<Notifications> notifs;\r
+\r
+    if (notificationsFile.isPresent()) {\r
+      notifs = Optional.of(loadNotifications(notificationsFile.get()));\r
+      scheduledExecutorService = Executors.newScheduledThreadPool(1);\r
+    } else {\r
+      notifs = Optional.empty();\r
+    }\r
+\r
+    if (notifs.isPresent()) {\r
+      final Collection<Notification> toCopy = notifs.get().getNotificationList();\r
+      final Map<Notification, NetconfMessage> preparedMessages =\r
+          Maps.newHashMapWithExpectedSize(toCopy.size());\r
+      for (final Notification notification : toCopy) {\r
+        final NetconfMessage parsedNotification =\r
+            parseNetconfNotification(notification.getContent());\r
+        preparedMessages.put(notification, parsedNotification);\r
+      }\r
+      this.notifications = preparedMessages;\r
+    } else {\r
+      this.notifications = Collections.emptyMap();\r
+    }\r
+  }\r
+\r
+  private static Notifications loadNotifications(final File file) {\r
+    try {\r
+      final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);\r
+      final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();\r
+      return (Notifications) jaxbUnmarshaller.unmarshal(file);\r
+    } catch (final JAXBException e) {\r
+      throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",\r
+          e);\r
+    }\r
+  }\r
+\r
+  @Override\r
+  protected String getOperationName() {\r
+    return "create-subscription";\r
+  }\r
+\r
+  @Override\r
+  protected String getOperationNamespace() {\r
+    return "urn:ietf:params:xml:ns:netconf:notification:1.0";\r
+  }\r
+\r
+  @Override\r
+  protected Element handleWithNoSubsequentOperations(final Document document,\r
+      final XmlElement operationElement) {\r
+    long delayAggregator = 0;\r
+    for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {\r
+      for (int i = 0; i <= notification.getKey().getTimes(); i++) {\r
+\r
+        delayAggregator += notification.getKey().getDelayInSeconds();\r
+\r
+        scheduledExecutorService.schedule(() -> {\r
+          Preconditions.checkState(session != null,\r
+              "Session is not set, cannot process notifications");\r
+          session.sendMessage(notification.getValue());\r
+        }, delayAggregator, TimeUnit.SECONDS);\r
+      }\r
+    }\r
+    return document.createElement(XmlNetconfConstants.OK);\r
+  }\r
+\r
+  private static NetconfMessage parseNetconfNotification(String content) {\r
+    final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();\r
+    final int endEventTime = content.indexOf("</eventTime>");\r
+    final String eventTime = content.substring(startEventTime, endEventTime);\r
+    if (eventTime.equals("XXXX")) {\r
+      content = content.replace(eventTime,\r
+          new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));\r
+    }\r
+\r
+    try {\r
+      return new NetconfMessage(XmlUtil.readXmlToDocument(content));\r
+    } catch (SAXException | IOException e) {\r
+      throw new IllegalArgumentException("Cannot parse notifications", e);\r
+    }\r
+  }\r
+\r
+  @Override\r
+  public void setNetconfSession(final NetconfServerSession newSession) {\r
+    logger.debug("Adding netconf session to notification server sessions map : {}", newSession);\r
+    logger.debug("This CreateSubscription is setup to support  macID= {}", deviceID);\r
+    this.session = newSession;\r
+    netconfServerSessionMap.put(deviceID, newSession);\r
+  }\r
+\r
+  public static void sendNotification(NetconfMessage netconfMessage, String deviceID) {\r
+    logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);\r
+    NetconfServerSession session = netconfServerSessionMap.get(deviceID);\r
+    if (session != null && session.isUp()) {\r
+      session.sendMessage(netconfMessage);\r
+      logger.debug("Successfully send notification for deviceID: {}", deviceID);\r
+    } else {\r
+      logger.debug("Failed to send notification. None of valid netconf session is available.");\r
+    }\r
+    logger.debug("Available netconf sessions : {}", netconfServerSessionMap);\r
+\r
+  }\r
+\r
+  @XmlRootElement(name = "notifications")\r
+  public static final class Notifications {\r
+\r
+    @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)\r
+    private List<Notification> notificationList;\r
+\r
+    public List<Notification> getNotificationList() {\r
+      return notificationList;\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+      final StringBuilder sb = new StringBuilder("Notifications{");\r
+      sb.append("notificationList=").append(notificationList);\r
+      sb.append('}');\r
+      return sb.toString();\r
+    }\r
+  }\r
+\r
+  public static final class Notification {\r
+\r
+    @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")\r
+    private long delayInSeconds;\r
+\r
+    @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")\r
+    private long times;\r
+\r
+    @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)\r
+    private String content;\r
+\r
+    public long getDelayInSeconds() {\r
+      return delayInSeconds;\r
+    }\r
+\r
+    public long getTimes() {\r
+      return times;\r
+    }\r
+\r
+    public String getContent() {\r
+      return content;\r
+    }\r
+\r
+    @Override\r
+    public String toString() {\r
+      final StringBuilder sb = new StringBuilder("Notification{");\r
+      sb.append("delayInSeconds=").append(delayInSeconds);\r
+      sb.append(", times=").append(times);\r
+      sb.append(", content='").append(content).append('\'');\r
+      sb.append('}');\r
+      return sb.toString();\r
+    }\r
+  }\r
+}\r