org.openhab.binding.ihc.ws.IhcResourceInteractionService.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.ihc.ws.IhcResourceInteractionService.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.ihc.ws;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.ihc.ws.datatypes.WSBaseDataType;
import org.openhab.binding.ihc.ws.datatypes.WSBooleanValue;
import org.openhab.binding.ihc.ws.datatypes.WSDateValue;
import org.openhab.binding.ihc.ws.datatypes.WSEnumValue;
import org.openhab.binding.ihc.ws.datatypes.WSFloatingPointValue;
import org.openhab.binding.ihc.ws.datatypes.WSIntegerValue;
import org.openhab.binding.ihc.ws.datatypes.WSResourceValue;
import org.openhab.binding.ihc.ws.datatypes.WSTimeValue;
import org.openhab.binding.ihc.ws.datatypes.WSTimerValue;
import org.openhab.binding.ihc.ws.datatypes.WSWeekdayValue;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * Class to handle IHC / ELKO LS Controller's resource interaction service.
 * 
 * Service is used to fetch or update resource values from/to controller.
 * 
 * @author Pauli Anttila
 * @since 1.5.0
 */
public class IhcResourceInteractionService extends IhcHttpsClient {

    private String url;
    private int timeout;

    IhcResourceInteractionService(String host, int timeout) {
        url = "https://" + host + "/ws/ResourceInteractionService";
        this.timeout = timeout;
        super.setConnectTimeout(timeout);
    }

    /**
     * Query resource value from controller.
     * 
     * 
     * @param resoureId
     *            Resource Identifier.
     * @return Resource value.
     */
    public WSResourceValue resourceQuery(int resoureId) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soapenv:Body>" + " <ns1:getRuntimeValue1 xmlns:ns1=\"utcs\">%s</ns1:getRuntimeValue1>"
                + "</soapenv:Body>" + "</soapenv:Envelope>";

        String query = String.format(soapQuery, String.valueOf(resoureId));

        openConnection(url);
        String response = sendQuery(query, timeout);
        closeConnection();

        NodeList nodeList;
        try {
            nodeList = parseList(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:getRuntimeValue2");

            if (nodeList.getLength() == 1) {

                WSResourceValue val = parseResourceValue(nodeList.item(0), 2);

                if (val.getResourceID() == resoureId) {
                    return val;
                } else {
                    throw new IhcExecption("No resource id found");
                }

            } else {
                throw new IhcExecption("No resource value found");
            }

        } catch (XPathExpressionException e) {
            throw new IhcExecption(e);
        } catch (UnsupportedEncodingException e) {
            throw new IhcExecption(e);
        }
    }

    private NodeList parseList(String xml, String xpathExpression)
            throws XPathExpressionException, UnsupportedEncodingException {
        InputStream is = new ByteArrayInputStream(xml.getBytes("UTF8"));
        XPath xpath = XPathFactory.newInstance().newXPath();
        InputSource inputSource = new InputSource(is);

        xpath.setNamespaceContext(new NamespaceContext() {
            public String getNamespaceURI(String prefix) {
                if (prefix == null)
                    throw new NullPointerException("Null prefix");
                else if ("SOAP-ENV".equals(prefix))
                    return "http://schemas.xmlsoap.org/soap/envelope/";
                else if ("ns1".equals(prefix))
                    return "utcs";
                else if ("ns2".equals(prefix))
                    return "utcs.values";
                return null;
            }

            public String getPrefix(String uri) {
                return null;
            }

            @SuppressWarnings("rawtypes")
            public Iterator getPrefixes(String uri) {
                throw new UnsupportedOperationException();
            }
        });

        return (NodeList) xpath.evaluate(xpathExpression, inputSource, XPathConstants.NODESET);
    }

