Decouple config and netconf subsystems.
[controller.git] / opendaylight / netconf / tools / netconf-testtool / src / main / java / org / opendaylight / controller / netconf / test / tool / rpc / SimulatedCreateSubscription.java
1 /*
2  * Copyright (c) 2015 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.test.tool.rpc;
10
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.Maps;
14 import java.io.File;
15 import java.io.IOException;
16 import java.text.SimpleDateFormat;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.concurrent.Executors;
22 import java.util.concurrent.ScheduledExecutorService;
23 import java.util.concurrent.TimeUnit;
24 import javax.xml.bind.JAXBContext;
25 import javax.xml.bind.JAXBException;
26 import javax.xml.bind.Unmarshaller;
27 import javax.xml.bind.annotation.XmlRootElement;
28 import org.opendaylight.controller.config.util.xml.DocumentedException;
29 import org.opendaylight.controller.config.util.xml.XmlElement;
30 import org.opendaylight.controller.config.util.xml.XmlUtil;
31 import org.opendaylight.controller.netconf.api.NetconfMessage;
32 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
33 import org.opendaylight.controller.netconf.impl.NetconfServerSession;
34 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultNetconfOperation;
35 import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.Element;
38 import org.xml.sax.SAXException;
39
40 public class SimulatedCreateSubscription extends AbstractLastNetconfOperation implements DefaultNetconfOperation {
41
42     private final Map<Notification, NetconfMessage> notifications;
43     private NetconfServerSession session;
44     private ScheduledExecutorService scheduledExecutorService;
45
46     public SimulatedCreateSubscription(final String id, final Optional<File> notificationsFile) {
47         super(id);
48
49         Optional<Notifications> notifications;
50
51         if(notificationsFile.isPresent()) {
52             notifications = Optional.of(loadNotifications(notificationsFile.get()));
53             scheduledExecutorService = Executors.newScheduledThreadPool(1);
54         } else {
55             notifications = Optional.absent();
56         }
57
58         if(notifications.isPresent()) {
59             Map<Notification, NetconfMessage> preparedMessages = Maps.newHashMapWithExpectedSize(notifications.get().getNotificationList().size());
60             for (final Notification notification : notifications.get().getNotificationList()) {
61                 final NetconfMessage parsedNotification = parseNetconfNotification(notification.getContent());
62                 preparedMessages.put(notification, parsedNotification);
63             }
64             this.notifications = preparedMessages;
65         } else {
66             this.notifications = Collections.emptyMap();
67         }
68
69     }
70
71     private Notifications loadNotifications(final File file) {
72         try {
73             final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
74             final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
75             return (Notifications) jaxbUnmarshaller.unmarshal(file);
76         } catch (final JAXBException e) {
77             throw new IllegalArgumentException("Canot parse file " + file + " as a notifications file", e);
78         }
79     }
80
81     @Override
82     protected String getOperationName() {
83         return "create-subscription";
84     }
85
86     @Override
87     protected String getOperationNamespace() {
88         return "urn:ietf:params:xml:ns:netconf:notification:1.0";
89     }
90
91     @Override
92     protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws DocumentedException {
93         long delayAggregator = 0;
94
95         for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
96             for (int i = 0; i <= notification.getKey().getTimes(); i++) {
97
98                 delayAggregator += notification.getKey().getDelayInSeconds();
99
100                 scheduledExecutorService.schedule(new Runnable() {
101                     @Override
102                     public void run() {
103                         Preconditions.checkState(session != null, "Session is not set, cannot process notifications");
104                         session.sendMessage(notification.getValue());
105                     }
106                 }, delayAggregator, TimeUnit.SECONDS);
107             }
108         }
109         return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.<String>absent());
110     }
111
112     private static NetconfMessage parseNetconfNotification(String content) {
113         final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
114         final int endEventTime = content.indexOf("</eventTime>");
115         final String eventTime = content.substring(startEventTime, endEventTime);
116         if(eventTime.equals("XXXX")) {
117             content = content.replace(eventTime, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
118         }
119
120         try {
121             return new NetconfMessage(XmlUtil.readXmlToDocument(content));
122         } catch (SAXException | IOException e) {
123             throw new IllegalArgumentException("Cannot parse notifications", e);
124         }
125     }
126
127     @Override
128     public void setNetconfSession(final NetconfServerSession s) {
129         this.session = s;
130     }
131
132     @XmlRootElement(name = "notifications")
133     public static final class Notifications {
134
135         @javax.xml.bind.annotation.XmlElement(nillable =  false, name = "notification", required = true)
136         private List<Notification> notificationList;
137
138         public List<Notification> getNotificationList() {
139             return notificationList;
140         }
141
142         @Override
143         public String toString() {
144             final StringBuffer sb = new StringBuffer("Notifications{");
145             sb.append("notificationList=").append(notificationList);
146             sb.append('}');
147             return sb.toString();
148         }
149     }
150
151     public static final class Notification {
152
153         @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
154         private long delayInSeconds;
155
156         @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
157         private long times;
158
159         @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
160         private String content;
161
162         public long getDelayInSeconds() {
163             return delayInSeconds;
164         }
165
166         public long getTimes() {
167             return times;
168         }
169
170         public String getContent() {
171             return content;
172         }
173
174         @Override
175         public String toString() {
176             final StringBuffer sb = new StringBuffer("Notification{");
177             sb.append("delayInSeconds=").append(delayInSeconds);
178             sb.append(", times=").append(times);
179             sb.append(", content='").append(content).append('\'');
180             sb.append('}');
181             return sb.toString();
182         }
183     }
184 }