View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.transport.impl;
17  
18  import org.fourthline.cling.model.Constants;
19  import org.fourthline.cling.model.XMLUtil;
20  import org.fourthline.cling.model.message.UpnpMessage;
21  import org.fourthline.cling.model.message.gena.IncomingEventRequestMessage;
22  import org.fourthline.cling.model.message.gena.OutgoingEventRequestMessage;
23  import org.fourthline.cling.model.meta.StateVariable;
24  import org.fourthline.cling.model.state.StateVariableValue;
25  import org.fourthline.cling.transport.spi.GENAEventProcessor;
26  import org.fourthline.cling.model.UnsupportedDataException;
27  import org.w3c.dom.Document;
28  import org.w3c.dom.Element;
29  import org.w3c.dom.Node;
30  import org.w3c.dom.NodeList;
31  import org.xml.sax.ErrorHandler;
32  import org.xml.sax.InputSource;
33  import org.xml.sax.SAXException;
34  import org.xml.sax.SAXParseException;
35  
36  import javax.xml.parsers.DocumentBuilder;
37  import javax.xml.parsers.DocumentBuilderFactory;
38  import javax.xml.parsers.FactoryConfigurationError;
39  
40  import java.io.StringReader;
41  import java.util.logging.Level;
42  import java.util.logging.Logger;
43  
44  /**
45   * Default implementation based on the <em>W3C DOM</em> XML processing API.
46   *
47   * @author Christian Bauer
48   */
49  public class GENAEventProcessorImpl implements GENAEventProcessor, ErrorHandler {
50  
51      private static Logger log = Logger.getLogger(GENAEventProcessor.class.getName());
52  
53      protected DocumentBuilderFactory createDocumentBuilderFactory() throws FactoryConfigurationError {
54      	return DocumentBuilderFactory.newInstance();
55      }
56  
57      public void writeBody(OutgoingEventRequestMessage requestMessage) throws UnsupportedDataException {
58          log.fine("Writing body of: " + requestMessage);
59  
60          try {
61  
62              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
63              factory.setNamespaceAware(true);
64              Document d = factory.newDocumentBuilder().newDocument();
65              Element propertysetElement = writePropertysetElement(d);
66  
67              writeProperties(d, propertysetElement, requestMessage);
68  
69              requestMessage.setBody(UpnpMessage.BodyType.STRING, toString(d));
70  
71              if (log.isLoggable(Level.FINER)) {
72                  log.finer("===================================== GENA BODY BEGIN ============================================");
73                  log.finer(requestMessage.getBody().toString());
74                  log.finer("====================================== GENA BODY END =============================================");
75              }
76  
77          } catch (Exception ex) {
78              throw new UnsupportedDataException("Can't transform message payload: " + ex.getMessage(), ex);
79          }
80      }
81  
82      public void readBody(IncomingEventRequestMessage requestMessage) throws UnsupportedDataException {
83  
84          log.fine("Reading body of: " + requestMessage);
85          if (log.isLoggable(Level.FINER)) {
86              log.finer("===================================== GENA BODY BEGIN ============================================");
87              log.finer(requestMessage.getBody() != null ? requestMessage.getBody().toString() : "null");
88              log.finer("-===================================== GENA BODY END ============================================");
89          }
90  
91          String body = getMessageBody(requestMessage);
92          try {
93  
94              DocumentBuilderFactory factory = createDocumentBuilderFactory();
95              factory.setNamespaceAware(true);
96              DocumentBuilder documentBuilder = factory.newDocumentBuilder();
97              documentBuilder.setErrorHandler(this);
98  
99              Document d = documentBuilder.parse(
100                 new InputSource(new StringReader(body))
101             );
102 
103             Element propertysetElement = readPropertysetElement(d);
104 
105             readProperties(propertysetElement, requestMessage);
106 
107         } catch (Exception ex) {
108             throw new UnsupportedDataException("Can't transform message payload: " + ex.getMessage(), ex, body);
109         }
110     }
111 
112     /* ##################################################################################################### */
113 
114     protected Element writePropertysetElement(Document d) {
115         Element propertysetElement = d.createElementNS(Constants.NS_UPNP_EVENT_10, "e:propertyset");
116         d.appendChild(propertysetElement);
117         return propertysetElement;
118     }
119 
120     protected Element readPropertysetElement(Document d) {
121 
122         Element propertysetElement = d.getDocumentElement();
123         if (propertysetElement == null || !getUnprefixedNodeName(propertysetElement).equals("propertyset")) {
124             throw new RuntimeException("Root element was not 'propertyset'");
125         }
126         return propertysetElement;
127     }
128 
129     /* ##################################################################################################### */
130 
131     protected void writeProperties(Document d, Element propertysetElement, OutgoingEventRequestMessage message) {
132         for (StateVariableValue stateVariableValue : message.getStateVariableValues()) {
133             Element propertyElement = d.createElementNS(Constants.NS_UPNP_EVENT_10, "e:property");
134             propertysetElement.appendChild(propertyElement);
135             XMLUtil.appendNewElement(
136                     d,
137                     propertyElement,
138                     stateVariableValue.getStateVariable().getName(),
139                     stateVariableValue.toString()
140             );
141         }
142     }
143 
144     protected void readProperties(Element propertysetElement, IncomingEventRequestMessage message) {
145         NodeList propertysetElementChildren = propertysetElement.getChildNodes();
146 
147         StateVariable[] stateVariables = message.getService().getStateVariables();
148 
149         for (int i = 0; i < propertysetElementChildren.getLength(); i++) {
150             Node propertysetChild = propertysetElementChildren.item(i);
151 
152             if (propertysetChild.getNodeType() != Node.ELEMENT_NODE)
153                 continue;
154 
155             if (getUnprefixedNodeName(propertysetChild).equals("property")) {
156 
157                 NodeList propertyChildren = propertysetChild.getChildNodes();
158 
159                 for (int j = 0; j < propertyChildren.getLength(); j++) {
160                     Node propertyChild = propertyChildren.item(j);
161 
162                     if (propertyChild.getNodeType() != Node.ELEMENT_NODE)
163                         continue;
164 
165                     String stateVariableName = getUnprefixedNodeName(propertyChild);
166                     for (StateVariable stateVariable : stateVariables) {
167                         if (stateVariable.getName().equals(stateVariableName)) {
168                             log.fine("Reading state variable value: " + stateVariableName);
169                             String value = XMLUtil.getTextContent(propertyChild);
170                             message.getStateVariableValues().add(
171                                     new StateVariableValue(stateVariable, value)
172                             );
173                             break;
174                         }
175                     }
176 
177                 }
178             }
179         }
180     }
181 
182     /* ##################################################################################################### */
183 
184     protected String getMessageBody(UpnpMessage message) throws UnsupportedDataException {
185         if (!message.isBodyNonEmptyString())
186             throw new UnsupportedDataException(
187                 "Can't transform null or non-string/zero-length body of: " + message
188             );
189         return message.getBodyString().trim();
190     }
191 
192     protected String toString(Document d) throws Exception {
193         // Just to be safe, no newline at the end
194         String output = XMLUtil.documentToString(d);
195         while (output.endsWith("\n") || output.endsWith("\r")) {
196             output = output.substring(0, output.length() - 1);
197         }
198 
199         return output;
200     }
201 
202     protected String getUnprefixedNodeName(Node node) {
203         return node.getPrefix() != null
204                 ? node.getNodeName().substring(node.getPrefix().length() + 1)
205                 : node.getNodeName();
206     }
207 
208     public void warning(SAXParseException e) throws SAXException {
209         log.warning(e.toString());
210     }
211 
212     public void error(SAXParseException e) throws SAXException {
213         throw e;
214     }
215 
216     public void fatalError(SAXParseException e) throws SAXException {
217         throw e;
218     }
219 }
220