    private WSResourceValue parseResourceValue(Node n, int index) throws XPathExpressionException {

        // parse resource id
        String resourceId = getValue(n, "ns1:resourceID");

        if (StringUtils.isNotBlank(resourceId)) {

            int id = Integer.parseInt(resourceId);

            // Parse floating point value

            String value = getValue(n, "ns1:value/ns" + index + ":floatingPointValue");

            if (StringUtils.isNotBlank(value)) {

                WSFloatingPointValue val = new WSFloatingPointValue();
                val.setResourceID(id);
                val.setFloatingPointValue(Double.valueOf(value));

                value = getValue(n, "ns1:value/ns" + index + ":maximumValue");
                if (StringUtils.isNotBlank(value)) {
                    val.setMaximumValue(Double.valueOf(value));
                }

                value = getValue(n, "ns1:value/ns" + index + ":minimumValue");
                if (StringUtils.isNotBlank(value)) {
                    val.setMinimumValue(Double.valueOf(value));
                }

                return val;
            }

            // Parse boolean value

            value = getValue(n, "ns1:value/ns" + index + ":value");
            if (StringUtils.isNotBlank(value)) {
                WSBooleanValue val = new WSBooleanValue();
                val.setResourceID(id);
                val.setValue(Boolean.valueOf(value));
                return val;
            }

            // Parse integer value

            value = getValue(n, "ns1:value/ns" + index + ":integer");
            if (StringUtils.isNotBlank(value)) {

                WSIntegerValue val = new WSIntegerValue();
                val.setResourceID(id);
                val.setInteger(Integer.valueOf(value));

                value = getValue(n, "ns1:value/ns" + index + ":maximumValue");
                if (StringUtils.isNotBlank(value)) {
                    val.setMaximumValue(Integer.valueOf(value));
                }

                value = getValue(n, "ns1:value/ns" + index + ":minimumValue");
                if (StringUtils.isNotBlank(value)) {
                    val.setMinimumValue(Integer.valueOf(value));
                }

                return val;
            }

            // Parse timer value

            value = getValue(n, "ns1:value/ns" + index + ":milliseconds");
            if (StringUtils.isNotBlank(value)) {
                WSTimerValue val = new WSTimerValue();
                val.setResourceID(id);
                val.setMilliseconds(Integer.valueOf(value));

                return val;
            }

            // Parse time value

            value = getValue(n, "ns1:value/ns" + index + ":hours");
            if (StringUtils.isNotBlank(value)) {

                WSTimeValue val = new WSTimeValue();
                val.setResourceID(id);
                val.setHours(Integer.valueOf(value));

                value = getValue(n, "ns1:value/ns" + index + ":minutes");
                if (StringUtils.isNotBlank(value)) {
                    val.setMinutes(Integer.valueOf(value));
                }

                value = getValue(n, "ns1:value/ns" + index + ":seconds");
                if (StringUtils.isNotBlank(value)) {
                    val.setSeconds(Integer.valueOf(value));
                }

                return val;
            }

            // Parse date value

            value = getValue(n, "ns1:value/ns" + index + ":day");
            if (StringUtils.isNotBlank(value)) {

                WSDateValue val = new WSDateValue();
                val.setResourceID(id);
                val.setDay(Byte.valueOf(value));

                value = getValue(n, "ns1:value/ns" + index + ":month");
                if (StringUtils.isNotBlank(value)) {
                    val.setMonth(Byte.valueOf(value));
                }

                value = getValue(n, "ns1:value/ns" + index + ":year");
                if (StringUtils.isNotBlank(value)) {
                    val.setYear(Short.valueOf(value));
                }

                return val;
            }

            // Parse enum value

            value = getValue(n, "ns1:value/ns" + index + ":definitionTypeID");
            if (StringUtils.isNotBlank(value)) {

                WSEnumValue val = new WSEnumValue();
                val.setResourceID(id);
                val.setDefinitionTypeID(Integer.valueOf(value));

                value = getValue(n, "ns1:value/ns" + index + ":enumValueID");
                if (StringUtils.isNotBlank(value)) {
                    val.setEnumValueID(Integer.valueOf(value));
                }

                value = getValue(n, "ns1:value/ns" + index + ":enumName");
                if (StringUtils.isNotBlank(value)) {
                    val.setEnumName(value);
                }

                return val;
            }

            // Parse week day value

            value = getValue(n, "ns1:value/ns" + index + ":weekdayNumber");
            if (StringUtils.isNotBlank(value)) {
                WSWeekdayValue val = new WSWeekdayValue();
                val.setResourceID(id);
                val.setWeekdayNumber(Integer.valueOf(value));

                return val;
            }

            throw new IllegalArgumentException("Unsupported value type");
        }

        return null;

    }

