001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: QSUpdateServiceWSDLHandler.java 6070 2005-01-11 18:08:31Z sauthieg $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas.ws.axis;
025:
026: import java.io.File;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.io.PrintWriter;
030: import java.net.HttpURLConnection;
031: import java.net.URL;
032: import java.util.Iterator;
033: import java.util.List;
034:
035: import javax.naming.InitialContext;
036: import javax.naming.NamingException;
037: import javax.servlet.http.HttpServletRequest;
038: import javax.servlet.http.HttpServletResponse;
039: import javax.wsdl.Definition;
040: import javax.wsdl.Port;
041: import javax.wsdl.Service;
042: import javax.wsdl.WSDLException;
043: import javax.wsdl.extensions.ExtensibilityElement;
044: import javax.wsdl.extensions.soap.SOAPAddress;
045: import javax.wsdl.factory.WSDLFactory;
046: import javax.wsdl.xml.WSDLReader;
047: import javax.wsdl.xml.WSDLWriter;
048: import javax.xml.namespace.QName;
049: import javax.xml.parsers.DocumentBuilder;
050: import javax.xml.parsers.DocumentBuilderFactory;
051: import javax.xml.parsers.ParserConfigurationException;
052:
053: import org.w3c.dom.Attr;
054: import org.w3c.dom.Document;
055: import org.w3c.dom.Element;
056: import org.w3c.dom.NodeList;
057: import org.xml.sax.SAXException;
058:
059: import org.apache.axis.AxisFault;
060: import org.apache.axis.Constants;
061: import org.apache.axis.MessageContext;
062: import org.apache.axis.i18n.Messages;
063: import org.apache.axis.server.AxisServer;
064: import org.apache.axis.transport.http.AbstractQueryStringHandler;
065: import org.apache.axis.transport.http.HTTPConstants;
066: import org.apache.axis.utils.XMLUtils;
067:
068: import org.objectweb.jonas_ws.deployment.api.PortComponentDesc;
069: import org.objectweb.jonas_ws.deployment.api.ServiceDesc;
070:
071: import org.objectweb.jonas.ws.WSServiceException;
072:
073: /**
074: * Update the wsdlFile specified in wsdd with the good URLs for endpoints.
075: * responds to the ?JWSDL Supports wsdl:import and xsd:include
076: * @author Guillaume Sauthier
077: */
078: public class QSUpdateServiceWSDLHandler extends
079: AbstractQueryStringHandler {
080:
081: /**
082: * SOAP NS URI
083: */
084: private static final String NS_URI_SOAP = "http://schemas.xmlsoap.org/wsdl/soap/";
085:
086: /**
087: * soap:address Type QName
088: */
089: private static final QName QNAME_SOAP_ADDRESS = new QName(
090: NS_URI_SOAP, "address");
091:
092: /**
093: * WSDL NS URI
094: */
095: private static final String NS_URI_WSDL = "http://schemas.xmlsoap.org/wsdl/";
096:
097: /**
098: * XSD NS URI
099: */
100: private static final String NS_URI_XSD = "http://www.w3.org/2001/XMLSchema";
101:
102: /**
103: * filename parameter name
104: */
105: private static final String PARAM_FILENAME = "filename";
106:
107: /**
108: * context parameter name
109: */
110: private static final String PARAM_CONTEXT = "context";
111:
112: /**
113: * JWSDL parameter QSHandler name
114: */
115: private static final String PARAM_JWSDL = "JWSDL";
116:
117: /**
118: * wsdl:definition QName
119: */
120: private static final QName WSDL_DEFINITIONS_QNAME = new QName(
121: NS_URI_WSDL, "definitions");
122:
123: /**
124: * Service meta data
125: */
126: private ServiceDesc sd = null;
127:
128: /**
129: * Performs the action associated with this particular query string handler.
130: * @param msgContext a MessageContext object containing message context
131: * information for this query string handler.
132: * @throws AxisFault if an error occurs.
133: */
134: public void invoke(MessageContext msgContext) throws AxisFault {
135: // Obtain objects relevant to the task at hand from the provided
136: // MessageContext's bag.
137: configureFromContext(msgContext);
138:
139: AxisServer engine = (AxisServer) msgContext
140: .getProperty(HTTPConstants.PLUGIN_ENGINE);
141: PrintWriter writer = (PrintWriter) msgContext
142: .getProperty(HTTPConstants.PLUGIN_WRITER);
143: HttpServletResponse response = (HttpServletResponse) msgContext
144: .getProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE);
145: HttpServletRequest request = (HttpServletRequest) msgContext
146: .getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
147:
148: InitialContext ctx;
149: try {
150: ctx = new InitialContext();
151: sd = (ServiceDesc) ctx.lookup("java:comp/jonas/"
152: + engine.getName() + "/dd");
153: } catch (NamingException e) {
154: throw new AxisFault("Servlet name not found : "
155: + engine.getName(), e);
156: }
157:
158: String wsdlFilename = request.getParameter(PARAM_FILENAME);
159: String context = request.getParameter(PARAM_CONTEXT);
160: try {
161: Document doc = null;
162: if (wsdlFilename == null) {
163: // as a Document
164: doc = getDefinitionAsDocument(sd.getWSDL()
165: .getDefinition());
166: wsdlFilename = sd.getWSDL().getName();
167: String[] pathElements = wsdlFilename.split("/");
168: if (pathElements.length <= 2) {
169: throw new WSServiceException("invalid filename");
170: }
171:
172: StringBuffer buf = new StringBuffer();
173: for (int i = 2; i < pathElements.length; i++) {
174: buf.append(pathElements[i]);
175: if (i != (pathElements.length - 1)) {
176: // last part is a filename
177: buf.append("/");
178: }
179: }
180: // remove WEB-INF/wsdl/
181: // remove META-INF/wsdl/
182: wsdlFilename = buf.toString();
183: context = ".";
184: msgContext.setProperty("WSDL", doc);
185: } else {
186: // try to get the specified WSDL from cache ...
187: doc = (Document) msgContext.getProperty("WSDL_"
188: + wsdlFilename);
189:
190: if (doc == null) {
191: // create the WSDL/Imported file
192: doc = getDocument(wsdlFilename, context);
193: }
194:
195: msgContext.setProperty("WSDL_" + wsdlFilename, doc);
196: }
197:
198: if (doc != null) {
199: // update WSDL
200: modifyImports(doc, request, new File(context,
201: wsdlFilename).getParent());
202: Document up2date = updateWSDLPortLocations(doc);
203: response.setContentType("text/xml; charset="
204: + XMLUtils.getEncoding().toLowerCase());
205: reportWSDL(up2date, writer);
206: } else {
207: // report Error
208: if (log.isDebugEnabled()) {
209: log
210: .debug("processWsdlRequest: failed to create WSDL");
211: }
212: reportNoWSDL(response, writer, "noWSDL02", null);
213: }
214: } catch (AxisFault axisFault) {
215: //the no-service fault is mapped to a no-wsdl error
216: if (axisFault.getFaultCode().equals(
217: Constants.QNAME_NO_SERVICE_FAULT_CODE)) {
218: //which we log
219: processAxisFault(axisFault);
220:
221: //then report under a 404 error
222: response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
223: reportNoWSDL(response, writer, "noWSDL01", axisFault);
224: } else {
225: //all other faults get thrown
226: throw axisFault;
227: }
228: }
229: }
230:
231: /**
232: * @param definition Definition to convert
233: * @return returns the given Definition instance as a Document instance
234: * @throws AxisFault when convertion is not possible
235: */
236: private Document getDefinitionAsDocument(Definition definition)
237: throws AxisFault {
238:
239: try {
240: WSDLWriter writer = getWSDLWriter();
241: return writer.getDocument(definition);
242: } catch (WSDLException e) {
243: throw new AxisFault(e.getMessage(), e);
244: }
245:
246: }
247:
248: /**
249: * Update the wsdl:import and xsd:include elements of the given Document.
250: * @param doc Definitions or Schema document instance.
251: * @param request HTTP request
252: * @param context loading Context
253: */
254: private void modifyImports(Document doc,
255: HttpServletRequest request, String context) {
256: // Document may be a wsdl:definition or xsd:schema
257: // So we must handle import (definitions) and include (schema)
258:
259: // wsdl:definition / xsd:schema
260: Element de = doc.getDocumentElement();
261:
262: NodeList imports = de.getElementsByTagNameNS(NS_URI_WSDL,
263: "import");
264:
265: // modify wsdl:import location
266: for (int i = 0; i < imports.getLength(); i++) {
267: Element imp = (Element) imports.item(i);
268: Attr location = imp.getAttributeNode("location");
269: if (!location.getValue().startsWith("http://")) {
270: // relative import
271: String req = computeUpdatedURL(request, context,
272: location);
273:
274: log.debug("Replacing wsdl:location '"
275: + location.getValue() + "' with '"
276: + req.toString() + "'");
277: location.setValue(req.toString());
278: }
279: }
280:
281: // modify xsd:include schemaLocation
282: updateSchema(de, "include", request, context);
283: // modify xsd:import schemaLocation
284: updateSchema(de, "import", request, context);
285:
286: // wsdl:definitions/wsdl:types/xsd:schema/(xsd:import|xsd:include)
287: NodeList types = de
288: .getElementsByTagNameNS(NS_URI_WSDL, "types");
289: // is there a types here ?
290: if (types.getLength() != 0) {
291:
292: // get the only wsdl:types element
293: Element typesElement = (Element) types.item(0);
294:
295: // is there some xsd:schema out there ?
296: NodeList schemasList = typesElement.getElementsByTagNameNS(
297: NS_URI_XSD, "schema");
298: for (int i = 0; i < schemasList.getLength(); i++) {
299: Element schema = (Element) schemasList.item(i);
300: updateSchema(schema, "include", request, context);
301: updateSchema(schema, "import", request, context);
302: }
303: }
304: }
305:
306: /**
307: * @param request the Http Request
308: * @param context loading context
309: * @param location attribute to update
310: * @return return the new location value
311: */
312: private String computeUpdatedURL(HttpServletRequest request,
313: String context, Attr location) {
314: StringBuffer req = request.getRequestURL();
315: req.append("?" + PARAM_JWSDL);
316: req.append("&" + PARAM_FILENAME + "=" + location.getValue());
317: req.append("&" + PARAM_CONTEXT + "=" + context);
318: return req.toString();
319: }
320:
321: /**
322: * @param schema The Element representing a Schema to be updated
323: * @param elementName the element name with "schemaLocation" attribute (can be "import" or "include")
324: * @param request the HTTP Request
325: * @param context loading context
326: */
327: private void updateSchema(Element schema, String elementName,
328: HttpServletRequest request, String context) {
329:
330: NodeList elements = schema.getElementsByTagNameNS(NS_URI_XSD,
331: elementName);
332:
333: // modify xsd:include|xsd:import schemaLocation
334: for (int i = 0; i < elements.getLength(); i++) {
335: Element e = (Element) elements.item(i);
336: Attr location = e.getAttributeNode("schemaLocation");
337: if ((location != null)
338: && (!location.getValue().startsWith("http://"))) {
339: // relative import
340: String req = computeUpdatedURL(request, context,
341: location);
342:
343: log.debug("Replacing xsd:schemaLocation '"
344: + location.getValue() + "' with '"
345: + req.toString() + "'");
346: location.setValue(req.toString());
347: }
348: }
349: }
350:
351: /**
352: * @param wsdlFilename resource to load.
353: * @param context loading context
354: * @return Returns a Document created from the filename loaded in the
355: * context ClassLoader.
356: * @throws AxisFault if resource if not found or if resource is not XML.
357: */
358: private Document getDocument(String wsdlFilename, String context)
359: throws AxisFault {
360:
361: // Check that the filename is only relative to META-INF/wsdl or
362: // WEB-INF/wsdl no deeper !
363: // TODO Security Check !
364:
365: ClassLoader cl = Thread.currentThread().getContextClassLoader();
366: URL res = cl.getResource(context + "/" + wsdlFilename);
367:
368: Document doc = null;
369: if (res != null) {
370: try {
371: doc = createDocument(res.openStream());
372: } catch (IOException ioe) {
373: throw new AxisFault("Cannot open requested URL : "
374: + res);
375: }
376: } else {
377: throw new AxisFault("Cannot find requested document : "
378: + wsdlFilename);
379: }
380:
381: return doc;
382: }
383:
384: /**
385: * @param stream supposed XML InputStream
386: * @return Returns the Document parsed from the given InputStream
387: * @throws AxisFault if parsing goes wrong.
388: */
389: private Document createDocument(InputStream stream)
390: throws AxisFault {
391: try {
392: DocumentBuilderFactory factory = DocumentBuilderFactory
393: .newInstance();
394: factory.setNamespaceAware(true);
395: factory.setValidating(false);
396: DocumentBuilder builder = factory.newDocumentBuilder();
397: return builder.parse(stream);
398: } catch (ParserConfigurationException pce) {
399: throw new AxisFault(pce.getMessage(), pce);
400: } catch (SAXException se) {
401: throw new AxisFault(se.getMessage(), se);
402: } catch (IOException ioe) {
403: throw new AxisFault(ioe.getMessage(), ioe);
404: }
405: }
406:
407: /**
408: * @param doc Document to update
409: * @return updated Document
410: * @throws AxisFault When Document cannot be parsed as a WSDL Definition
411: * instance
412: */
413: private Document updateWSDLPortLocations(Document doc)
414: throws AxisFault {
415: log.debug("Entering updateWSDL");
416:
417: QName docQname = new QName(doc.getDocumentElement()
418: .getNamespaceURI(), doc.getDocumentElement()
419: .getLocalName());
420:
421: // If this is a wsdl:definition
422: if (WSDL_DEFINITIONS_QNAME.equals(docQname)) {
423: try {
424: WSDLReader reader = getWSDLReader();
425: // get Definition from Document
426: Definition def = reader.readWSDL(null, doc);
427:
428: /**
429: * 1. iterer sur le port-component pour prendre leur URL 2. les
430: * assigner en fonction du wsdl-port
431: */
432: QName sQName = sd.getWSDL().getServiceQname();
433: Service s = def.getService(sQName);
434: if (s != null) {
435:
436: List portsComp = sd.getPortComponents();
437: for (Iterator i = portsComp.iterator(); i.hasNext();) {
438: PortComponentDesc pcd = (PortComponentDesc) i
439: .next();
440: URL endpoint = pcd.getEndpointURL();
441: QName portQName = pcd.getQName();
442:
443: Port port = s.getPort(portQName.getLocalPart());
444: // maybe we have not found the requested Port
445: if (port != null) {
446: // Set the updated soap:address address
447: List ext = port.getExtensibilityElements();
448: for (Iterator it = ext.iterator(); it
449: .hasNext();) {
450: ExtensibilityElement element = (ExtensibilityElement) it
451: .next();
452: if (element.getElementType().equals(
453: QNAME_SOAP_ADDRESS)) {
454: SOAPAddress sa = (SOAPAddress) element;
455: sa.setLocationURI(endpoint
456: .toExternalForm());
457: log
458: .debug("Update port soap:location with "
459: + endpoint);
460: }
461: }
462: } else {
463: log.warn("Cannot find wsdl:port '"
464: + portQName.getLocalPart()
465: + "' in wsdl:service "
466: + s.getQName());
467: }
468: }
469: }
470:
471: return WSDLFactory.newInstance().newWSDLWriter()
472: .getDocument(def);
473: } catch (WSDLException wsdle) {
474: throw new AxisFault("Cannot read WSDL Document", wsdle);
475: }
476: } else {
477: // if we have something else (not a wsdl:definition)
478: // return the document unmodified
479: return doc;
480: }
481:
482: }
483:
484: /**
485: * @return Returns a configured WSDLReader
486: * @throws WSDLException if factory or reader cannot be instanciated.
487: */
488: private WSDLReader getWSDLReader() throws WSDLException {
489: WSDLFactory factory = WSDLFactory.newInstance();
490: WSDLReader reader = factory.newWSDLReader();
491: reader.setFeature("javax.wsdl.importDocuments", false);
492: return reader;
493: }
494:
495: /**
496: * @return Returns a configured WSDLWriter
497: * @throws WSDLException if factory or writer cannot be instanciated.
498: */
499: private WSDLWriter getWSDLWriter() throws WSDLException {
500: WSDLFactory factory = WSDLFactory.newInstance();
501: return factory.newWSDLWriter();
502: }
503:
504: /**
505: * Reports WSDL
506: * @param doc Document to write
507: * @param writer Servlet Writer to use
508: */
509: public void reportWSDL(Document doc, PrintWriter writer) {
510: XMLUtils.PrettyDocumentToWriter(doc, writer);
511: }
512:
513: /**
514: * Reports that we have no WSDL
515: * @param res HttpServletResponse
516: * @param writer PrintWriter
517: * @param moreDetailCode optional name of a message to provide more detail
518: * @param axisFault optional fault string, for extra info at debug time only
519: */
520: public void reportNoWSDL(HttpServletResponse res,
521: PrintWriter writer, String moreDetailCode,
522: AxisFault axisFault) {
523: res.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
524: res.setContentType("text/html");
525:
526: writer.println("<h2>" + Messages.getMessage("error00")
527: + "</h2>");
528: writer
529: .println("<p>" + Messages.getMessage("noWSDL00")
530: + "</p>");
531:
532: if (moreDetailCode != null) {
533: writer.println("<p>" + Messages.getMessage(moreDetailCode)
534: + "</p>");
535: }
536:
537: if (axisFault != null && isDevelopment()) {
538: //dev systems only give fault dumps
539: writeFault(writer, axisFault);
540: }
541: }
542:
543: }
|