blob: 919010aeb6375db3c4df34f4e317f3350bebdc49 [file] [log] [blame]
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.net.driver;
17
sangyun-han04c461d2017-03-02 20:29:39 +090018import com.google.common.collect.ImmutableList;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080019import com.google.common.collect.ImmutableMap;
Andrea Campanella80520b82016-01-05 17:55:29 -080020import com.google.common.collect.Lists;
alshabib975617b2015-04-09 13:26:53 -070021import com.google.common.collect.Maps;
Andrea Campanella80520b82016-01-05 17:55:29 -080022import org.slf4j.Logger;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080023
Andrea Campanella28dbe8f2016-01-28 17:51:58 -080024import java.util.ArrayList;
Charles Chana59f9b762017-07-30 18:09:44 -070025import java.util.LinkedList;
Andrea Campanella80520b82016-01-05 17:55:29 -080026import java.util.List;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080027import java.util.Map;
Thomas Vachuska635c2d72015-05-08 14:32:13 -070028import java.util.Objects;
Charles Chana59f9b762017-07-30 18:09:44 -070029import java.util.Queue;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080030import java.util.Set;
31
32import static com.google.common.base.MoreObjects.toStringHelper;
33import static com.google.common.base.Preconditions.checkArgument;
34import static com.google.common.base.Preconditions.checkNotNull;
35import static com.google.common.collect.ImmutableMap.copyOf;
Andrea Campanella80520b82016-01-05 17:55:29 -080036import static org.slf4j.LoggerFactory.getLogger;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080037
38/**
39 * Default implementation of extensible driver.
40 */
41public class DefaultDriver implements Driver {
42
Andrea Campanella80520b82016-01-05 17:55:29 -080043 private final Logger log = getLogger(getClass());
44
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080045 private final String name;
Andrea Campanella80520b82016-01-05 17:55:29 -080046 private final List<Driver> parents;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080047
48 private final String manufacturer;
49 private final String hwVersion;
50 private final String swVersion;
51
52 private final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
53 private final Map<String, String> properties;
54
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080055 /**
56 * Creates a driver with the specified name.
57 *
58 * @param name driver name
Andrea Campanella80520b82016-01-05 17:55:29 -080059 * @param parents optional parent drivers
60 * @param manufacturer device manufacturer
61 * @param hwVersion device hardware version
62 * @param swVersion device software version
63 * @param behaviours device behaviour classes
64 * @param properties properties for configuration of device behaviour classes
65 */
66 public DefaultDriver(String name, List<Driver> parents, String manufacturer,
67 String hwVersion, String swVersion,
68 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours,
69 Map<String, String> properties) {
70 this.name = checkNotNull(name, "Name cannot be null");
sangyun-han04c461d2017-03-02 20:29:39 +090071 this.parents = parents == null ? ImmutableList.of() : ImmutableList.copyOf(parents);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080072 this.manufacturer = checkNotNull(manufacturer, "Manufacturer cannot be null");
73 this.hwVersion = checkNotNull(hwVersion, "HW version cannot be null");
74 this.swVersion = checkNotNull(swVersion, "SW version cannot be null");
75 this.behaviours = copyOf(checkNotNull(behaviours, "Behaviours cannot be null"));
76 this.properties = copyOf(checkNotNull(properties, "Properties cannot be null"));
77 }
78
Thomas Vachuska5c2f8132015-04-08 23:09:08 -070079 @Override
80 public Driver merge(Driver other) {
Andrea Campanella80520b82016-01-05 17:55:29 -080081 checkArgument(parents == null || Objects.equals(parent(), other.parent()),
Thomas Vachuska635c2d72015-05-08 14:32:13 -070082 "Parent drivers are not the same");
sangyun-han04c461d2017-03-02 20:29:39 +090083
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080084 // Merge the behaviours.
alshabib975617b2015-04-09 13:26:53 -070085 Map<Class<? extends Behaviour>, Class<? extends Behaviour>>
86 behaviours = Maps.newHashMap();
Thomas Vachuska5c2f8132015-04-08 23:09:08 -070087 behaviours.putAll(this.behaviours);
88 other.behaviours().forEach(b -> behaviours.put(b, other.implementation(b)));
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080089
90 // Merge the properties.
91 ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
Thomas Vachuska5c2f8132015-04-08 23:09:08 -070092 properties.putAll(this.properties).putAll(other.properties());
Andrea Campanella28dbe8f2016-01-28 17:51:58 -080093 List<Driver> completeParents = new ArrayList<>();
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080094
Andrea Campanella28dbe8f2016-01-28 17:51:58 -080095 if (parents != null) {
96 parents.forEach(parent -> other.parents().forEach(otherParent -> {
97 if (otherParent.name().equals(parent.name())) {
98 completeParents.add(parent.merge(otherParent));
99 } else if (!completeParents.contains(otherParent)) {
100 completeParents.add(otherParent);
101 } else if (!completeParents.contains(parent)) {
102 completeParents.add(parent);
103 }
104 }));
105 }
Jon Hallcbd1b392017-01-18 20:15:44 -0800106 return new DefaultDriver(name, !completeParents.isEmpty() ? completeParents : other.parents(),
Andrea Campanella80520b82016-01-05 17:55:29 -0800107 manufacturer, hwVersion, swVersion,
alshabib975617b2015-04-09 13:26:53 -0700108 ImmutableMap.copyOf(behaviours), properties.build());
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800109 }
110
111 @Override
112 public String name() {
113 return name;
114 }
115
116 @Override
117 public String manufacturer() {
118 return manufacturer;
119 }
120
121 @Override
122 public String hwVersion() {
123 return hwVersion;
124 }
125
126 @Override
127 public String swVersion() {
128 return swVersion;
129 }
130
131 @Override
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700132 public Driver parent() {
sangyun-han04c461d2017-03-02 20:29:39 +0900133 return parents.isEmpty() ? null : parents.get(0);
Andrea Campanella80520b82016-01-05 17:55:29 -0800134 }
135
136 @Override
137 public List<Driver> parents() {
138 return parents;
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700139 }
140
141 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800142 public Set<Class<? extends Behaviour>> behaviours() {
143 return behaviours.keySet();
144 }
145
146 @Override
Thomas Vachuska5c2f8132015-04-08 23:09:08 -0700147 public Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour) {
148 return behaviours.get(behaviour);
149 }
150
151 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800152 public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700153 return behaviours.containsKey(behaviourClass) ||
Andrea Campanella80520b82016-01-05 17:55:29 -0800154 (parents != null && parents.stream()
155 .filter(parent -> parent.hasBehaviour(behaviourClass)).count() > 0);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800156 }
157
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700158 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800159 public <T extends Behaviour> T createBehaviour(DriverData data,
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700160 Class<T> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700161 T behaviour = createBehaviour(data, null, behaviourClass);
162 if (behaviour != null) {
163 return behaviour;
Andrea Campanella80520b82016-01-05 17:55:29 -0800164 } else if (parents != null) {
165 for (Driver parent : Lists.reverse(parents)) {
166 try {
167 return parent.createBehaviour(data, behaviourClass);
168 } catch (IllegalArgumentException e) {
169 log.debug("Parent {} does not support behaviour {}", parent, behaviourClass);
170 }
171 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700172 }
173 throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700174 }
175
176 @Override
177 public <T extends Behaviour> T createBehaviour(DriverHandler handler,
178 Class<T> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700179 T behaviour = createBehaviour(handler.data(), handler, behaviourClass);
180 if (behaviour != null) {
181 return behaviour;
Andrea Campanella80520b82016-01-05 17:55:29 -0800182 } else if (parents != null && !parents.isEmpty()) {
183 for (Driver parent : Lists.reverse(parents)) {
184 try {
185 return parent.createBehaviour(handler, behaviourClass);
186 } catch (IllegalArgumentException e) {
187 log.debug("Parent {} does not support behaviour {}", parent, behaviourClass);
188 }
189 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700190 }
191 throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700192 }
193
194 // Creates an instance of behaviour primed with the specified driver data.
195 private <T extends Behaviour> T createBehaviour(DriverData data, DriverHandler handler,
196 Class<T> behaviourClass) {
CNluciusa66c3972015-09-06 20:31:29 +0800197 //checkArgument(handler != null || !HandlerBehaviour.class.isAssignableFrom(behaviourClass),
198 // "{} is applicable only to handler context", behaviourClass.getName());
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800199
200 // Locate the implementation of the requested behaviour.
201 Class<? extends Behaviour> implementation = behaviours.get(behaviourClass);
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700202 if (implementation != null) {
203 // Create an instance of the behaviour and apply data as its context.
204 T behaviour = createBehaviour(behaviourClass, implementation);
205 behaviour.setData(data);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800206
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700207 // If this is a handler behaviour, also apply handler as its context.
208 if (handler != null) {
209 ((HandlerBehaviour) behaviour).setHandler(handler);
210 }
211 return behaviour;
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700212 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700213 return null;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800214 }
215
216 @SuppressWarnings("unchecked")
217 private <T extends Behaviour> T createBehaviour(Class<T> behaviourClass,
218 Class<? extends Behaviour> implementation) {
219 try {
220 return (T) implementation.newInstance();
221 } catch (InstantiationException | IllegalAccessException e) {
222 // TODO: add a specific unchecked exception
223 throw new IllegalArgumentException("Unable to create behaviour", e);
224 }
225 }
226
227 @Override
228 public Set<String> keys() {
229 return properties.keySet();
230 }
231
232 @Override
233 public String value(String key) {
234 return properties.get(key);
235 }
236
237 @Override
238 public Map<String, String> properties() {
239 return properties;
240 }
241
242 @Override
Charles Chana59f9b762017-07-30 18:09:44 -0700243 public String getProperty(String name) {
244 Queue<Driver> queue = new LinkedList<>();
245 queue.add(this);
246 while (!queue.isEmpty()) {
247 Driver driver = queue.remove();
248 String property = driver.properties().get(name);
249 if (property != null) {
250 return property;
251 } else if (driver.parents() != null) {
252 queue.addAll(driver.parents());
253 }
254 }
255 return null;
256 }
257
258 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800259 public String toString() {
260 return toStringHelper(this)
261 .add("name", name)
Andrea Campanella80520b82016-01-05 17:55:29 -0800262 .add("parents", parents)
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800263 .add("manufacturer", manufacturer)
264 .add("hwVersion", hwVersion)
265 .add("swVersion", swVersion)
266 .add("behaviours", behaviours)
267 .add("properties", properties)
268 .toString();
269 }
270
Andrea Campanella238d96e2016-01-20 11:52:02 -0800271 @Override
272 public boolean equals(Object driverToBeCompared) {
273 if (this == driverToBeCompared) {
274 return true;
275 }
276 if (driverToBeCompared == null || getClass() != driverToBeCompared.getClass()) {
277 return false;
278 }
279
280 DefaultDriver driver = (DefaultDriver) driverToBeCompared;
281
282 return name.equals(driver.name());
283
284 }
285
286 @Override
287 public int hashCode() {
288 return Objects.hashCode(name);
289 }
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800290}