    private String getValue(Node n, String expr) throws XPathExpressionException {

        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(new NamespaceContext() {

            public String getNamespaceURI(String prefix) {

                if (prefix == null) {
                    throw new NullPointerException("Null prefix");
                } else if ("SOAP-ENV".equals(prefix)) {
                    return "http://schemas.xmlsoap.org/soap/envelope/";
                } else if ("ns1".equals(prefix)) {
                    return "utcs";
                }
                // else if ("ns2".equals(prefix)) return "utcs.values";
                return "utcs.values";
                // return null;
            }

            public String getPrefix(String uri) {
                return null;
            }

            @SuppressWarnings("rawtypes")
            public Iterator getPrefixes(String uri) {
                throw new UnsupportedOperationException();
            }
        });

        XPathExpression pathExpr = xpath.compile(expr);
        return (String) pathExpr.evaluate(n, XPathConstants.STRING);
    }

    /**
     * Update resource value to controller.
     * 
     * 
     * @param value
     *            Resource value.
     * @return True if value is successfully updated.
     */
    public boolean resourceUpdate(WSResourceValue value) throws IhcExecption {

        boolean retval = false;

        if (value instanceof WSFloatingPointValue) {
            retval = resourceUpdate((WSFloatingPointValue) value);
        }

        else if (value instanceof WSBooleanValue) {
            retval = resourceUpdate((WSBooleanValue) value);
        }

        else if (value instanceof WSIntegerValue) {
            retval = resourceUpdate((WSIntegerValue) value);
        }

        else if (value instanceof WSTimerValue) {
            retval = resourceUpdate((WSTimerValue) value);
        }

        else if (value instanceof WSWeekdayValue) {
            retval = resourceUpdate((WSWeekdayValue) value);
        }

        else if (value instanceof WSEnumValue) {
            retval = resourceUpdate((WSEnumValue) value);
        }

        else if (value instanceof WSTimeValue) {
            retval = resourceUpdate((WSTimeValue) value);
        }

        else if (value instanceof WSDateValue) {
            retval = resourceUpdate((WSDateValue) value);
        }

        else {
            throw new IhcExecption("Unsupported value type " + value.getClass().toString());
        }

        return retval;
    }

    public boolean resourceUpdate(WSBooleanValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSBooleanValue\">" + "   <q1:value>%s</q1:value>"
                + "  </value>" + "  <resourceID>%s</resourceID>" + "  <isValueRuntime>true</isValueRuntime>"
                + " </setResourceValue1>" + "</soap:Body>" + "</soap:Envelope>";

        String query = String.format(soapQuery, value.isValue() ? "true" : "false", value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSFloatingPointValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSFloatingPointValue\">"
                + "   <q1:maximumValue>%s</q1:maximumValue>" + "   <q1:minimumValue>%s</q1:minimumValue>"
                + "   <q1:floatingPointValue>%s</q1:floatingPointValue>" + "  </value>"
                + "  <resourceID>%s</resourceID>" + "  <isValueRuntime>true</isValueRuntime>"
                + " </setResourceValue1>" + "</soap:Body>" + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getMaximumValue(), value.getMinimumValue(),
                value.getFloatingPointValue(), value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSIntegerValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSIntegerValue\">"
                + "   <q1:maximumValue>%s</q1:maximumValue>" + "   <q1:minimumValue>%s</q1:minimumValue>"
                + "   <q1:integer>%s</q1:integer>" + "  </value>" + "  <resourceID>%s</resourceID>"
                + "  <isValueRuntime>true</isValueRuntime>" + " </setResourceValue1>" + "</soap:Body>"
                + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getMaximumValue(), value.getMinimumValue(),
                value.getInteger(), value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSTimerValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSTimerValue\">"
                + "   <q1:milliseconds>%s</q1:milliseconds>" + "  </value>" + "  <resourceID>%s</resourceID>"
                + "  <isValueRuntime>true</isValueRuntime>" + " </setResourceValue1>" + "</soap:Body>"
                + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getMilliseconds(), value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSWeekdayValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSWeekdayValue\">"
                + "   <q1:weekdayNumber>%s</q1:weekdayNumber>" + "  </value>" + "  <resourceID>%s</resourceID>"
                + "  <isValueRuntime>true</isValueRuntime>" + " </setResourceValue1>" + "</soap:Body>"
                + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getWeekdayNumber(), value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSEnumValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSEnumValue\">"
                + "   <q1:definitionTypeID>%s</q1:definitionTypeID>" + "   <q1:enumValueID>%s</q1:enumValueID>"
                + "   <q1:enumName>%s</q1:enumName>" + "  </value>" + "  <resourceID>%s</resourceID>"
                + "  <isValueRuntime>true</isValueRuntime>" + " </setResourceValue1>" + "</soap:Body>"
                + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getDefinitionTypeID(), value.getEnumValueID(),
                value.getEnumName(), value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSTimeValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSTimeValue\">" + "   <q1:hours>%s</q1:hours>"
                + "   <q1:minutes>%s</q1:minutes>" + "   <q1:seconds>%s</q1:seconds>" + "  </value>"
                + "  <resourceID>%s</resourceID>" + "  <isValueRuntime>true</isValueRuntime>"
                + " </setResourceValue1>" + "</soap:Body>" + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getHours(), value.getMinutes(), value.getSeconds(),
                value.getResourceID());

        return doResourceUpdate(query);
    }

