1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.fourthline.cling.transport.impl;
17
18 import java.util.*;
19 import java.util.logging.Logger;
20
21 import org.fourthline.cling.model.action.ActionArgumentValue;
22 import org.fourthline.cling.model.action.ActionException;
23 import org.fourthline.cling.model.action.ActionInvocation;
24 import org.fourthline.cling.model.message.control.ActionRequestMessage;
25 import org.fourthline.cling.model.message.control.ActionResponseMessage;
26 import org.fourthline.cling.model.meta.ActionArgument;
27 import org.fourthline.cling.model.types.ErrorCode;
28 import org.fourthline.cling.transport.spi.SOAPActionProcessor;
29 import org.fourthline.cling.model.UnsupportedDataException;
30 import org.seamless.xml.XmlPullParserUtils;
31 import org.xmlpull.v1.XmlPullParser;
32
33 import javax.enterprise.inject.Alternative;
34
35
36
37
38
39
40
41
42
43
44
45
46
47 @Alternative
48 public class PullSOAPActionProcessorImpl extends SOAPActionProcessorImpl {
49
50 protected static Logger log = Logger.getLogger(SOAPActionProcessor.class.getName());
51
52 public void readBody(ActionRequestMessage requestMessage, ActionInvocation actionInvocation) throws UnsupportedDataException {
53 String body = getMessageBody(requestMessage);
54 try {
55 XmlPullParser xpp = XmlPullParserUtils.createParser(body);
56 readBodyRequest(xpp, requestMessage, actionInvocation);
57 } catch (Exception ex) {
58 throw new UnsupportedDataException("Can't transform message payload: " + ex, ex, body);
59 }
60 }
61
62 public void readBody(ActionResponseMessage responseMsg, ActionInvocation actionInvocation) throws UnsupportedDataException {
63 String body = getMessageBody(responseMsg);
64 try {
65 XmlPullParser xpp = XmlPullParserUtils.createParser(body);
66 readBodyElement(xpp);
67 readBodyResponse(xpp, actionInvocation);
68 } catch (Exception ex) {
69 throw new UnsupportedDataException("Can't transform message payload: " + ex, ex, body);
70 }
71 }
72
73 protected void readBodyElement(XmlPullParser xpp) throws Exception {
74 XmlPullParserUtils.searchTag(xpp, "Body");
75 }
76
77 protected void readBodyRequest(XmlPullParser xpp, ActionRequestMessage requestMessage, ActionInvocation actionInvocation) throws Exception {
78 XmlPullParserUtils.searchTag(xpp, actionInvocation.getAction().getName());
79 readActionInputArguments(xpp, actionInvocation);
80 }
81
82 protected void readBodyResponse(XmlPullParser xpp, ActionInvocation actionInvocation) throws Exception {
83
84 int event;
85 do {
86 event = xpp.next();
87 if (event == XmlPullParser.START_TAG) {
88 if (xpp.getName().equals("Fault")) {
89 ActionException e = readFaultElement(xpp);
90 actionInvocation.setFailure(e);
91 return;
92 } else if (xpp.getName().equals(actionInvocation.getAction().getName() + "Response")) {
93 readActionOutputArguments(xpp, actionInvocation);
94 return;
95 }
96 }
97
98 }
99 while (event != XmlPullParser.END_DOCUMENT && (event != XmlPullParser.END_TAG || !xpp.getName().equals("Body")));
100
101 throw new ActionException(
102 ErrorCode.ACTION_FAILED,
103 String.format("Action SOAP response do not contain %s element",
104 actionInvocation.getAction().getName() + "Response"
105 )
106 );
107 }
108
109 protected void readActionInputArguments(XmlPullParser xpp, ActionInvocation actionInvocation) throws Exception {
110 actionInvocation.setInput(readArgumentValues(xpp, actionInvocation.getAction().getInputArguments()));
111 }
112
113 protected void readActionOutputArguments(XmlPullParser xpp, ActionInvocation actionInvocation) throws Exception {
114 actionInvocation.setOutput(readArgumentValues(xpp, actionInvocation.getAction().getOutputArguments()));
115 }
116
117 protected Map<String, String> getMatchingNodes(XmlPullParser xpp, ActionArgument[] args) throws Exception {
118
119
120 List<String> names = new ArrayList<>();
121 for (ActionArgument argument : args) {
122 names.add(argument.getName().toUpperCase(Locale.ROOT));
123 for (String alias : Arrays.asList(argument.getAliases())) {
124 names.add(alias.toUpperCase(Locale.ROOT));
125 }
126 }
127
128 Map<String, String> matches = new HashMap<>();
129
130 String enclosingTag = xpp.getName();
131
132 int event;
133 do {
134 event = xpp.next();
135 if(event == XmlPullParser.START_TAG && names.contains(xpp.getName().toUpperCase(Locale.ROOT))) {
136 matches.put(xpp.getName(), xpp.nextText());
137 }
138
139 }
140 while (event != XmlPullParser.END_DOCUMENT && (event != XmlPullParser.END_TAG || !xpp.getName().equals(enclosingTag)));
141
142 if (matches.size() < args.length) {
143 throw new ActionException(
144 ErrorCode.ARGUMENT_VALUE_INVALID,
145 "Invalid number of input or output arguments in XML message, expected "
146 + args.length + " but found " + matches.size()
147 );
148 }
149 return matches;
150 }
151
152 protected ActionArgumentValue[] readArgumentValues(XmlPullParser xpp, ActionArgument[] args) throws Exception {
153
154 Map<String, String> matches = getMatchingNodes(xpp, args);
155
156 ActionArgumentValue[] values = new ActionArgumentValue[args.length];
157
158 for (int i = 0; i < args.length; i++) {
159
160 ActionArgument arg = args[i];
161 String value = findActionArgumentValue(matches, arg);
162 if (value == null) {
163 throw new ActionException(
164 ErrorCode.ARGUMENT_VALUE_INVALID,
165 "Could not find argument '" + arg.getName() + "' node");
166 }
167
168 log.fine("Reading action argument: " + arg.getName());
169 values[i] = createValue(arg, value);
170 }
171 return values;
172 }
173
174 protected String findActionArgumentValue(Map<String, String> entries, ActionArgument arg) {
175 for (Map.Entry<String, String> entry : entries.entrySet()) {
176 if (arg.isNameOrAlias(entry.getKey())) return entry.getValue();
177 }
178 return null;
179 }
180
181 protected ActionException readFaultElement(XmlPullParser xpp) throws Exception {
182
183
184 String errorCode = null;
185 String errorDescription = null;
186
187 XmlPullParserUtils.searchTag(xpp, "UPnPError");
188
189 int event;
190 do {
191 event = xpp.next();
192 if (event == XmlPullParser.START_TAG) {
193 String tag = xpp.getName();
194 if (tag.equals("errorCode")) {
195 errorCode = xpp.nextText();
196 } else if (tag.equals("errorDescription")) {
197 errorDescription = xpp.nextText();
198 }
199 }
200 }
201 while (event != XmlPullParser.END_DOCUMENT && (event != XmlPullParser.END_TAG || !xpp.getName().equals("UPnPError")));
202
203 if (errorCode != null) {
204 try {
205 int numericCode = Integer.valueOf(errorCode);
206 ErrorCode standardErrorCode = ErrorCode.getByCode(numericCode);
207 if (standardErrorCode != null) {
208 log.fine("Reading fault element: " + standardErrorCode.getCode() + " - " + errorDescription);
209 return new ActionException(standardErrorCode, errorDescription, false);
210 } else {
211 log.fine("Reading fault element: " + numericCode + " - " + errorDescription);
212 return new ActionException(numericCode, errorDescription);
213 }
214 } catch (NumberFormatException ex) {
215 throw new RuntimeException("Error code was not a number");
216 }
217 }
218
219 throw new RuntimeException("Received fault element but no error code");
220 }
221 }