blob: 8197c90657037039de57e4b3f97afb1074ec60c3 [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();
Yi Tseng809b5d72017-08-16 14:00:37 -070092 properties.putAll(other.properties());
93
94 // remove duplicated properties from this driver and merge
95 this.properties().entrySet().stream()
96 .filter(e -> !other.properties().containsKey(e.getKey()))
97 .forEach(properties::put);
98
Andrea Campanella28dbe8f2016-01-28 17:51:58 -080099 List<Driver> completeParents = new ArrayList<>();
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800100
Andrea Campanella28dbe8f2016-01-28 17:51:58 -0800101 if (parents != null) {
102 parents.forEach(parent -> other.parents().forEach(otherParent -> {
103 if (otherParent.name().equals(parent.name())) {
104 completeParents.add(parent.merge(otherParent));
105 } else if (!completeParents.contains(otherParent)) {
106 completeParents.add(otherParent);
107 } else if (!completeParents.contains(parent)) {
108 completeParents.add(parent);
109 }
110 }));
111 }
Jon Hallcbd1b392017-01-18 20:15:44 -0800112 return new DefaultDriver(name, !completeParents.isEmpty() ? completeParents : other.parents(),
Andrea Campanella80520b82016-01-05 17:55:29 -0800113 manufacturer, hwVersion, swVersion,
alshabib975617b2015-04-09 13:26:53 -0700114 ImmutableMap.copyOf(behaviours), properties.build());
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800115 }
116
117 @Override
118 public String name() {
119 return name;
120 }
121
122 @Override
123 public String manufacturer() {
124 return manufacturer;
125 }
126
127 @Override
128 public String hwVersion() {
129 return hwVersion;
130 }
131
132 @Override
133 public String swVersion() {
134 return swVersion;
135 }
136
137 @Override
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700138 public Driver parent() {
sangyun-han04c461d2017-03-02 20:29:39 +0900139 return parents.isEmpty() ? null : parents.get(0);
Andrea Campanella80520b82016-01-05 17:55:29 -0800140 }
141
142 @Override
143 public List<Driver> parents() {
144 return parents;
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700145 }
146
147 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800148 public Set<Class<? extends Behaviour>> behaviours() {
149 return behaviours.keySet();
150 }
151
152 @Override
Thomas Vachuska5c2f8132015-04-08 23:09:08 -0700153 public Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour) {
154 return behaviours.get(behaviour);
155 }
156
157 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800158 public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700159 return behaviours.containsKey(behaviourClass) ||
Andrea Campanella80520b82016-01-05 17:55:29 -0800160 (parents != null && parents.stream()
161 .filter(parent -> parent.hasBehaviour(behaviourClass)).count() > 0);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800162 }
163
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700164 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800165 public <T extends Behaviour> T createBehaviour(DriverData data,
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700166 Class<T> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700167 T behaviour = createBehaviour(data, null, behaviourClass);
168 if (behaviour != null) {
169 return behaviour;
Andrea Campanella80520b82016-01-05 17:55:29 -0800170 } else if (parents != null) {
171 for (Driver parent : Lists.reverse(parents)) {
172 try {
173 return parent.createBehaviour(data, behaviourClass);
174 } catch (IllegalArgumentException e) {
175 log.debug("Parent {} does not support behaviour {}", parent, behaviourClass);
176 }
177 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700178 }
179 throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700180 }
181
182 @Override
183 public <T extends Behaviour> T createBehaviour(DriverHandler handler,
184 Class<T> behaviourClass) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700185 T behaviour = createBehaviour(handler.data(), handler, behaviourClass);
186 if (behaviour != null) {
187 return behaviour;
Andrea Campanella80520b82016-01-05 17:55:29 -0800188 } else if (parents != null && !parents.isEmpty()) {
189 for (Driver parent : Lists.reverse(parents)) {
190 try {
191 return parent.createBehaviour(handler, behaviourClass);
192 } catch (IllegalArgumentException e) {
193 log.debug("Parent {} does not support behaviour {}", parent, behaviourClass);
194 }
195 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700196 }
197 throw new IllegalArgumentException(behaviourClass.getName() + " not supported");
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700198 }
199
200 // Creates an instance of behaviour primed with the specified driver data.
201 private <T extends Behaviour> T createBehaviour(DriverData data, DriverHandler handler,
202 Class<T> behaviourClass) {
CNluciusa66c3972015-09-06 20:31:29 +0800203 //checkArgument(handler != null || !HandlerBehaviour.class.isAssignableFrom(behaviourClass),
204 // "{} is applicable only to handler context", behaviourClass.getName());
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800205
206 // Locate the implementation of the requested behaviour.
207 Class<? extends Behaviour> implementation = behaviours.get(behaviourClass);
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700208 if (implementation != null) {
209 // Create an instance of the behaviour and apply data as its context.
210 T behaviour = createBehaviour(behaviourClass, implementation);
211 behaviour.setData(data);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800212
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700213 // If this is a handler behaviour, also apply handler as its context.
214 if (handler != null) {
215 ((HandlerBehaviour) behaviour).setHandler(handler);
216 }
217 return behaviour;
Thomas Vachuskafacc3f52015-04-10 08:58:36 -0700218 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700219 return null;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800220 }
221
222 @SuppressWarnings("unchecked")
223 private <T extends Behaviour> T createBehaviour(Class<T> behaviourClass,
224 Class<? extends Behaviour> implementation) {
225 try {
226 return (T) implementation.newInstance();
227 } catch (InstantiationException | IllegalAccessException e) {
228 // TODO: add a specific unchecked exception
229 throw new IllegalArgumentException("Unable to create behaviour", e);
230 }
231 }
232
233 @Override
234 public Set<String> keys() {
235 return properties.keySet();
236 }
237
238 @Override
239 public String value(String key) {
240 return properties.get(key);
241 }
242
243 @Override
244 public Map<String, String> properties() {
245 return properties;
246 }
247
248 @Override
Charles Chana59f9b762017-07-30 18:09:44 -0700249 public String getProperty(String name) {
250 Queue<Driver> queue = new LinkedList<>();
251 queue.add(this);
252 while (!queue.isEmpty()) {
253 Driver driver = queue.remove();
254 String property = driver.properties().get(name);
255 if (property != null) {
256 return property;
257 } else if (driver.parents() != null) {
258 queue.addAll(driver.parents());
259 }
260 }
261 return null;
262 }
263
264 @Override
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800265 public String toString() {
266 return toStringHelper(this)
267 .add("name", name)
Andrea Campanella80520b82016-01-05 17:55:29 -0800268 .add("parents", parents)
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800269 .add("manufacturer", manufacturer)
270 .add("hwVersion", hwVersion)
271 .add("swVersion", swVersion)
272 .add("behaviours", behaviours)
273 .add("properties", properties)
274 .toString();
275 }
276
Andrea Campanella238d96e2016-01-20 11:52:02 -0800277 @Override
278 public boolean equals(Object driverToBeCompared) {
279 if (this == driverToBeCompared) {
280 return true;
281 }
282 if (driverToBeCompared == null || getClass() != driverToBeCompared.getClass()) {
283 return false;
284 }
285
286 DefaultDriver driver = (DefaultDriver) driverToBeCompared;
287
288 return name.equals(driver.name());
289
290 }
291
292 @Override
293 public int hashCode() {
294 return Objects.hashCode(name);
295 }
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800296}