    public boolean resourceUpdate(WSDateValue value) throws IhcExecption {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + " <setResourceValue1 xmlns=\"utcs\">"
                + "  <value xmlns:q1=\"utcs.values\" xsi:type=\"q1:WSDateValue\">" + "   <q1:month>%s</q1:month>"
                + "   <q1:year>%s</q1:year>" + "   <q1:day>%s</q1:day>" + "  </value>"
                + "  <resourceID>%s</resourceID>" + "  <isValueRuntime>true</isValueRuntime>"
                + " </setResourceValue1>" + "</soap:Body>" + "</soap:Envelope>";

        String query = String.format(soapQuery, value.getMonth(), value.getYear(), value.getDay(),
                value.getResourceID());

        return doResourceUpdate(query);
    }

    private boolean doResourceUpdate(String query) throws IhcExecption {

        openConnection(url);
        String response = sendQuery(query, timeout);
        closeConnection();

        return Boolean.parseBoolean(
                WSBaseDataType.parseValue(response, "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:setResourceValue2"));
    }

    /**
     * Enable resources runtime value notifications.
     * 
     * @param resourceIdList
     *            List of resource Identifiers.
     * @return True is connection successfully opened.
     */
    public void enableRuntimeValueNotifications(List<? extends Integer> resourceIdList) throws IhcExecption {

        final String soapQueryPrefix = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                + "<soap:Body>" + "<enableRuntimeValueNotifications1 xmlns=\"utcs\">";

        final String soapQuerySuffix = "</enableRuntimeValueNotifications1>" + "</soap:Body>" + "</soap:Envelope>";

        String query = soapQueryPrefix;
        for (int i : resourceIdList) {
            query += "<xsd:arrayItem>" + i + "</xsd:arrayItem>";
        }
        query += soapQuerySuffix;

        openConnection(url);
        @SuppressWarnings("unused")
        String response = sendQuery(query, timeout);
        closeConnection();
    }

    /**
     * Wait runtime value notifications.
     * 
     * Runtime value notification should firstly be activated by
     * enableRuntimeValueNotifications function.
     * 
     * @param timeoutInSeconds
     *            How many seconds to wait notifications.
     * @return List of received runtime value notifications.
     * @throws SocketTimeoutException 
     */
    public List<? extends WSResourceValue> waitResourceValueNotifications(int timeoutInSeconds)
            throws IhcExecption, SocketTimeoutException {

        final String soapQuery = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:utcs=\"utcs\">"
                + "<soapenv:Header/>" + "<soapenv:Body>"
                + " <utcs:waitForResourceValueChanges1>%s</utcs:waitForResourceValueChanges1>" + "</soapenv:Body>"
                + "</soapenv:Envelope>";

        String query = String.format(soapQuery, timeoutInSeconds);
        openConnection(url);
        String response = sendQuery(query, timeout + timeoutInSeconds * 1000);
        closeConnection();

        List<WSResourceValue> resourceValueList = new ArrayList<WSResourceValue>();

        NodeList nodeList;
        try {
            nodeList = parseList(response,
                    "/SOAP-ENV:Envelope/SOAP-ENV:Body/ns1:waitForResourceValueChanges2/ns1:arrayItem");

            if (nodeList.getLength() == 1) {
                String resourceId = getValue(nodeList.item(0), "ns1:resourceID");
                if (resourceId == null || resourceId == "") {
                    throw new SocketTimeoutException();
                }
            }

            for (int i = 0; i < nodeList.getLength(); i++) {

                int index = i + 2;

                WSResourceValue newVal = parseResourceValue(nodeList.item(i), index);
                resourceValueList.add(newVal);
            }

            return resourceValueList;

        } catch (XPathExpressionException e) {
            throw new IhcExecption(e);
        } catch (UnsupportedEncodingException e) {
            throw new IhcExecption(e);
        }
    }

}