001: /* ****************************************************************************
002: * SOAPDataSource.java
003: * ****************************************************************************/
004:
005: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
006: * Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
007: * Use is subject to license terms. *
008: * J_LZ_COPYRIGHT_END *********************************************************/
009:
010: package org.openlaszlo.data.json;
011:
012: import org.openlaszlo.data.*; // LoadCount belongs in utils
013: import org.openlaszlo.servlets.LoadCount;
014: import org.openlaszlo.media.MimeType;
015: import org.openlaszlo.remote.json.soap.LZSOAPService;
016: import org.openlaszlo.remote.json.soap.WSDLException;
017: import org.openlaszlo.remote.json.soap.WSDLParser;
018: import org.openlaszlo.remote.json.soap.encoding.SOAPDataEncoder;
019: import org.openlaszlo.server.LPS;
020: import org.openlaszlo.xml.internal.XMLUtils;
021: import java.io.ByteArrayInputStream;
022: import java.io.File;
023: import java.io.InputStream;
024: import java.io.InterruptedIOException;
025: import java.io.IOException;
026: import java.io.StringReader;
027: import java.net.URLDecoder;
028: import java.net.URLEncoder;
029: import java.rmi.RemoteException;
030: import java.util.Date;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Properties;
034: import java.util.Vector;
035: import javax.xml.parsers.DocumentBuilder;
036: import javax.xml.parsers.DocumentBuilderFactory;
037: import javax.xml.parsers.ParserConfigurationException;
038: import javax.xml.rpc.ServiceException;
039: import javax.servlet.http.HttpServletRequest;
040: import javax.servlet.http.HttpServletResponse;
041: import org.apache.axis.message.SOAPBodyElement;
042: import org.apache.axis.AxisFault;
043: import org.apache.log4j.Logger;
044: import org.xml.sax.InputSource;
045: import org.xml.sax.SAXException;
046: import org.w3c.dom.DOMException;
047: import org.w3c.dom.Element;
048: import org.w3c.dom.NodeList;
049: import org.w3c.dom.Text;
050: import org.apache.axis.AxisProperties;
051: import org.apache.axis.configuration.EngineConfigurationFactoryDefault;
052: import org.apache.axis.components.net.DefaultCommonsHTTPClientProperties;
053: import org.apache.axis.message.SOAPHeader;
054:
055: /**
056: * Data source for SOAP.
057: */
058: public class SOAPDataSource extends DataSource {
059:
060: public static Object[] soapvalue;
061: private static Logger mLogger = Logger
062: .getLogger(SOAPDataSource.class);
063:
064: /** Static document builder. */
065: static DocumentBuilder mBuilder = null;
066:
067: static String mLoadOption;
068:
069: static HashMap mServiceMap = new HashMap();
070:
071: //------------------------------------------------------------
072: // Begin soap info variables
073: //------------------------------------------------------------
074:
075: static long mLastCleared = -1;
076:
077: static LoadCount mInvokeLoad = new LoadCount(10);
078: static LoadCount mGetServiceLoad = new LoadCount(10);
079:
080: //------------------------------------------------------------
081: // End soap info variables
082: //------------------------------------------------------------
083:
084: {
085: DocumentBuilderFactory factory = DocumentBuilderFactory
086: .newInstance();
087: try {
088: mBuilder = factory.newDocumentBuilder();
089: } catch (ParserConfigurationException e) {
090: System.err.println(
091: /* (non-Javadoc)
092: * @i18n.test
093: * @org-mes="Can't create DocumentBuilder"
094: */
095: org.openlaszlo.i18n.LaszloMessages.getMessage(
096: SOAPDataSource.class.getName(), "051018-104"));
097: }
098:
099: mLoadOption = LPS.getProperty("rpc.soap.wsdlLoadOption",
100: "always");
101: if (mLogger.isDebugEnabled()) {
102: mLogger.debug(
103: /* (non-Javadoc)
104: * @i18n.test
105: * @org-mes="WSDL load option is set to \"" + p[0] + "\""
106: */
107: org.openlaszlo.i18n.LaszloMessages.getMessage(
108: SOAPDataSource.class.getName(), "051018-116",
109: new Object[] { mLoadOption }));
110: }
111: }
112:
113: public SOAPDataSource() {
114: clearLoadInfo();
115:
116: //------------------------------------------------------------
117: // AXIS CommonsHTTPClient properties
118: //------------------------------------------------------------
119: String clientConfigFile = LPS.getProperties().getProperty(
120: "axis.clientConfigFile",
121: LPS.getConfigDirectory() + File.separatorChar
122: + "client-config.wsdd");
123:
124: AxisProperties
125: .setProperty(
126: EngineConfigurationFactoryDefault.OPTION_CLIENT_CONFIG_FILE,
127: clientConfigFile);
128:
129: // Reuse HTTPDataSource HttpClient properties.
130: AxisProperties
131: .setProperty(
132: DefaultCommonsHTTPClientProperties.MAXIMUM_TOTAL_CONNECTIONS_PROPERTY_KEY,
133: "" + HTTPDataSource.getMaxTotalConnections());
134: AxisProperties
135: .setProperty(
136: DefaultCommonsHTTPClientProperties.MAXIMUM_CONNECTIONS_PER_HOST_PROPERTY_KEY,
137: "" + HTTPDataSource.getMaxConnectionsPerHost());
138: AxisProperties
139: .setProperty(
140: DefaultCommonsHTTPClientProperties.CONNECTION_POOL_TIMEOUT_KEY,
141: "" + HTTPDataSource.getConnectionPoolTimeout());
142: }
143:
144: /**
145: * @return unique name of this data source
146: */
147: public String name() {
148: return "soap";
149: }
150:
151: /**
152: * Sends system information to client.
153: *
154: * @throws DataSourceException if there was a problem retrieving or sending
155: * the data.
156: */
157: public Data getData(String app, HttpServletRequest req,
158: HttpServletResponse res, long lastModifiedTime)
159: throws InterruptedIOException, IOException,
160: DataSourceException {
161: if (mLogger.isDebugEnabled()) {
162: mLogger.debug("getData");
163: }
164:
165: String request = req.getParameter("request");
166: if (request == null || request.equals(""))
167: request = "invoke";
168:
169: int swfnum = LPS.getSWFVersionNum(req);
170:
171: long t0, t1;
172: t0 = System.currentTimeMillis();
173: if (request.equals("invoke")) {
174: mInvokeLoad.increment();
175: try {
176: return invoke(app, req, res, swfnum);
177: } finally {
178: t1 = System.currentTimeMillis();
179: mInvokeLoad.decrement((int) (t1 - t0));
180: }
181: } else if (request.equals("load")) {
182: mGetServiceLoad.increment();
183: try {
184: LZSOAPService service = getService(app, req, res, true);
185: return new SOAPData(service
186: .getClientSOAPService(swfnum));
187: } catch (ServiceException e) {
188: return exceptionToSOAPData(swfnum,
189: new DataSourceException(e.getMessage()));
190: } finally {
191: t1 = System.currentTimeMillis();
192: mGetServiceLoad.decrement((int) (t1 - t0));
193: }
194: }
195:
196: return exceptionToSOAPData(swfnum, new DataSourceException(
197: /* (non-Javadoc)
198: * @i18n.test
199: * @org-mes="unknown SOAP datasource request"
200: */
201: org.openlaszlo.i18n.LaszloMessages.getMessage(
202: SOAPDataSource.class.getName(), "051018-201")));
203: }
204:
205: public Data invoke(String app, HttpServletRequest req,
206: HttpServletResponse res, int swfnum) {
207:
208: if (!req.getMethod().equals("POST")) {
209: mLogger.error(
210: /* (non-Javadoc)
211: * @i18n.test
212: * @org-mes="request must be POST"
213: */
214: org.openlaszlo.i18n.LaszloMessages.getMessage(
215: SOAPDataSource.class.getName(), "051018-215"));
216: return exceptionToSOAPData(swfnum, new DataSourceException(
217: /* (non-Javadoc)
218: * @i18n.test
219: * @org-mes="request must be POST"
220: */
221: org.openlaszlo.i18n.LaszloMessages.getMessage(
222: SOAPDataSource.class.getName(), "051018-215")));
223: }
224:
225: String xml = req.getParameter("lzpostbody");
226: if (xml == null || xml.equals("")) {
227: mLogger.error(
228: /* (non-Javadoc)
229: * @i18n.test
230: * @org-mes="no post body"
231: */
232: org.openlaszlo.i18n.LaszloMessages.getMessage(
233: SOAPDataSource.class.getName(), "051018-236"));
234: return exceptionToSOAPData(swfnum, new DataSourceException(
235: /* (non-Javadoc)
236: * @i18n.test
237: * @org-mes="no post body"
238: */
239: org.openlaszlo.i18n.LaszloMessages.getMessage(
240: SOAPDataSource.class.getName(), "051018-236")));
241: }
242:
243: if (mLogger.isDebugEnabled()) {
244: mLogger.debug("lzpostbody: " + xml);
245: }
246:
247: try {
248:
249: LZSOAPService service = getService(app, req, res, false);
250: if (service == null) {
251: return exceptionToSOAPData(swfnum,
252: new DataSourceException(
253: /* (non-Javadoc)
254: * @i18n.test
255: * @org-mes="could not find service"
256: */
257: org.openlaszlo.i18n.LaszloMessages.getMessage(
258: SOAPDataSource.class.getName(),
259: "051018-264")));
260: }
261:
262: try {
263: String operation = req.getParameter("operation");
264: Object[] value = service.invoke(operation, xml);
265: soapvalue = value;
266:
267: if (value[0].equals("rpc")) {
268: return programToSOAPData(swfnum, value[1],
269: (SOAPHeader) value[2]);
270: } else {
271: return documentsToSOAPData(swfnum,
272: (Vector) value[1], (SOAPHeader) value[2]);
273: }
274: } catch (AxisFault fault) {
275: // Axis throws exceptions for faults
276: return new SOAPData(new SOAPDataEncoder()
277: .buildFromFault(fault));
278: }
279:
280: } catch (RemoteException e) {
281: mLogger.error("RemoteException: " + e.getMessage(), e);
282: return exceptionToSOAPData(swfnum, e);
283: } catch (DataSourceException e) {
284: mLogger.error("DataSourceException: " + e.getMessage(), e);
285: return exceptionToSOAPData(swfnum, e);
286: } catch (IOException e) {
287: mLogger.error("IOException: " + e.getMessage(), e);
288: return exceptionToSOAPData(swfnum, e);
289: } catch (ServiceException e) {
290: mLogger.error("ServiceException: " + e.getMessage(), e);
291: return exceptionToSOAPData(swfnum, e);
292: } catch (Exception e) {
293: mLogger.error("Exception: " + e.getMessage(), e);
294: return exceptionToSOAPData(swfnum, e);
295: }
296:
297: }
298:
299: SOAPData exceptionToSOAPData(int swfnum, Exception e) {
300: mLogger.error("Exception", e);
301: return new SOAPData(new SOAPDataEncoder().buildFromException(e));
302: }
303:
304: SOAPData documentsToSOAPData(int swfnum, Vector docs,
305: SOAPHeader header) throws DataSourceException {
306:
307: if (mLogger.isDebugEnabled()) {
308: mLogger.debug(
309: /* (non-Javadoc)
310: * @i18n.test
311: * @org-mes="documentsToSOAPData"
312: */
313: org.openlaszlo.i18n.LaszloMessages.getMessage(
314: SOAPDataSource.class.getName(), "051018-323"));
315: }
316:
317: if (docs.size() == 0) {
318: mLogger.error(
319: /* (non-Javadoc)
320: * @i18n.test
321: * @org-mes="no document return"
322: */
323: org.openlaszlo.i18n.LaszloMessages.getMessage(
324: SOAPDataSource.class.getName(), "051018-334"));
325: throw new DataSourceException(
326: /* (non-Javadoc)
327: * @i18n.test
328: * @org-mes="no document return"
329: */
330: org.openlaszlo.i18n.LaszloMessages.getMessage(
331: SOAPDataSource.class.getName(), "051018-334"));
332: }
333:
334: try {
335: return new SOAPData(new SOAPDataEncoder(docs, header));
336: } catch (Exception e) {
337: mLogger.error(
338: /* (non-Javadoc)
339: * @i18n.test
340: * @org-mes="problems getting SOAP document element"
341: */
342: org.openlaszlo.i18n.LaszloMessages.getMessage(
343: SOAPDataSource.class.getName(), "051018-355"), e);
344: throw new DataSourceException(
345: /* (non-Javadoc)
346: * @i18n.test
347: * @org-mes="problems getting SOAP document element"
348: */
349: org.openlaszlo.i18n.LaszloMessages.getMessage(
350: SOAPDataSource.class.getName(), "051018-355"));
351: }
352: }
353:
354: /**
355: * Creates key for services map.
356: *
357: * @param wsdl WSDL URL.
358: * @param service SOAP service name.
359: * @param port SOAP port name.
360: * @return key to use for services map.
361: */
362: public String key(String wsdl, String service, String port) {
363: return wsdl + " " + service + " " + port;
364: }
365:
366: /**
367: * Get SOAP service.
368: *
369: * @param req
370: * @param res
371: * @exception DataSourceException
372: */
373: synchronized public LZSOAPService getService(String app,
374: HttpServletRequest req, HttpServletResponse res,
375: boolean checkLoadOption) throws DataSourceException {
376:
377: String wsdl = req.getParameter("wsdl");
378: String service = req.getParameter("service");
379: String port = req.getParameter("port");
380:
381: if (wsdl == null || wsdl.equals("")) {
382: throw new DataSourceException(
383: /* (non-Javadoc)
384: * @i18n.test
385: * @org-mes="no wsdl specified"
386: */
387: org.openlaszlo.i18n.LaszloMessages.getMessage(
388: SOAPDataSource.class.getName(), "051018-408"));
389: }
390:
391: LZSOAPService soapService = null;
392: if (checkLoadOption) {
393: if ("never" == mLoadOption.intern()) {
394: soapService = (LZSOAPService) mServiceMap.get(key(wsdl,
395: service, port));
396: }
397: } else {
398: soapService = (LZSOAPService) mServiceMap.get(key(wsdl,
399: service, port));
400: }
401: if (soapService != null)
402: return soapService;
403:
404: return fetchService(app, req, res);
405: }
406:
407: /**
408: * Fetch SOAP service over HTTP.
409: */
410: synchronized public LZSOAPService fetchService(String app,
411: HttpServletRequest req, HttpServletResponse res)
412: throws DataSourceException {
413:
414: String wsdl = req.getParameter("wsdl");
415: String service = req.getParameter("service");
416: String port = req.getParameter("port");
417:
418: // TODO: [2007-06-24 pkang] cache WSDLs.
419: try {
420: Data data;
421: String wsdlurl = getURL(req, wsdl);
422: if (wsdlurl.startsWith("file:")) {
423: data = FileDataSource.getFileData(app, req, res,
424: wsdlurl, -1);
425: } else if (wsdlurl.startsWith("http://")
426: || wsdlurl.startsWith("https://")) {
427: data = HTTPDataSource
428: .getHTTPData(req, res, wsdlurl, -1);
429: } else {
430: throw new DataSourceException(
431: /* (non-Javadoc)
432: * @i18n.test
433: * @org-mes="unsupported WSDL protocol: " + p[0]
434: */
435: org.openlaszlo.i18n.LaszloMessages.getMessage(
436: SOAPDataSource.class.getName(), "051018-453",
437: new Object[] { wsdlurl }));
438: }
439: String _wsdl = data.getAsString();
440: LZSOAPService soapService = WSDLParser.parse(wsdl,
441: new InputSource(new StringReader(_wsdl)), service,
442: port);
443:
444: if (soapService == null) {
445: throw new DataSourceException(
446: /* (non-Javadoc)
447: * @i18n.test
448: * @org-mes="could not find SOAP service (" + p[0] + ", " + p[1] + ", " + p[2] + ")"
449: */
450: org.openlaszlo.i18n.LaszloMessages.getMessage(
451: SOAPDataSource.class.getName(), "051018-468",
452: new Object[] { wsdl, service, port }));
453: }
454:
455: mServiceMap.put(key(wsdl, soapService.getServiceName(),
456: soapService.getPort()), soapService);
457:
458: // secondary mapping to the same service
459: if (service == null || port == null) {
460: mServiceMap.put(key(wsdl, service, port), soapService);
461: }
462:
463: return soapService;
464:
465: } catch (IOException e) {
466: mLogger.error("Exception", e);
467: throw new DataSourceException(e.getMessage());
468: } catch (WSDLException e) {
469: mLogger.error("Exception", e);
470: throw new DataSourceException(e.getMessage());
471: } catch (ServiceException e) {
472: mLogger.error("Exception", e);
473: throw new DataSourceException(e.getMessage());
474: } catch (Exception e) {
475: mLogger.error("Exception", e);
476: throw new DataSourceException(e.getMessage());
477: }
478: }
479:
480: SOAPData programToSOAPData(int swfnum, Object program,
481: SOAPHeader header) throws DataSourceException {
482:
483: if (mLogger.isDebugEnabled()) {
484: mLogger.debug(
485: /* (non-Javadoc)
486: * @i18n.test
487: * @org-mes="programToSOAPData"
488: */
489: org.openlaszlo.i18n.LaszloMessages.getMessage(
490: SOAPDataSource.class.getName(), "051018-508"));
491: }
492:
493: try {
494: return new SOAPData(new SOAPDataEncoder(program, header));
495: } catch (Exception e) {
496: mLogger.error(
497: /* (non-Javadoc)
498: * @i18n.test
499: * @org-mes="problems creating RPC program"
500: */
501: org.openlaszlo.i18n.LaszloMessages.getMessage(
502: SOAPDataSource.class.getName(), "051018-521"), e);
503: throw new DataSourceException(
504: /* (non-Javadoc)
505: * @i18n.test
506: * @org-mes="problems creating RPC program"
507: */
508: org.openlaszlo.i18n.LaszloMessages.getMessage(
509: SOAPDataSource.class.getName(), "051018-521"));
510: }
511: }
512:
513: /**
514: * @param serviceKey the key name to the LZSOAPService
515: */
516: synchronized public static void serviceXML(StringBuffer sb,
517: String serviceKey) {
518: LZSOAPService service = (LZSOAPService) mServiceMap
519: .get(serviceKey);
520: if (service != null) {
521: service.toXML(sb);
522: } else {
523: sb.append("<error>").append(serviceKey).append(
524: " service not found.").append("</error>");
525: }
526: }
527:
528: synchronized public static void listServicesXML(StringBuffer sb) {
529: sb.append("<services count=\"").append(mServiceMap.size())
530: .append("\">");
531: Iterator iter = mServiceMap.keySet().iterator();
532: while (iter.hasNext()) {
533: String k = (String) iter.next();
534: sb.append("<service").append(" name=\"").append(
535: XMLUtils.escapeXml(k)).append("\"").append(
536: " urlname=\"").append(URLEncoder.encode(k)).append(
537: "\"").append("/>");
538: }
539: sb.append("</services>");
540: }
541:
542: synchronized static public void toXML(StringBuffer sb) {
543: Date lc = new Date(mLastCleared);
544: sb.append("<soapinfo ").append(" last-cleared=\"").append(lc)
545: .append("\"").append(">");
546: {
547: sb.append(mInvokeLoad.toXML("invoke_load"));
548: sb.append(mGetServiceLoad.toXML("get_service_load"));
549: listServicesXML(sb);
550: }
551: sb.append("</soapinfo>");
552: }
553:
554: public static void clearLoadInfo() {
555: mInvokeLoad.reset();
556: mGetServiceLoad.reset();
557: mLastCleared = System.currentTimeMillis();
558: }
559:
560: /**
561: * A data object to hold session object.
562: */
563: public class SOAPData extends Data {
564: byte[] mObject = null;
565: SOAPDataEncoder mSOAPDataEncoder = null;
566:
567: public SOAPData(byte[] object) {
568: mObject = object;
569: }
570:
571: public SOAPData(SOAPDataEncoder de) {
572: mSOAPDataEncoder = de;
573: }
574:
575: public String getMimeType() {
576: return MimeType.SWF;
577: }
578:
579: public InputStream getInputStream() throws IOException {
580: if (mObject != null) {
581: return new ByteArrayInputStream(mObject);
582: } else if (mSOAPDataEncoder != null) {
583: return mSOAPDataEncoder.getInputStream();
584: }
585:
586: throw new IOException("no input stream");
587: }
588:
589: public long size() {
590: if (mObject != null) {
591: return mObject.length;
592: } else if (mSOAPDataEncoder != null) {
593: return mSOAPDataEncoder.getSize();
594: }
595:
596: return 0;
597: }
598: }
599: }
|