blob: c76e869c20ede63658f7cda2e42139782894cc62 [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
18import com.google.common.collect.ImmutableMap;
Andrea Campanella80520b82016-01-05 17:55:29 -080019import com.google.common.collect.Lists;
Thomas Vachuska635c2d72015-05-08 14:32:13 -070020import com.google.common.collect.Maps;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080021import org.apache.commons.configuration.ConfigurationException;
22import org.apache.commons.configuration.HierarchicalConfiguration;
23import org.apache.commons.configuration.XMLConfiguration;
24
25import java.io.IOException;
26import java.io.InputStream;
Andrea Campanella80520b82016-01-05 17:55:29 -080027import java.util.Arrays;
28import java.util.List;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080029import java.util.Map;
Andrea Campanella80520b82016-01-05 17:55:29 -080030import java.util.stream.Collectors;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080031
32/**
33 * Utility capable of reading driver configuration XML resources and producing
34 * a device driver provider as a result.
35 * <p>
36 * The drivers stream structure is as follows:
37 * </p>
38 * <pre>
39 * &lt;drivers&gt;
40 * &lt;driver name=“...” [manufacturer="..." hwVersion="..." swVersion="..."]&gt;
41 * &lt;behaviour api="..." impl="..."/&gt;
42 * ...
43 * [&lt;property name=“key”&gt;value&lt;/key&gt;]
44 * ...
45 * &lt;/driver&gt;
46 * ...
47 * &lt;/drivers&gt;
48 * </pre>
49 */
50public class XmlDriverLoader {
51
52 private static final String DRIVERS = "drivers";
53 private static final String DRIVER = "driver";
54
55 private static final String BEHAVIOUR = "behaviour";
56 private static final String PROPERTY = "property";
57
58 private static final String NAME = "[@name]";
Thomas Vachuska635c2d72015-05-08 14:32:13 -070059 private static final String EXTENDS = "[@extends]";
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080060 private static final String MFG = "[@manufacturer]";
61 private static final String HW = "[@hwVersion]";
62 private static final String SW = "[@swVersion]";
63 private static final String API = "[@api]";
64 private static final String IMPL = "[@impl]";
65
66 private final ClassLoader classLoader;
Thomas Vachuskae7ea6882016-07-22 10:22:46 -070067 private final BehaviourClassResolver resolver;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080068
Thomas Vachuska635c2d72015-05-08 14:32:13 -070069 private Map<String, Driver> drivers = Maps.newHashMap();
70
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080071 /**
72 * Creates a new driver loader capable of loading drivers from the supplied
73 * class loader.
74 *
75 * @param classLoader class loader to use
Thomas Vachuskae7ea6882016-07-22 10:22:46 -070076 * @deprecated since 1.7.0 (Hummingbird)
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080077 */
Thomas Vachuskae7ea6882016-07-22 10:22:46 -070078 @Deprecated
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080079 public XmlDriverLoader(ClassLoader classLoader) {
Thomas Vachuskae7ea6882016-07-22 10:22:46 -070080 this(classLoader, null);
81 }
82
83 /**
84 * Creates a new driver loader capable of loading drivers from the supplied
85 * class loader.
86 *
87 * @param classLoader class loader to use
88 * @param resolver behaviour class resolver
89 */
90 public XmlDriverLoader(ClassLoader classLoader, BehaviourClassResolver resolver) {
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080091 this.classLoader = classLoader;
Thomas Vachuskae7ea6882016-07-22 10:22:46 -070092 this.resolver = resolver;
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -080093 }
94
95 /**
96 * Loads the specified drivers resource as an XML stream and parses it to
97 * produce a ready-to-register driver provider.
98 *
99 * @param driversStream stream containing the drivers definitions
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700100 * @param resolver driver resolver
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800101 * @return driver provider
102 * @throws java.io.IOException if issues are encountered reading the stream
103 * or parsing the driver definitions within
104 */
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700105 public DefaultDriverProvider loadDrivers(InputStream driversStream,
106 DriverResolver resolver) throws IOException {
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800107 try {
108 XMLConfiguration cfg = new XMLConfiguration();
109 cfg.setRootElementName(DRIVERS);
110 cfg.setAttributeSplittingDisabled(true);
111
112 cfg.load(driversStream);
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700113 return loadDrivers(cfg, resolver);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800114 } catch (ConfigurationException e) {
115 throw new IOException("Unable to load drivers", e);
116 }
117 }
118
119 /**
120 * Loads a driver provider from the supplied hierarchical configuration.
121 *
122 * @param driversCfg hierarchical configuration containing the drivers definitions
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700123 * @param resolver driver resolver
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800124 * @return driver provider
125 */
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700126 public DefaultDriverProvider loadDrivers(HierarchicalConfiguration driversCfg,
127 DriverResolver resolver) {
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800128 DefaultDriverProvider provider = new DefaultDriverProvider();
129 for (HierarchicalConfiguration cfg : driversCfg.configurationsAt(DRIVER)) {
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700130 DefaultDriver driver = loadDriver(cfg, resolver);
131 drivers.put(driver.name(), driver);
132 provider.addDriver(driver);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800133 }
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700134 drivers.clear();
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800135 return provider;
136 }
137
138 /**
139 * Loads a driver from the supplied hierarchical configuration.
140 *
141 * @param driverCfg hierarchical configuration containing the driver definition
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700142 * @param resolver driver resolver
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800143 * @return driver
144 */
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700145 public DefaultDriver loadDriver(HierarchicalConfiguration driverCfg,
146 DriverResolver resolver) {
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800147 String name = driverCfg.getString(NAME);
Andrea Campanella80520b82016-01-05 17:55:29 -0800148 String parentsString = driverCfg.getString(EXTENDS, "");
149 List<Driver> parents = Lists.newArrayList();
Jon Halla3fcf672017-03-28 16:53:22 -0700150 if (!"".equals(parentsString)) {
Andrea Campanella80520b82016-01-05 17:55:29 -0800151 List<String> parentsNames;
152 if (parentsString.contains(",")) {
153 parentsNames = Arrays.asList(parentsString.replace(" ", "").split(","));
154 } else {
155 parentsNames = Lists.newArrayList(parentsString);
156 }
157 parents = parentsNames.stream().map(parent -> (parent != null) ?
158 resolve(parent, resolver) : null).collect(Collectors.toList());
159 }
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800160 String manufacturer = driverCfg.getString(MFG, "");
161 String hwVersion = driverCfg.getString(HW, "");
162 String swVersion = driverCfg.getString(SW, "");
Andrea Campanella80520b82016-01-05 17:55:29 -0800163 return new DefaultDriver(name, parents, manufacturer, hwVersion, swVersion,
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800164 parseBehaviours(driverCfg),
165 parseProperties(driverCfg));
166 }
167
Thomas Vachuska635c2d72015-05-08 14:32:13 -0700168 // Resolves the driver by name locally at first and then using the specified resolver.
169 private Driver resolve(String parentName, DriverResolver resolver) {
170 Driver driver = drivers.get(parentName);
171 return driver != null ? driver :
172 (resolver != null ? resolver.getDriver(parentName) : null);
173 }
174
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800175 // Parses the behaviours section.
176 private Map<Class<? extends Behaviour>, Class<? extends Behaviour>>
177 parseBehaviours(HierarchicalConfiguration driverCfg) {
178 ImmutableMap.Builder<Class<? extends Behaviour>,
179 Class<? extends Behaviour>> behaviours = ImmutableMap.builder();
180 for (HierarchicalConfiguration b : driverCfg.configurationsAt(BEHAVIOUR)) {
181 behaviours.put(getClass(b.getString(API)), getClass(b.getString(IMPL)));
182 }
183 return behaviours.build();
184 }
185
186 // Parses the properties section.
187 private Map<String, String> parseProperties(HierarchicalConfiguration driverCfg) {
188 ImmutableMap.Builder<String, String> properties = ImmutableMap.builder();
189 for (HierarchicalConfiguration b : driverCfg.configurationsAt(PROPERTY)) {
190 properties.put(b.getString(NAME), (String) b.getRootNode().getValue());
191 }
192 return properties.build();
193 }
194
195 @SuppressWarnings("unchecked")
196 private Class<? extends Behaviour> getClass(String className) {
197 try {
198 return (Class<? extends Behaviour>) classLoader.loadClass(className);
199 } catch (ClassNotFoundException e) {
Thomas Vachuskae7ea6882016-07-22 10:22:46 -0700200 if (resolver != null) {
201 Class<? extends Behaviour> cls = resolver.getBehaviourClass(className);
202 if (cls != null) {
203 return cls;
204 }
205 }
206 throw new IllegalArgumentException("Unable to resolve class " + className, e);
Thomas Vachuskaa8f4e7d2015-01-08 17:31:55 -0800207 }
208 }
209
210}