001: /*
002: * $Id: HttpSOAPConnection.java,v 1.41 2006/01/27 12:49:17 vj135062 Exp $
003: * $Revision: 1.41 $
004: * $Date: 2006/01/27 12:49:17 $
005: */
006:
007: /*
008: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
009: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
010: *
011: * This code is free software; you can redistribute it and/or modify it
012: * under the terms of the GNU General Public License version 2 only, as
013: * published by the Free Software Foundation. Sun designates this
014: * particular file as subject to the "Classpath" exception as provided
015: * by Sun in the LICENSE file that accompanied this code.
016: *
017: * This code is distributed in the hope that it will be useful, but WITHOUT
018: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
019: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
020: * version 2 for more details (a copy is included in the LICENSE file that
021: * accompanied this code).
022: *
023: * You should have received a copy of the GNU General Public License version
024: * 2 along with this work; if not, write to the Free Software Foundation,
025: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
026: *
027: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
028: * CA 95054 USA or visit www.sun.com if you need additional information or
029: * have any questions.
030: */
031: package com.sun.xml.internal.messaging.saaj.client.p2p;
032:
033: import java.io.*;
034: import java.lang.reflect.Method;
035: import java.net.*;
036: import java.security.*;
037: import java.util.Iterator;
038: import java.util.StringTokenizer;
039: import java.util.logging.Level;
040: import java.util.logging.Logger;
041:
042: import javax.xml.soap.*;
043:
044: import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
045: import com.sun.xml.internal.messaging.saaj.util.*;
046:
047: /**
048: * This represents a "connection" to the simple HTTP-based provider.
049: *
050: * @author Anil Vijendran (akv@eng.sun.com)
051: * @author Rajiv Mordani (rajiv.mordani@sun.com)
052: * @author Manveen Kaur (manveen.kaur@sun.com)
053: *
054: */
055: public class HttpSOAPConnection extends SOAPConnection {
056:
057: protected static Logger log = Logger
058: .getLogger(LogDomainConstants.HTTP_CONN_DOMAIN,
059: "com.sun.xml.internal.messaging.saaj.client.p2p.LocalStrings");
060:
061: public static String defaultProxyHost = null;
062: public static int defaultProxyPort = -1;
063:
064: MessageFactory messageFactory = null;
065:
066: boolean closed = false;
067:
068: public HttpSOAPConnection() throws SOAPException {
069: proxyHost = defaultProxyHost;
070: proxyPort = defaultProxyPort;
071:
072: try {
073: messageFactory = MessageFactory
074: .newInstance(SOAPConstants.DYNAMIC_SOAP_PROTOCOL);
075: } catch (Exception ex) {
076: log.log(Level.SEVERE,
077: "SAAJ0001.p2p.cannot.create.msg.factory", ex);
078: throw new SOAPExceptionImpl(
079: "Unable to create message factory", ex);
080: }
081: }
082:
083: public void close() throws SOAPException {
084: if (closed) {
085: log.severe("SAAJ0002.p2p.close.already.closed.conn");
086: throw new SOAPExceptionImpl("Connection already closed");
087: }
088:
089: messageFactory = null;
090: closed = true;
091: }
092:
093: public SOAPMessage call(SOAPMessage message, Object endPoint)
094: throws SOAPException {
095: if (closed) {
096: log.severe("SAAJ0003.p2p.call.already.closed.conn");
097: throw new SOAPExceptionImpl("Connection is closed");
098: }
099:
100: Class urlEndpointClass = null;
101:
102: try {
103: urlEndpointClass = Class
104: .forName("javax.xml.messaging.URLEndpoint");
105: } catch (Exception ex) {
106: //Do nothing. URLEndpoint is available only when JAXM is there.
107: log.finest("SAAJ0090.p2p.endpoint.available.only.for.JAXM");
108: }
109:
110: if (urlEndpointClass != null) {
111: if (urlEndpointClass.isInstance(endPoint)) {
112: String url = null;
113:
114: try {
115: Method m = urlEndpointClass.getMethod("getURL",
116: (Class[]) null);
117: url = (String) m.invoke(endPoint, (Object[]) null);
118: } catch (Exception ex) {
119: // TBD -- exception chaining
120: log.log(Level.SEVERE, "SAAJ0004.p2p.internal.err",
121: ex);
122: throw new SOAPExceptionImpl("Internal error: "
123: + ex.getMessage());
124: }
125: try {
126: endPoint = new URL(url);
127: } catch (MalformedURLException mex) {
128: log.log(Level.SEVERE, "SAAJ0005.p2p.", mex);
129: throw new SOAPExceptionImpl("Bad URL: "
130: + mex.getMessage());
131: }
132: }
133: }
134:
135: if (endPoint instanceof java.lang.String) {
136: try {
137: endPoint = new URL((String) endPoint);
138: } catch (MalformedURLException mex) {
139: log.log(Level.SEVERE, "SAAJ0006.p2p.bad.URL", mex);
140: throw new SOAPExceptionImpl("Bad URL: "
141: + mex.getMessage());
142: }
143: }
144:
145: if (endPoint instanceof URL)
146: try {
147: PriviledgedPost pp = new PriviledgedPost(this , message,
148: (URL) endPoint);
149: SOAPMessage response = (SOAPMessage) AccessController
150: .doPrivileged(pp);
151:
152: return response;
153: } catch (Exception ex) {
154: // TBD -- chaining?
155: throw new SOAPExceptionImpl(ex);
156: }
157: else {
158: log.severe("SAAJ0007.p2p.bad.endPoint.type");
159: throw new SOAPExceptionImpl("Bad endPoint type " + endPoint);
160: }
161: }
162:
163: static class PriviledgedPost implements PrivilegedExceptionAction {
164:
165: HttpSOAPConnection c;
166: SOAPMessage message;
167: URL endPoint;
168:
169: PriviledgedPost(HttpSOAPConnection c, SOAPMessage message,
170: URL endPoint) {
171: this .c = c;
172: this .message = message;
173: this .endPoint = endPoint;
174: }
175:
176: public Object run() throws Exception {
177: return c.post(message, endPoint);
178: }
179: }
180:
181: // TBD
182: // Fix this to do things better.
183:
184: private String proxyHost = null;
185:
186: static class PriviledgedSetProxyAction implements
187: PrivilegedExceptionAction {
188:
189: String proxyHost = null;
190: int proxyPort = 0;
191:
192: PriviledgedSetProxyAction(String host, int port) {
193: this .proxyHost = host;
194: this .proxyPort = port;
195: }
196:
197: public Object run() throws Exception {
198: System.setProperty("http.proxyHost", proxyHost);
199: System.setProperty("http.proxyPort", new Integer(proxyPort)
200: .toString());
201: log.log(Level.FINE, "SAAJ0050.p2p.proxy.host",
202: new String[] { proxyHost });
203: log.log(Level.FINE, "SAAJ0051.p2p.proxy.port",
204: new String[] { new Integer(proxyPort).toString() });
205: return proxyHost;
206: }
207: }
208:
209: public void setProxy(String host, int port) {
210: try {
211: proxyPort = port;
212: PriviledgedSetProxyAction ps = new PriviledgedSetProxyAction(
213: host, port);
214: proxyHost = (String) AccessController.doPrivileged(ps);
215: } catch (Exception e) {
216: throw new RuntimeException(e);
217: }
218: }
219:
220: public String getProxyHost() {
221: return proxyHost;
222: }
223:
224: private int proxyPort = -1;
225:
226: public int getProxyPort() {
227: return proxyPort;
228: }
229:
230: SOAPMessage post(SOAPMessage message, URL endPoint)
231: throws SOAPException {
232: boolean isFailure = false;
233:
234: URL url = null;
235: HttpURLConnection httpConnection = null;
236:
237: int responseCode = 0;
238: try {
239: if (endPoint.getProtocol().equals("https"))
240: //if(!setHttps)
241: initHttps();
242: // Process the URL
243: JaxmURI uri = new JaxmURI(endPoint.toString());
244: String userInfo = uri.getUserinfo();
245:
246: url = endPoint;
247:
248: if (dL > 0)
249: d("uri: " + userInfo + " " + url + " " + uri);
250:
251: // TBD
252: // Will deal with https later.
253: if (!url.getProtocol().equalsIgnoreCase("http")
254: && !url.getProtocol().equalsIgnoreCase("https")) {
255: log
256: .severe("SAAJ0052.p2p.protocol.mustbe.http.or.https");
257: throw new IllegalArgumentException("Protocol "
258: + url.getProtocol() + " not supported in URL "
259: + url);
260: }
261: httpConnection = (HttpURLConnection) createConnection(url);
262:
263: httpConnection.setRequestMethod("POST");
264:
265: httpConnection.setDoOutput(true);
266: httpConnection.setDoInput(true);
267: httpConnection.setUseCaches(false);
268: HttpURLConnection.setFollowRedirects(true);
269:
270: if (message.saveRequired())
271: message.saveChanges();
272:
273: MimeHeaders headers = message.getMimeHeaders();
274:
275: Iterator it = headers.getAllHeaders();
276: boolean hasAuth = false; // true if we find explicit Auth header
277: while (it.hasNext()) {
278: MimeHeader header = (MimeHeader) it.next();
279:
280: String[] values = headers.getHeader(header.getName());
281:
282: if (values.length == 1)
283: httpConnection.setRequestProperty(header.getName(),
284: header.getValue());
285: else {
286: StringBuffer concat = new StringBuffer();
287: int i = 0;
288: while (i < values.length) {
289: if (i != 0)
290: concat.append(',');
291: concat.append(values[i]);
292: i++;
293: }
294:
295: httpConnection.setRequestProperty(header.getName(),
296: concat.toString());
297: }
298:
299: if ("Authorization".equals(header.getName())) {
300: hasAuth = true;
301: log.fine("SAAJ0091.p2p.https.auth.in.POST.true");
302: }
303: }
304:
305: if (!hasAuth && userInfo != null) {
306: initAuthUserInfo(httpConnection, userInfo);
307: }
308:
309: OutputStream out = httpConnection.getOutputStream();
310: message.writeTo(out);
311:
312: out.flush();
313: out.close();
314:
315: httpConnection.connect();
316:
317: try {
318:
319: responseCode = httpConnection.getResponseCode();
320:
321: // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults
322: if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
323: isFailure = true;
324: }
325: //else if (responseCode != HttpURLConnection.HTTP_OK)
326: //else if (!(responseCode >= HttpURLConnection.HTTP_OK && responseCode < 207))
327: else if ((responseCode / 100) != 2) {
328: log.log(Level.SEVERE, "SAAJ0008.p2p.bad.response",
329: new String[] { httpConnection
330: .getResponseMessage() });
331: throw new SOAPExceptionImpl("Bad response: ("
332: + responseCode
333: + httpConnection.getResponseMessage());
334:
335: }
336: } catch (IOException e) {
337: // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds!
338: responseCode = httpConnection.getResponseCode();
339: if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
340: isFailure = true;
341: } else {
342: throw e;
343: }
344:
345: }
346:
347: } catch (SOAPException ex) {
348: throw ex;
349: } catch (Exception ex) {
350: log.severe("SAAJ0009.p2p.msg.send.failed");
351: throw new SOAPExceptionImpl("Message send failed", ex);
352: }
353:
354: SOAPMessage response = null;
355: if (responseCode == HttpURLConnection.HTTP_OK || isFailure) {
356: try {
357: MimeHeaders headers = new MimeHeaders();
358:
359: String key, value;
360:
361: // Header field 0 is the status line so we skip it.
362:
363: int i = 1;
364:
365: while (true) {
366: key = httpConnection.getHeaderFieldKey(i);
367: value = httpConnection.getHeaderField(i);
368:
369: if (key == null && value == null)
370: break;
371:
372: if (key != null) {
373: StringTokenizer values = new StringTokenizer(
374: value, ",");
375: while (values.hasMoreTokens())
376: headers.addHeader(key, values.nextToken()
377: .trim());
378: }
379: i++;
380: }
381:
382: InputStream httpIn = (isFailure ? httpConnection
383: .getErrorStream() : httpConnection
384: .getInputStream());
385:
386: byte[] bytes = readFully(httpIn);
387:
388: int length = httpConnection.getContentLength() == -1 ? bytes.length
389: : httpConnection.getContentLength();
390:
391: // If no reply message is returned,
392: // content-Length header field value is expected to be zero.
393: if (length == 0) {
394: response = null;
395: log.warning("SAAJ0014.p2p.content.zero");
396: } else {
397: ByteInputStream in = new ByteInputStream(bytes,
398: length);
399: response = messageFactory
400: .createMessage(headers, in);
401: }
402:
403: httpIn.close();
404: httpConnection.disconnect();
405:
406: } catch (SOAPException ex) {
407: throw ex;
408: } catch (Exception ex) {
409: log.log(Level.SEVERE, "SAAJ0010.p2p.cannot.read.resp",
410: ex);
411: throw new SOAPExceptionImpl("Unable to read response: "
412: + ex.getMessage());
413: }
414: }
415: return response;
416: }
417:
418: // Object identifies where the request should be sent.
419: // It is required to support objects of type String and java.net.URL.
420:
421: public SOAPMessage get(Object endPoint) throws SOAPException {
422: if (closed) {
423: log.severe("SAAJ0011.p2p.get.already.closed.conn");
424: throw new SOAPExceptionImpl("Connection is closed");
425: }
426: Class urlEndpointClass = null;
427:
428: try {
429: urlEndpointClass = Class
430: .forName("javax.xml.messaging.URLEndpoint");
431: } catch (Exception ex) {
432: //Do nothing. URLEndpoint is available only when JAXM is there.
433: }
434:
435: if (urlEndpointClass != null) {
436: if (urlEndpointClass.isInstance(endPoint)) {
437: String url = null;
438:
439: try {
440: Method m = urlEndpointClass.getMethod("getURL",
441: (Class[]) null);
442: url = (String) m.invoke(endPoint, (Object[]) null);
443: } catch (Exception ex) {
444: log.severe("SAAJ0004.p2p.internal.err");
445: throw new SOAPExceptionImpl("Internal error: "
446: + ex.getMessage());
447: }
448: try {
449: endPoint = new URL(url);
450: } catch (MalformedURLException mex) {
451: log.severe("SAAJ0005.p2p.");
452: throw new SOAPExceptionImpl("Bad URL: "
453: + mex.getMessage());
454: }
455: }
456: }
457:
458: if (endPoint instanceof java.lang.String) {
459: try {
460: endPoint = new URL((String) endPoint);
461: } catch (MalformedURLException mex) {
462: log.severe("SAAJ0006.p2p.bad.URL");
463: throw new SOAPExceptionImpl("Bad URL: "
464: + mex.getMessage());
465: }
466: }
467:
468: if (endPoint instanceof URL)
469: try {
470: PriviledgedGet pg = new PriviledgedGet(this ,
471: (URL) endPoint);
472: SOAPMessage response = (SOAPMessage) AccessController
473: .doPrivileged(pg);
474:
475: return response;
476: } catch (Exception ex) {
477: throw new SOAPExceptionImpl(ex);
478: }
479: else
480: throw new SOAPExceptionImpl("Bad endPoint type " + endPoint);
481: }
482:
483: static class PriviledgedGet implements PrivilegedExceptionAction {
484:
485: HttpSOAPConnection c;
486: URL endPoint;
487:
488: PriviledgedGet(HttpSOAPConnection c, URL endPoint) {
489: this .c = c;
490: this .endPoint = endPoint;
491: }
492:
493: public Object run() throws Exception {
494: return c.get(endPoint);
495: }
496: }
497:
498: SOAPMessage get(URL endPoint) throws SOAPException {
499: boolean isFailure = false;
500:
501: URL url = null;
502: HttpURLConnection httpConnection = null;
503:
504: int responseCode = 0;
505: try {
506: /// Is https GET allowed??
507: if (endPoint.getProtocol().equals("https"))
508: initHttps();
509: // Process the URL
510: JaxmURI uri = new JaxmURI(endPoint.toString());
511: String userInfo = uri.getUserinfo();
512:
513: url = endPoint;
514:
515: if (dL > 0)
516: d("uri: " + userInfo + " " + url + " " + uri);
517:
518: // TBD
519: // Will deal with https later.
520: if (!url.getProtocol().equalsIgnoreCase("http")
521: && !url.getProtocol().equalsIgnoreCase("https")) {
522: log
523: .severe("SAAJ0052.p2p.protocol.mustbe.http.or.https");
524: throw new IllegalArgumentException("Protocol "
525: + url.getProtocol() + " not supported in URL "
526: + url);
527: }
528: httpConnection = (HttpURLConnection) createConnection(url);
529:
530: httpConnection.setRequestMethod("GET");
531:
532: httpConnection.setDoOutput(true);
533: httpConnection.setDoInput(true);
534: httpConnection.setUseCaches(false);
535: HttpURLConnection.setFollowRedirects(true);
536:
537: httpConnection.connect();
538:
539: try {
540:
541: responseCode = httpConnection.getResponseCode();
542:
543: // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults
544: if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
545: isFailure = true;
546: } else if ((responseCode / 100) != 2) {
547: log.log(Level.SEVERE, "SAAJ0008.p2p.bad.response",
548: new String[] { httpConnection
549: .getResponseMessage() });
550: throw new SOAPExceptionImpl("Bad response: ("
551: + responseCode
552: + httpConnection.getResponseMessage());
553:
554: }
555: } catch (IOException e) {
556: // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds!
557: responseCode = httpConnection.getResponseCode();
558: if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
559: isFailure = true;
560: } else {
561: throw e;
562: }
563:
564: }
565:
566: } catch (SOAPException ex) {
567: throw ex;
568: } catch (Exception ex) {
569: log.severe("SAAJ0012.p2p.get.failed");
570: throw new SOAPExceptionImpl("Get failed", ex);
571: }
572:
573: SOAPMessage response = null;
574: if (responseCode == HttpURLConnection.HTTP_OK || isFailure) {
575: try {
576: MimeHeaders headers = new MimeHeaders();
577:
578: String key, value;
579:
580: // Header field 0 is the status line so we skip it.
581:
582: int i = 1;
583:
584: while (true) {
585: key = httpConnection.getHeaderFieldKey(i);
586: value = httpConnection.getHeaderField(i);
587:
588: if (key == null && value == null)
589: break;
590:
591: if (key != null) {
592: StringTokenizer values = new StringTokenizer(
593: value, ",");
594: while (values.hasMoreTokens())
595: headers.addHeader(key, values.nextToken()
596: .trim());
597: }
598: i++;
599: }
600:
601: InputStream httpIn = (isFailure ? httpConnection
602: .getErrorStream() : httpConnection
603: .getInputStream());
604:
605: byte[] bytes = readFully(httpIn);
606:
607: int length = httpConnection.getContentLength() == -1 ? bytes.length
608: : httpConnection.getContentLength();
609:
610: // If no reply message is returned,
611: // content-Length header field value is expected to be zero.
612: if (length == 0) {
613: response = null;
614: log.warning("SAAJ0014.p2p.content.zero");
615: } else {
616:
617: ByteInputStream in = new ByteInputStream(bytes,
618: length);
619: response = messageFactory
620: .createMessage(headers, in);
621: }
622:
623: httpIn.close();
624: httpConnection.disconnect();
625:
626: } catch (SOAPException ex) {
627: throw ex;
628: } catch (Exception ex) {
629: log.log(Level.SEVERE, "SAAJ0010.p2p.cannot.read.resp",
630: ex);
631: throw new SOAPExceptionImpl("Unable to read response: "
632: + ex.getMessage());
633: }
634: }
635: return response;
636: }
637:
638: private byte[] readFully(InputStream istream) throws IOException {
639: ByteArrayOutputStream bout = new ByteArrayOutputStream();
640: byte[] buf = new byte[1024];
641: int num = 0;
642:
643: while ((num = istream.read(buf)) != -1) {
644: bout.write(buf, 0, num);
645: }
646:
647: byte[] ret = bout.toByteArray();
648:
649: return ret;
650: }
651:
652: private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol";
653: private static String SSL_PROVIDER = "com.sun.net.ssl.internal.ssl.Provider";
654:
655: private void initHttps() {
656: //if(!setHttps) {
657: String pkgs = System.getProperty("java.protocol.handler.pkgs");
658: log.log(Level.FINE, "SAAJ0053.p2p.providers",
659: new String[] { pkgs });
660:
661: if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) {
662: if (pkgs == null)
663: pkgs = SSL_PKG;
664: else
665: pkgs = pkgs + "|" + SSL_PKG;
666: System.setProperty("java.protocol.handler.pkgs", pkgs);
667: log.log(Level.FINE, "SAAJ0054.p2p.set.providers",
668: new String[] { pkgs });
669: try {
670: Class c = Class.forName(SSL_PROVIDER);
671: Provider p = (Provider) c.newInstance();
672: Security.addProvider(p);
673: log.log(Level.FINE, "SAAJ0055.p2p.added.ssl.provider",
674: new String[] { SSL_PROVIDER });
675: //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER);
676: //setHttps = true;
677: } catch (Exception ex) {
678: }
679: }
680: //}
681: }
682:
683: private void initAuthUserInfo(HttpURLConnection conn,
684: String userInfo) {
685: String user;
686: String password;
687: if (userInfo != null) { // get the user and password
688: //System.out.println("UserInfo= " + userInfo );
689: int delimiter = userInfo.indexOf(':');
690: if (delimiter == -1) {
691: user = ParseUtil.decode(userInfo);
692: password = null;
693: } else {
694: user = ParseUtil.decode(userInfo.substring(0,
695: delimiter++));
696: password = ParseUtil.decode(userInfo
697: .substring(delimiter));
698: }
699:
700: String plain = user + ":";
701: byte[] nameBytes = plain.getBytes();
702: byte[] passwdBytes = password.getBytes();
703:
704: // concatenate user name and password bytes and encode them
705: byte[] concat = new byte[nameBytes.length
706: + passwdBytes.length];
707:
708: System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length);
709: System.arraycopy(passwdBytes, 0, concat, nameBytes.length,
710: passwdBytes.length);
711: String auth = "Basic " + new String(Base64.encode(concat));
712: conn.setRequestProperty("Authorization", auth);
713: if (dL > 0)
714: d("Adding auth " + auth);
715: }
716: }
717:
718: private static final int dL = 0;
719:
720: private void d(String s) {
721: log.log(Level.SEVERE, "SAAJ0013.p2p.HttpSOAPConnection",
722: new String[] { s });
723: System.err.println("HttpSOAPConnection: " + s);
724: }
725:
726: private java.net.HttpURLConnection createConnection(URL endpoint)
727: throws IOException {
728: return (HttpURLConnection) endpoint.openConnection();
729: }
730:
731: }
|