2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.tr064.internal.util;
15 import java.io.ByteArrayInputStream;
16 import java.io.InputStream;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
21 import java.util.Optional;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.TimeoutException;
25 import java.util.stream.Collectors;
26 import java.util.stream.Stream;
28 import javax.xml.bind.JAXBContext;
29 import javax.xml.bind.JAXBException;
30 import javax.xml.bind.Unmarshaller;
31 import javax.xml.transform.stream.StreamSource;
33 import org.eclipse.jdt.annotation.NonNullByDefault;
34 import org.eclipse.jdt.annotation.Nullable;
35 import org.eclipse.jetty.client.HttpClient;
36 import org.eclipse.jetty.client.api.ContentResponse;
37 import org.eclipse.jetty.http.HttpMethod;
38 import org.openhab.binding.tr064.internal.SCPDException;
39 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDDeviceType;
40 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDRootType;
41 import org.openhab.binding.tr064.internal.dto.scpd.root.SCPDServiceType;
42 import org.openhab.binding.tr064.internal.dto.scpd.service.SCPDScpdType;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link SCPDUtil} is responsible for handling commands, which are
48 * sent to one of the channels.
50 * @author Jan N. Klug - Initial contribution
53 public class SCPDUtil {
54 private final Logger logger = LoggerFactory.getLogger(SCPDUtil.class);
56 private final HttpClient httpClient;
58 private SCPDRootType scpdRoot;
59 private final List<SCPDDeviceType> scpdDevicesList = new ArrayList<>();
60 private final Map<String, SCPDScpdType> serviceMap = new HashMap<>();
62 public SCPDUtil(HttpClient httpClient, String endpoint) throws SCPDException {
63 this.httpClient = httpClient;
65 SCPDRootType scpdRoot = getAndUnmarshalSCPD(endpoint + "/tr64desc.xml", SCPDRootType.class);
66 if (scpdRoot == null) {
67 throw new SCPDException("could not get SCPD root");
69 this.scpdRoot = scpdRoot;
71 scpdDevicesList.addAll(flatDeviceList(scpdRoot.getDevice()).collect(Collectors.toList()));
72 for (SCPDDeviceType device : scpdDevicesList) {
73 for (SCPDServiceType service : device.getServiceList()) {
74 SCPDScpdType scpd = serviceMap.computeIfAbsent(service.getServiceId(),
75 serviceId -> getAndUnmarshalSCPD(endpoint + service.getSCPDURL(), SCPDScpdType.class));
77 throw new SCPDException("could not get SCPD service");
84 * generic unmarshaller
86 * @param uri the uri of the XML file
87 * @param clazz the class describing the XML file
88 * @return unmarshalling result
90 private <T> @Nullable T getAndUnmarshalSCPD(String uri, Class<T> clazz) {
92 ContentResponse contentResponse = httpClient.newRequest(uri).timeout(2, TimeUnit.SECONDS)
93 .method(HttpMethod.GET).send();
94 InputStream xml = new ByteArrayInputStream(contentResponse.getContent());
96 JAXBContext context = JAXBContext.newInstance(clazz);
97 Unmarshaller um = context.createUnmarshaller();
98 return um.unmarshal(new StreamSource(xml), clazz).getValue();
99 } catch (ExecutionException | InterruptedException | TimeoutException e) {
100 logger.debug("HTTP Failed to GET uri '{}': {}", uri, e.getMessage());
101 } catch (JAXBException e) {
102 logger.debug("Unmarshalling failed: {}", e.getMessage());
108 * recursively flatten the device tree to a stream
110 * @param device a device
111 * @return stream of sub-devices
113 private Stream<SCPDDeviceType> flatDeviceList(SCPDDeviceType device) {
114 return Stream.concat(Stream.of(device), device.getDeviceList().stream().flatMap(this::flatDeviceList));
118 * get a list of all sub-devices (root device not included)
120 * @return the device list
122 public List<SCPDDeviceType> getAllSubDevices() {
123 return scpdDevicesList.stream().filter(device -> !device.getUDN().equals(scpdRoot.getDevice().getUDN()))
124 .collect(Collectors.toList());
128 * get a single device by it's UDN
130 * @param udn the device UDN
133 public Optional<SCPDDeviceType> getDevice(String udn) {
135 return Optional.of(scpdRoot.getDevice());
137 return getAllSubDevices().stream().filter(device -> udn.equals(device.getUDN())).findFirst();
142 * get a single service by it's serviceId
144 * @param serviceId the service id
145 * @return the service
147 public Optional<SCPDScpdType> getService(String serviceId) {
148 return Optional.ofNullable(serviceMap.get(serviceId));