WsdlBuilder.java
001 /*
002  *
003  * Licensed to the Apache Software Foundation (ASF) under one
004  * or more contributor license agreements.  See the NOTICE file
005  * distributed with this work for additional information
006  * regarding copyright ownership.  The ASF licenses this file
007  * to you under the Apache License, Version 2.0 (the
008  * "License"); you may not use this file except in compliance
009  * with the License.  You may obtain a copy of the License at
010  *
011  *   http://www.apache.org/licenses/LICENSE-2.0
012  *
013  * Unless required by applicable law or agreed to in writing,
014  * software distributed under the License is distributed on an
015  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016  * KIND, either express or implied.  See the License for the
017  * specific language governing permissions and limitations
018  * under the License.
019  *
020  */
021 package org.apache.qpid.management.wsdm.capabilities;
022 
023 import java.net.InetAddress;
024 import java.util.HashMap;
025 import java.util.Map;
026 
027 import javax.management.MBeanAttributeInfo;
028 import javax.management.MBeanOperationInfo;
029 import javax.management.MBeanParameterInfo;
030 import javax.management.ObjectName;
031 import javax.xml.XMLConstants;
032 import javax.xml.namespace.QName;
033 
034 import org.apache.muse.core.Environment;
035 import org.apache.muse.util.ReflectUtils;
036 import org.apache.muse.util.xml.XmlUtils;
037 import org.apache.muse.ws.wsdl.WsdlUtils;
038 import org.apache.qpid.management.Messages;
039 import org.apache.qpid.management.Names;
040 import org.apache.qpid.management.wsdm.muse.serializer.ObjectSerializer;
041 import org.apache.qpid.qman.debug.WsdlDebugger;
042 import org.apache.qpid.transport.util.Logger;
043 import org.apache.xpath.XPathAPI;
044 import org.w3c.dom.Attr;
045 import org.w3c.dom.Document;
046 import org.w3c.dom.Element;
047 /**
048  * TO BE IMPROVED USING JAXB!!
049  
050  @author Andrea Gazzarini
051  */
052 class WsdlBuilder implements IArtifactBuilder {
053 
054   private final static Logger LOGGER = Logger.get(WsdlBuilder.class);
055   
056   private Environment _environment;
057   private Document _document;
058   private Element schema;
059   
060   private ObjectSerializer serializer = new ObjectSerializer();
061 
062   private ObjectName _objectName;
063   
064   private boolean mapTypeHasBeenDeclared;
065   private boolean uuidTypeHasBeenDeclared;
066   private Map<String, String> arrayTypesAlreadyDeclared = new HashMap<String, String>();
067   
068   public void onAttribute(MBeanAttributeInfo attributeMetadatathrows BuilderException  
069   {
070     try 
071     {
072   /*
073       <xs:element name='accountAttributes'>
074           <xs:complexType>
075            <xs:sequence>
076             <xs:element maxOccurs='unbounded' minOccurs='0' name='entry'>
077              <xs:complexType>
078               <xs:sequence>
079                <xs:element minOccurs='0' name='key' type='xs:string'/>
080                <xs:element minOccurs='0' name='value' type='xs:anyType'/>
081               </xs:sequence>
082              </xs:complexType>
083             </xs:element>
084            </xs:sequence>
085           </xs:complexType>
086          </xs:element>
087 */      
088       schema.appendChild(defineSchemaFor(attributeMetadata.getType(), attributeMetadata.getName()));        
089       Element wsrpProperties = (ElementXPathAPI.selectSingleNode(
090           _document, 
091           "/wsdl:definitions/wsdl:types/xsd:schema[" +
092           "@targetNamespace='http://amqp.apache.org/qpid/management/qman']" +
093           "/xsd:element[@name='QManWsResourceProperties']/xsd:complexType/xsd:sequence");
094 
095       Element propertyRef= XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
096       propertyRef.setAttribute(
097           "ref"
098           Names.PREFIX+":"+attributeMetadata.getName());
099       propertyRef.setAttribute("minOccurs""0");    
100       wsrpProperties.appendChild(propertyRef);
101       
102     catch(Exception exception)
103     {
104       throw new BuilderException(exception);
105     }
106   }
107   
108   private final static QName XSD_ELEMENT_QNAME = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI,"element","xsd");
109   private final static QName XSD_COMPLEX_TYPE_QNAME = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI,"complexType","xsd");
110   private final static QName XSD_SEQUENCE_QNAME = new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI,"sequence","xsd");
111   
112   @SuppressWarnings("unchecked")
113   private Element defineSchemaFor(String type, String attributeNamethrows Exception
114   {
115     if (type.equals("java.util.Map")) 
116     {
117       if (!mapTypeHasBeenDeclared)
118       {        
119           Element complexType = XmlUtils.createElement(_document, XSD_COMPLEX_TYPE_QNAME);
120           complexType.setAttribute("name","map");
121             Element sequence = XmlUtils.createElement(_document, XSD_SEQUENCE_QNAME);
122   
123             Element entry = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
124             entry.setAttribute("name""entry");
125             entry.setAttribute("minOccurs""0");
126             entry.setAttribute("maxOccurs""unbounded");
127   
128             Element complexType2 = XmlUtils.createElement(_document, XSD_COMPLEX_TYPE_QNAME);
129             Element sequence2 = XmlUtils.createElement(_document, XSD_SEQUENCE_QNAME);
130   
131               Element key = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
132               key.setAttribute("name""key");
133               key.setAttribute("type""xsd:string");
134   
135               Element value = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
136               value.setAttribute("name""value");
137               value.setAttribute("type""xsd:anyType");
138       
139               sequence2.appendChild(key);
140               sequence2.appendChild(value);
141               complexType2.appendChild(sequence2);
142               entry.appendChild(complexType2);
143               sequence.appendChild(entry);
144               complexType.appendChild(sequence);
145               schema.appendChild(complexType);      
146               mapTypeHasBeenDeclared = true;
147       
148       Element propertyDeclaration = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
149       propertyDeclaration.setAttribute("name",attributeName);
150       propertyDeclaration.setAttribute("type""qman:map");
151       return propertyDeclaration;
152       
153     else if ("java.util.UUID".equals(type)) 
154     {
155       if (!uuidTypeHasBeenDeclared)
156       {
157           Element complexType = XmlUtils.createElement(_document, XSD_COMPLEX_TYPE_QNAME);
158           complexType.setAttribute("name""uuid");
159             Element sequence = XmlUtils.createElement(_document, XSD_SEQUENCE_QNAME);
160               Element uuid = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
161               uuid.setAttribute("name""uuid");
162               uuid.setAttribute("type""xsd:string");            
163         sequence.appendChild(uuid);
164         complexType.appendChild(sequence);
165         schema.appendChild(complexType);
166         uuidTypeHasBeenDeclared = true;
167       }
168       Element propertyDeclaration = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
169       propertyDeclaration.setAttribute("name",attributeName);
170       propertyDeclaration.setAttribute("type""qman:uuid");
171       return propertyDeclaration;
172     else if (type.startsWith("["))
173     {
174       Class arrayClass =  Class.forName(type);
175       Class clazz = ReflectUtils.getClassFromArrayClass(arrayClass);
176       String arrayType = arrayClass.getSimpleName().replace("[]""").trim();
177       arrayType = Character.toUpperCase(arrayType.charAt(0))+arrayType.substring(1);
178       if (!arrayTypesAlreadyDeclared.containsKey(type))
179       {
180           Element complexType = XmlUtils.createElement(_document, XSD_COMPLEX_TYPE_QNAME);
181           complexType.setAttribute("name""arrayOf"+arrayType);
182             Element sequence = XmlUtils.createElement(_document, XSD_SEQUENCE_QNAME);
183               Element entry = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
184               entry.setAttribute("name""entry");
185               entry.setAttribute("type", serializer.getXmlType(clazz));            
186         sequence.appendChild(entry);
187         complexType.appendChild(sequence);
188         schema.appendChild(complexType);
189         arrayTypesAlreadyDeclared.put(type, arrayType);
190       }
191       Element propertyDeclaration = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
192       propertyDeclaration.setAttribute("name",attributeName);
193       propertyDeclaration.setAttribute("type""qman:arrayOf"+arrayTypesAlreadyDeclared.get(type));
194       return propertyDeclaration;
195     }
196     else {      
197       Element propertyDeclaration = XmlUtils.createElement(_document, XSD_ELEMENT_QNAME);
198       propertyDeclaration.setAttribute("name",attributeName);
199       propertyDeclaration.setAttribute("type", serializer.getXmlType(Class.forName(type)));
200         
201       return propertyDeclaration;
202     }      
203   }
204   
205   public void begin(ObjectName objectNamethrows BuilderException
206   {
207     this._objectName = objectName;
208     try 
209     {
210       Attr location = (AttrXPathAPI.selectSingleNode(
211           _document, 
212           "/wsdl:definitions/wsdl:service/wsdl:port/wsdl-soap:address/@location");
213           
214       StringBuilder builder = new StringBuilder("http://")
215         .append(InetAddress.getLocalHost().getHostName())
216         .append(':')
217         .append(System.getProperty(Names.ADAPTER_PORT_PROPERTY_NAME,"8080"))
218         .append('/')
219         .append("qman")
220         .append('/')
221         .append("services/QManWsResource");
222       location.setValue(builder.toString());
223     catch(Exception exception)
224     {
225       LOGGER.error(
226           exception,
227           Messages.QMAN_100026_SOAP_ADDRESS_REPLACEMENT_FAILURE);
228       throw new BuilderException(exception);
229     }
230     
231     try 
232     {
233       schema = (ElementXPathAPI.selectSingleNode(
234           _document.getDocumentElement(),
235         "/wsdl:definitions/wsdl:types/xsd:schema[@targetNamespace='http://amqp.apache.org/qpid/management/qman']");
236     catch(Exception exception)
237     {
238       LOGGER.error(
239           exception,
240           Messages.QMAN_100034_WSDL_SCHEMA_SECTION_NOT_FOUND);
241       throw new BuilderException(exception);
242     }
243 /*
244     <xs:complexType name='InvocationResult'>
245       <xs:sequence>
246         <xs:element name='statusCode' type="xsd:long" />
247         <xs:element name='statusText' type="xsd:string" />
248         <xs:element name='outputParameters'>
249               <xs:complexType>
250                  <xs:sequence>
251                  
252                     <xs:element maxOccurs='unbounded' minOccurs='0' name='entry'>
253                     
254                        <xs:complexType>
255                         <xs:sequence>
256                            <xs:element minOccurs='0' name="name' type='xs:string'/>
257                            <xs:element minOccurs='0' name="value" type='xs:anyType'/>
258                         </xs:sequence>
259                        </xs:complexType>
260                     </xs:element>
261                  </xs:sequence>
262               </xs:complexType>
263            </xs:element>
264     </xs:sequence>
265   </xs:complexType>
266 */
267     Element complexTypeResult = _document.createElement("xsd:complexType");
268     complexTypeResult.setAttribute("name""result");
269     Element sequence = _document.createElement("xsd:sequence");
270     complexTypeResult.appendChild(sequence);
271     
272     Element statusCode = _document.createElement("xsd:element");
273     statusCode.setAttribute("name""statusCode");
274     statusCode.setAttribute("type""xsd:long");
275 
276     Element statusText = _document.createElement("xsd:element");
277     statusText.setAttribute("name""statusText");
278     statusText.setAttribute("type""xsd:string");
279     
280     sequence.appendChild(statusCode);
281     sequence.appendChild(statusText);
282     
283     Element outputParams = _document.createElement("xsd:complexType");
284     outputParams.setAttribute("name""outputParameters");
285     sequence.appendChild(outputParams);    
286     
287     Element complexTypeOutput = _document.createElement("xsd:complexType");
288     Element outputSequence = _document.createElement("xsd:sequence");
289     
290     outputParams.appendChild(complexTypeOutput);
291     complexTypeOutput.appendChild(outputSequence);
292     
293     Element entry = _document.createElement("xsd:element");
294     entry.setAttribute("maxOccurs""unbounded");
295     entry.setAttribute("minOccurs""0");
296     entry.setAttribute("name""entry");
297     
298     outputSequence.appendChild(entry);
299     
300     Element entryComplexType = _document.createElement("xsd:complexType");
301     Element entrySequence = _document.createElement("xsd:sequence");
302     entryComplexType.appendChild(entrySequence);
303     entry.appendChild(entryComplexType);
304     
305     Element name = _document.createElement("xsd:name");
306     name.setAttribute("name""key");
307     name.setAttribute("type""xsd:string");
308     
309     Element value = _document.createElement("xsd:element");
310     value.setAttribute("name""value");
311     value.setAttribute("type""xsd:anyType");
312     
313     entrySequence.appendChild(name);
314     entrySequence.appendChild(value);    
315     
316     schema.appendChild(complexTypeResult);
317   }
318   
319   public void onOperation(MBeanOperationInfo operationMetadatathrows BuilderException
320   {
321     // SCHEMA SECTION
322     /*
323       <xs:element name='purgeRequest' type='qman:purgeRequest' />
324       
325       <xs:element name='purgeResponse' type='qman:purgeResponse' />
326       
327       <xs:complexType name='purgeRequest'>
328         <xs:sequence>
329           <xs:element name='arg0' type='xs:int' />
330           <xs:element minOccurs='0' name='arg1' type='qman:hashMap' />
331         </xs:sequence>
332       </xs:complexType>
333       
334       <xs:element name='hashMap'>
335             <xs:complexType>
336                <xs:sequence>
337                   <xs:element maxOccurs='unbounded' minOccurs='0' name='entry'>
338                      <xs:complexType>
339                       <xs:sequence>
340                          <xs:element minOccurs='0' name='key' type='xs:string'/>
341                          <xs:element minOccurs='0' name='value' type='xs:anyType'/>
342                       </xs:sequence>
343                      </xs:complexType>
344                   </xs:element>
345                </xs:sequence>
346             </xs:complexType>
347          </xs:element>
348       
349       <xs:complexType name='purgeResponse'>
350         <xs:sequence />
351       </xs:complexType>
352      */
353     try 
354     {
355       // <xs:element name='purgeRequest' type='qman:purgeRequest' />
356       // <xsd:element xmlns="" name="purgeRequest" type="qman:purgeRequest"/>
357       
358       Element methodRequestElement= _document.createElement("xsd:element");    
359       String methodNameRequest = operationMetadata.getName()+"Request";
360       methodRequestElement.setAttribute("name", methodNameRequest);
361       methodRequestElement.setAttribute("type""qman:"+methodNameRequest);
362       
363       // <xs:element name='purgeResponse' type='qman:purgeResponse' />
364       Element methodResponseElement= _document.createElement("xsd:element");    
365       String methodNameResponse= operationMetadata.getName()+"Response";
366       methodResponseElement.setAttribute("name", methodNameResponse);
367       methodResponseElement.setAttribute("type""qman:"+methodNameResponse);
368       
369       schema.appendChild(methodRequestElement);
370       schema.appendChild(methodResponseElement);
371   
372       /*
373         <xs:complexType name='purgeRequest'>
374           <xs:sequence>
375             <xs:element name='arg0' type='xs:int' />
376             <xs:element minOccurs='0' name='arg1' type='qman:hashMap' />
377           </xs:sequence>
378         </xs:complexType>
379   
380        */
381       
382       Element methodNameRequestComplexType =  _document.createElement("xsd:complexType");
383       methodNameRequestComplexType.setAttribute("name", methodNameRequest);
384       Element methodNameRequestComplexTypeSequence = _document.createElement("xsd:sequence");
385       
386       for(MBeanParameterInfo parameter : operationMetadata.getSignature())
387       {
388         methodNameRequestComplexTypeSequence.appendChild(defineSchemaFor(parameter.getType(), parameter.getName()));
389       }
390             
391       methodNameRequestComplexType.appendChild(methodNameRequestComplexTypeSequence);
392       schema.appendChild(methodNameRequestComplexType);
393       
394       Element methodNameResponseComplexType =  _document.createElement("xsd:complexType");
395       methodNameResponseComplexType.setAttribute("name", methodNameResponse);
396       
397       Element methodNameResponseSequence = _document.createElement("xsd:sequence");
398       methodNameResponseComplexType.appendChild(methodNameResponseSequence);
399       
400       Element result = _document.createElement("xsd:element");
401       result.setAttribute("name""result");
402       result.setAttribute("type""qman:result");
403       methodNameResponseSequence.appendChild(result);
404       
405       schema.appendChild(methodNameResponseComplexType);
406       
407       /*
408     <message name="purgeResponseMessage">
409       <part element='qman:purgeResponse' name='purgeResponse'></part>
410     </message>
411     
412     <message name='purgeRequestMessage'>
413       <part element="qman:purgeRequest" name='purgeRequest'></part>
414     </message>
415        */  
416       Element definitions = (ElementXPathAPI.selectSingleNode(_document, "/wsdl:definitions");
417       
418       String requestMessageName = methodNameRequest+"Message";
419       String responseMessageName = methodNameResponse+"Message";
420       
421       Element requestMessage = _document.createElement("message");
422       requestMessage.setAttribute("name", requestMessageName);
423       Element requestPart = _document.createElement("wsdl:part");
424       requestPart.setAttribute("element""qman:"+methodNameRequest);
425       requestPart.setAttribute("name", methodNameRequest);
426       requestMessage.appendChild(requestPart);
427       
428       Element responseMessage = _document.createElement("wsdl:message");
429       responseMessage.setAttribute("name", responseMessageName);
430       Element responsePart = _document.createElement("wsdl:part");
431       responsePart.setAttribute("element""qman:"+methodNameResponse);
432       responsePart.setAttribute("name", methodNameResponse);
433       responseMessage.appendChild(responsePart);
434       
435       definitions.appendChild(requestMessage);
436       definitions.appendChild(responseMessage);
437       
438       
439       /*
440   <operation name='purge'>
441       <input message="qman:purgeRequestMessage">
442       </input>
443       <output message='qman:purgeResponseMessage'>
444       </output>
445     </operation>     
446        */
447       Element portType = (ElementXPathAPI.selectSingleNode(_document, "/wsdl:definitions/wsdl:portType");
448       Element operation = _document.createElement("wsdl:operation");
449       operation.setAttribute("name", operationMetadata.getName());
450       
451       Element input = _document.createElement("wsdl:input");
452       input.setAttribute("message""qman:"+requestMessageName);
453       input.setAttribute("name", methodNameRequest);
454       input.setAttribute("wsa:action", Names.NAMESPACE_URI+"/"+operationMetadata.getName());
455       
456       //name="SetResourcePropertiesRequest" wsa:Action="http://docs.oasis-open.org/wsrf/rpw-2/SetResourceProperties/SetResourcePropertiesRequest"/>
457       
458       operation.appendChild(input);
459       
460       Element output = _document.createElement("wsdl:output");
461       output.setAttribute("message""qman:"+responseMessageName);
462       output.setAttribute("name", methodNameResponse);
463       output.setAttribute("wsa:action", Names.NAMESPACE_URI+"/"+methodNameResponse);
464       
465       operation.appendChild(output);    
466       
467       portType.appendChild(operation);
468       
469       /*
470       <operation name='purge'>
471         <soap:operation soapAction='purge' />
472         <input>
473           <soap:body use='literal' />
474         </input>
475         <output>
476           <soap:body use='literal' />
477         </output>
478       </operation>
479        */
480       Element binding = (ElementXPathAPI.selectSingleNode(_document, "/wsdl:definitions/wsdl:binding");
481       Element bindingOperation = _document.createElement("wsdl:operation");
482       bindingOperation.setAttribute("name", operationMetadata.getName());
483       
484       Element soapOperation = _document.createElement("wsdl-soap:operation");
485       soapOperation.setAttribute("soapAction", Names.NAMESPACE_URI+"/"+operationMetadata.getName());
486       
487       Element bindingInput = _document.createElement("wsdl:input");
488       Element bodyIn = _document.createElement("wsdl-soap:body");
489       bodyIn.setAttribute("use""literal");
490       
491       Element bindingOutput = _document.createElement("wsdl:output");
492       Element bodyOut = _document.createElement("wsdl-soap:body");
493       bodyOut.setAttribute("use""literal");
494       
495       bindingOutput.appendChild(bodyOut);
496       bindingInput.appendChild(bodyIn);
497       
498       bindingOperation.appendChild(soapOperation);
499       bindingOperation.appendChild(bindingInput);
500       bindingOperation.appendChild(bindingOutput);
501       
502       binding.appendChild(bindingOperation);
503     catch(Exception exception
504     {
505       throw new BuilderException(exception);
506     }
507   }
508 
509   public void endAttributes() 
510   {
511     // N.A.
512   }
513 
514   public void endOperations() 
515   {
516 
517   }
518 
519   public Document getWsdl() 
520   {
521     WsdlDebugger.debug(_objectName,_document);
522     return _document;
523   }
524 
525   public void setEnvironment(Environment environment
526   {
527     this._environment = environment;
528   }
529   
530   public void setWsdlPath(String wsdlPath)
531   {
532     _document = WsdlUtils.createWSDL(_environment, wsdlPath, true);
533   }
534 }