Wiki source code of Web Services-Problems

Version 7.1 by Francis Labrie on 2007/10/09 13:16

Show last authors
1 |= Contents
2 | {{section}}
3 # [#Problems]
4 ## [#DirectToWebService can't return a WSDL with secure HTTPS references in it]
5 ## [#SOAP serializers and deserializers registered with {{WOWebServiceRegistrar}} class doesn't appear in the WSDL schema]
6 ## [#DirectToWebService can't return a WSDL with custom namespace and definitions name in it]
7 ## [#WOWebServiceClient class can't connect to a server that requires an authentication]
8 ## [#Web Services can't return a WSDL with secure HTTPS references specifying port other than the default 443]
9 {{/section}}
10
11 = Problems =
12
13 This section describes some problems and bugs that sometimes occur when using //WebObjects Web Services//.
14
15 == DirectToWebService can't return a WSDL with secure HTTPS references in it ==
16
17 **Authors:** Francis Labrie
18 **Affected products:** //WebObjects// 5.2.x, 5.3.x
19 **Bug reference:** [[rdar://3546304]]
20
21 === Problem: ===
22
23 A //DirectToWebService// defined Web Services doesn't return correct WSDL document, even if the correct procedure (see [[Secure Web Services]]) is followed. So only classes oriented Web Services manually registered with the ##com.webobjects.appserver.WOWebServiceRegistrar## class seems to generate a correct WSDL.
24
25 === Solution: ===
26
27 Darel Lee from Apple told me that right now, the dynamic WSDL generation is not exposed to developers so there currently isn't a clean solution to perform this. One workaround is to hardcode rules (of ##com.webobjects.directtoweb.Assignment## type) with the ##serviceLocationURL## key for each operation that you want to use secure HTTPS references. For instance:
28
29 {{code}}
30
31 ((operationName="anOperation") and (serviceName="Service")) ->
32 serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"
33
34 {{/code}}
35
36 If you need all operation to be called using the secure protocol, you can also define a more generic rule like this one:
37
38 {{code}}
39
40 (serviceName="Service") ->
41 serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"
42
43 {{/code}}
44
45 == SOAP serializers and deserializers registered with ##WOWebServiceRegistrar## class doesn't appear in the WSDL schema ==
46
47 **Authors:** Francis Labrie
48 **Affected products:** //WebObjects// 5.2.x, 5.3.x
49 **Bug reference:** [[rdar://3546330]]
50
51 === Problem: ===
52
53 Custom SOAP serializers and deserializers registered to Web Services with ##com.webobjects.appserver.WOWebServiceRegistrar## class are never added to the types / schema definition of the WSDL. The only type definitions shown are the following:
54
55 {{code}}
56
57 <types>
58 <schema targetNamespace="http://lang.java/" xmlns:soapenc=
59 "http://schemas.xmlsoap.org/soap/encoding/" xmlns=
60 "http://www.w3.org/2001/XMLSchema">
61 <complexType name="Class">
62 <sequence/>
63 </complexType>
64 <complexType name="ArrayOf_xsd_any">
65 <complexContent>
66 <restriction base="soapenc:Array">
67 <attribute ref="soapenc:arrayType" wsdl:arrayType=
68 "xsd:any[]"/>
69 </restriction>
70 </complexContent>
71 </complexType>
72 <element name="ArrayOf_xsd_any" nillable="true" type=
73 "lang:ArrayOf_xsd_any"/>
74 </schema>
75 <schema targetNamespace="http://www.apple.com/webobjects/
76 webservices/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/
77 soap/encoding/" xmlns="http://www.w3.org/2001/XMLSchema">
78 <complexType name="EOGlobalID">
79 <element name="entityName" type="xsd:string"/>
80 <element name="primaryKeys" type="lang:ArrayOf_xsd_any"/>
81 </complexType>
82 <element name="EOGlobalID" type="tns:EOGlobalID"/>
83 <complexType name="EOEnterpriseObject">
84 <element name="entityName" type="xsd:string"/>
85 <element name="globalID" type="webobjects:EOGlobalID"/>
86 <element name="properties" type="soapenc:Struct"/>
87 </complexType>
88 </schema>
89 </types>
90
91 {{/code}}
92
93 === Solution: ===
94
95 You must know that all complexe data types referred in operations will be added to the WSDL types definition. But if a complexe type makes references to others complexe types, you should make sure you add proper calls in the ##writeSchema(Types)## method of the class serializer. For example:
96
97 {{code}}
98
99 public boolean writeSchema(Types types)
100 throws Exception {
101 ...
102 // Add type for Foo
103 types.writeType(Foo.class, _FooQName);
104 ...
105
106 return true;
107 }
108
109 {{/code}}
110
111 Unfortunately, I don't know a dynamic workaround for all possible cases right now. At least a static and complete WSDL can be shared through a direct action, but it's not very handy though...
112
113 == DirectToWebService can't return a WSDL with custom namespace and definitions name in it ==
114
115 **Authors:** Francis Labrie
116 **Affected products:** //WebObjects// 5.2.x, 5.3.x
117 **Bug reference:**
118
119 === Problem: ===
120
121 A //DirectToWebService// defined Web Services can't return a WSDL with custom values for properties like namespaces and definitions name. Worse, the generated namespace can even contains WebObjects application instance number or the wrong hostname.
122
123 === Solution: ===
124
125 Base on a tips from Darel Lee, I've found in the ##com.webobjects.webservices.generation.private.WOWSDLTemplate## class some extras key definitions read from the ##user.d2wmodel## //DirectToWebService// rule file. For instance:
126
127 * **serviceLocationURL:** a key that allow the setting of the location URL for an operation. This is usefull if you need your WebServices to be reached using a secure HTTPS reference;
128 * **WSDLDefinitionName:** a key that allow the definitions name change. So instead of having "ServiceNameDefinition", you can set this value;
129 * **WSDLTargetNamespace:** a key that allow the namespace change. This is the most usefull: you can avoid dynamic generation depending on WebObjects HTTP Adaptor with this.
130
131 Here is an example of rule definition changing the above values:
132
133 {{code}}
134
135 (se(serviceName="Service") ->
136 WSDLTargetNamespace="https://host.net/cgi-bin/Service.woa/ws/Service"
137 (serviceName="Service") ->
138 WSDLDefinitionName="AnotherDefinition"
139 ((operationName="anOperation") and (serviceName="Service")) ->
140 serviceLocationURL="https://host.net/cgi-bin/Service.woa/ws/Service"
141
142 {{/code}}
143
144 == WOWebServiceClient class can't connect to a server that requires an authentication ==
145
146 **Authors:** Francis Labrie
147 **Affected products:** //WebObjects// 5.2.x, 5.3.x
148 **Bug reference:** [[rdar://3568441]]
149
150 === Problem: ===
151
152 The ##com.webobjects.webservices.client.WOWebServiceClient## class can't connect to a server that requires a Basic HTTP Authentication despite the fact this class offers a way to register a security delegate (see ##setSecurityDelegateForServiceNamed(Object, String)## instance method).
153
154 Normally, the ##processClientRequest(MessageContext)## delegate method (see ##com.webobjects.webservices.support.WOSecurityDelegateinterface## documentation) would allow an easy way to set a username and a password to the message context. But there is a problem related to the design of the class: to register a security delegate, the ##WOWebServiceClient## class has to fetch the Web Services Definition Language (WSDL) XML document. But to get access to this WSDL, an authentication header must be set. This is the classic chicken and egg problem...
155
156 === Solution: ===
157
158 The best would be to add a default method to ##WOWebServiceClient## class to register a default security delegate that is not related to a service name before the class fetch the WSDL. But unfortunately, all key methods that would allow this kind of behavior change are privates, so subclassing is not a solution...
159
160 But a workaround is still possible:
161
162 1. Fetch the WSDL document yourself and store it to the local filesystem, using the java.net.URL instance and setting up the Basic HTTP Authentication header field of the ##java.net.URLConnection## yourself;
163 1. Instanciate another ##java.net.URL## class that refer to the local WSDL document file;
164 1. Instanciate the ##com.webobjects.webservices.client.WOWebServiceClient## class using the file URL;
165 1. Set for each service a security delegate that will add the proper credential information for the Basic HTTP Authentication.
166
167 That's it. It looks like a big hack, but it works...
168
169 == Web Services can't return a WSDL with secure HTTPS references specifying port other than the default 443 ==
170
171 **Authors:** Francis Labrie
172 **Affected products:** //WebObjects// 5.2.x, 5.3.x
173 **Bug reference:** [[rdar://4196417]]
174
175 === Problem: ===
176
177 HTTPS protocol references can be published in Web Services WSDL. But unfortunately, WebObjects seems to ignore ports other than the default 443.
178
179 This problem is related to the bad way ##com.webobjects.appserver.WORequest## builds the URL prefix: if the protocol is secure and no port (i.e. 0) is set when calling the ##//completeURLPrefix(StringBuffer, boolean, int)//##// method, the port will always be 443. Unfortunately, Web Services ##com.webobjects.appserver.##//##private.WOWebService## class seems to call this method without setting the port number.
180
181 === Solution: ===
182
183 To work around this bug, you can subclass the ##com.webobjects.appserver.WORequest## class like this:
184
185 {{code}}
186
187 package com.smoguli.appserver;
188
189 import com.webobjects.appserver.WORequest;
190 import com.webobjects.foundation.NSData;
191 import com.webobjects.foundation.NSDictionary;
192
193 /**
194 * This class provide fixed {@link com.webobjects.appserver.WORequest} methods.
195 * To use it, just overload the {@link com.webobjects.appserver.WOApplication.
196 * createRequest(String,String,String,NSDictionary,NSData,NSDictionary)} method
197 * to instanciate this class instead.
198 *
199 * @author Francis Labrie <francis.labrie at smoguli.com>
200 */
201 public class WOFixedRequest extends WORequest {
202
203 /**
204 * @see com.webobjects.appserver.WORequest#WORequest(String,String,String,
205 * NSDictionary,NSData,NSDictionary)
206 */
207 public WOFixedRequest(String method, String url, String httpVersion, NSDictionary headers, NSData content, NSDictionary info) {
208 super(method, url, httpVersion, headers, content, info);
209 } // WOFixedRequest
210
211 /**
212 * This method builds the URL prefix into the <code>urlPrefix</code> buffer
213 * with the appropriate protocol (<code>http</code> or <code>https</code>)
214 * and the right TCP port. But unlike the {@link com.webobjects.appserver.
215 * WORequest#_completeURLPrefix(StringBuffer,boolean,int} method, it
216 * supports secure HTTP protocol (<code>https</code>) with port other than
217 * <code>443</code>, even if the <code>port</code> parameter is set
218 * <code>0</code>.
219 *
220 * @param urlPrefix the buffer that receives the contructed URL.
221 * @param isSecure a flag indicating if the protocol is secure.
222 * @param port the port number.
223 */
224 public void _completeURLPrefix(StringBuffer urlPrefix, boolean isSecure, int port) {
225 if(isSecure && (port == 0)) {
226 String serverPort;
227
228 serverPort = _serverPort();
229 if((serverPort != null) && !serverPort.equals("443")) {
230 try {
231 port = Integer.parseInt(serverPort);
232 } catch(NumberFormatException exception) {} // catch
233 } // if
234 } // if
235
236 super._completeURLPrefix(urlPrefix, isSecure, port);
237 } // _completeURLPrefix
238 } // WOFixedRequest
239
240 {{/code}}