Mon, 04 May 2009 21:10:41 -0700
6658158: Mutable statics in SAAJ (findbugs)
6658163: txw2.DatatypeWriter.BUILDIN is a mutable static (findbugs)
Reviewed-by: darcy
1 /*
2 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 /*
26 * $Id: HttpSOAPConnection.java,v 1.41 2006/01/27 12:49:17 vj135062 Exp $
27 * $Revision: 1.41 $
28 * $Date: 2006/01/27 12:49:17 $
29 */
32 package com.sun.xml.internal.messaging.saaj.client.p2p;
34 import java.io.*;
35 import java.lang.reflect.Method;
36 import java.net.*;
37 import java.security.*;
38 import java.util.Iterator;
39 import java.util.StringTokenizer;
40 import java.util.logging.Level;
41 import java.util.logging.Logger;
43 import javax.xml.soap.*;
45 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
46 import com.sun.xml.internal.messaging.saaj.util.*;
48 /**
49 * This represents a "connection" to the simple HTTP-based provider.
50 *
51 * @author Anil Vijendran (akv@eng.sun.com)
52 * @author Rajiv Mordani (rajiv.mordani@sun.com)
53 * @author Manveen Kaur (manveen.kaur@sun.com)
54 *
55 */
56 public class HttpSOAPConnection extends SOAPConnection {
58 public static final String vmVendor = System.getProperty("java.vendor.url");
59 private static final String sunVmVendor = "http://java.sun.com/";
60 private static final String ibmVmVendor = "http://www.ibm.com/";
61 private static final boolean isSunVM = sunVmVendor.equals(vmVendor) ? true: false;
62 private static final boolean isIBMVM = ibmVmVendor.equals(vmVendor) ? true : false;
63 private static final String JAXM_URLENDPOINT="javax.xml.messaging.URLEndpoint";
65 protected static final Logger log =
66 Logger.getLogger(LogDomainConstants.HTTP_CONN_DOMAIN,
67 "com.sun.xml.internal.messaging.saaj.client.p2p.LocalStrings");
69 public static final String defaultProxyHost = null;
70 public static final int defaultProxyPort = -1;
72 MessageFactory messageFactory = null;
74 boolean closed = false;
76 public HttpSOAPConnection() throws SOAPException {
77 proxyHost = defaultProxyHost;
78 proxyPort = defaultProxyPort;
80 try {
81 messageFactory = MessageFactory.newInstance(SOAPConstants.DYNAMIC_SOAP_PROTOCOL);
82 } catch (NoSuchMethodError ex) {
83 //fallback to default SOAP 1.1 in this case for backward compatibility
84 messageFactory = MessageFactory.newInstance();
85 } catch (Exception ex) {
86 log.log(Level.SEVERE, "SAAJ0001.p2p.cannot.create.msg.factory", ex);
87 throw new SOAPExceptionImpl("Unable to create message factory", ex);
88 }
89 }
91 public void close() throws SOAPException {
92 if (closed) {
93 log.severe("SAAJ0002.p2p.close.already.closed.conn");
94 throw new SOAPExceptionImpl("Connection already closed");
95 }
97 messageFactory = null;
98 closed = true;
99 }
101 public SOAPMessage call(SOAPMessage message, Object endPoint)
102 throws SOAPException {
103 if (closed) {
104 log.severe("SAAJ0003.p2p.call.already.closed.conn");
105 throw new SOAPExceptionImpl("Connection is closed");
106 }
108 Class urlEndpointClass = null;
109 ClassLoader loader = Thread.currentThread().getContextClassLoader();
111 try {
112 if (loader != null) {
113 urlEndpointClass = loader.loadClass(JAXM_URLENDPOINT);
114 } else {
115 urlEndpointClass = Class.forName(JAXM_URLENDPOINT);
116 }
117 } catch (ClassNotFoundException ex) {
118 //Do nothing. URLEndpoint is available only when JAXM is there.
119 log.finest("SAAJ0090.p2p.endpoint.available.only.for.JAXM");
120 }
122 if (urlEndpointClass != null) {
123 if (urlEndpointClass.isInstance(endPoint)) {
124 String url = null;
126 try {
127 Method m = urlEndpointClass.getMethod("getURL", (Class[])null);
128 url = (String) m.invoke(endPoint, (Object[])null);
129 } catch (Exception ex) {
130 // TBD -- exception chaining
131 log.log(Level.SEVERE,"SAAJ0004.p2p.internal.err",ex);
132 throw new SOAPExceptionImpl(
133 "Internal error: " + ex.getMessage());
134 }
135 try {
136 endPoint = new URL(url);
137 } catch (MalformedURLException mex) {
138 log.log(Level.SEVERE,"SAAJ0005.p2p.", mex);
139 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage());
140 }
141 }
142 }
144 if (endPoint instanceof java.lang.String) {
145 try {
146 endPoint = new URL((String) endPoint);
147 } catch (MalformedURLException mex) {
148 log.log(Level.SEVERE, "SAAJ0006.p2p.bad.URL", mex);
149 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage());
150 }
151 }
153 if (endPoint instanceof URL)
154 try {
155 PriviledgedPost pp =
156 new PriviledgedPost(this, message, (URL) endPoint);
157 SOAPMessage response =
158 (SOAPMessage) AccessController.doPrivileged(pp);
160 return response;
161 } catch (Exception ex) {
162 // TBD -- chaining?
163 throw new SOAPExceptionImpl(ex);
164 } else {
165 log.severe("SAAJ0007.p2p.bad.endPoint.type");
166 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint);
167 }
168 }
170 static class PriviledgedPost implements PrivilegedExceptionAction {
172 HttpSOAPConnection c;
173 SOAPMessage message;
174 URL endPoint;
176 PriviledgedPost(
177 HttpSOAPConnection c,
178 SOAPMessage message,
179 URL endPoint) {
180 this.c = c;
181 this.message = message;
182 this.endPoint = endPoint;
183 }
185 public Object run() throws Exception {
186 return c.post(message, endPoint);
187 }
188 }
190 // TBD
191 // Fix this to do things better.
193 private String proxyHost = null;
195 static class PriviledgedSetProxyAction implements PrivilegedExceptionAction {
197 String proxyHost = null;
198 int proxyPort = 0;
200 PriviledgedSetProxyAction(String host, int port) {
201 this.proxyHost = host;
202 this.proxyPort = port;
203 }
205 public Object run() throws Exception {
206 System.setProperty("http.proxyHost", proxyHost);
207 System.setProperty("http.proxyPort", new Integer(proxyPort).toString());
208 log.log(Level.FINE, "SAAJ0050.p2p.proxy.host",
209 new String[] { proxyHost });
210 log.log(Level.FINE, "SAAJ0051.p2p.proxy.port",
211 new String[] { new Integer(proxyPort).toString() });
212 return proxyHost;
213 }
214 }
217 public void setProxy(String host, int port) {
218 try {
219 proxyPort = port;
220 PriviledgedSetProxyAction ps = new PriviledgedSetProxyAction(host, port);
221 proxyHost = (String) AccessController.doPrivileged(ps);
222 } catch (Exception e) {
223 throw new RuntimeException(e);
224 }
225 }
227 public String getProxyHost() {
228 return proxyHost;
229 }
231 private int proxyPort = -1;
233 public int getProxyPort() {
234 return proxyPort;
235 }
237 SOAPMessage post(SOAPMessage message, URL endPoint) throws SOAPException {
238 boolean isFailure = false;
240 URL url = null;
241 HttpURLConnection httpConnection = null;
243 int responseCode = 0;
244 try {
245 if (endPoint.getProtocol().equals("https"))
246 //if(!setHttps)
247 initHttps();
248 // Process the URL
249 JaxmURI uri = new JaxmURI(endPoint.toString());
250 String userInfo = uri.getUserinfo();
252 url = endPoint;
254 if (dL > 0)
255 d("uri: " + userInfo + " " + url + " " + uri);
257 // TBD
258 // Will deal with https later.
259 if (!url.getProtocol().equalsIgnoreCase("http")
260 && !url.getProtocol().equalsIgnoreCase("https")) {
261 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https");
262 throw new IllegalArgumentException(
263 "Protocol "
264 + url.getProtocol()
265 + " not supported in URL "
266 + url);
267 }
268 httpConnection = (HttpURLConnection) createConnection(url);
270 httpConnection.setRequestMethod("POST");
272 httpConnection.setDoOutput(true);
273 httpConnection.setDoInput(true);
274 httpConnection.setUseCaches(false);
275 HttpURLConnection.setFollowRedirects(true);
277 if (message.saveRequired())
278 message.saveChanges();
280 MimeHeaders headers = message.getMimeHeaders();
282 Iterator it = headers.getAllHeaders();
283 boolean hasAuth = false; // true if we find explicit Auth header
284 while (it.hasNext()) {
285 MimeHeader header = (MimeHeader) it.next();
287 String[] values = headers.getHeader(header.getName());
289 if (values.length == 1)
290 httpConnection.setRequestProperty(
291 header.getName(),
292 header.getValue());
293 else {
294 StringBuffer concat = new StringBuffer();
295 int i = 0;
296 while (i < values.length) {
297 if (i != 0)
298 concat.append(',');
299 concat.append(values[i]);
300 i++;
301 }
303 httpConnection.setRequestProperty(
304 header.getName(),
305 concat.toString());
306 }
308 if ("Authorization".equals(header.getName())) {
309 hasAuth = true;
310 log.fine("SAAJ0091.p2p.https.auth.in.POST.true");
311 }
312 }
314 if (!hasAuth && userInfo != null) {
315 initAuthUserInfo(httpConnection, userInfo);
316 }
318 OutputStream out = httpConnection.getOutputStream();
319 message.writeTo(out);
321 out.flush();
322 out.close();
324 httpConnection.connect();
326 try {
328 responseCode = httpConnection.getResponseCode();
330 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults
331 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
332 isFailure = true;
333 }
334 //else if (responseCode != HttpURLConnection.HTTP_OK)
335 //else if (!(responseCode >= HttpURLConnection.HTTP_OK && responseCode < 207))
336 else if ((responseCode / 100) != 2) {
337 log.log(Level.SEVERE,
338 "SAAJ0008.p2p.bad.response",
339 new String[] {httpConnection.getResponseMessage()});
340 throw new SOAPExceptionImpl(
341 "Bad response: ("
342 + responseCode
343 + httpConnection.getResponseMessage());
345 }
346 } catch (IOException e) {
347 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds!
348 responseCode = httpConnection.getResponseCode();
349 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
350 isFailure = true;
351 } else {
352 throw e;
353 }
355 }
357 } catch (SOAPException ex) {
358 throw ex;
359 } catch (Exception ex) {
360 log.severe("SAAJ0009.p2p.msg.send.failed");
361 throw new SOAPExceptionImpl("Message send failed", ex);
362 }
364 SOAPMessage response = null;
365 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) {
366 try {
367 MimeHeaders headers = new MimeHeaders();
369 String key, value;
371 // Header field 0 is the status line so we skip it.
373 int i = 1;
375 while (true) {
376 key = httpConnection.getHeaderFieldKey(i);
377 value = httpConnection.getHeaderField(i);
379 if (key == null && value == null)
380 break;
382 if (key != null) {
383 StringTokenizer values =
384 new StringTokenizer(value, ",");
385 while (values.hasMoreTokens())
386 headers.addHeader(key, values.nextToken().trim());
387 }
388 i++;
389 }
391 InputStream httpIn =
392 (isFailure
393 ? httpConnection.getErrorStream()
394 : httpConnection.getInputStream());
396 byte[] bytes = readFully(httpIn);
398 int length =
399 httpConnection.getContentLength() == -1
400 ? bytes.length
401 : httpConnection.getContentLength();
403 // If no reply message is returned,
404 // content-Length header field value is expected to be zero.
405 if (length == 0) {
406 response = null;
407 log.warning("SAAJ0014.p2p.content.zero");
408 } else {
409 ByteInputStream in = new ByteInputStream(bytes, length);
410 response = messageFactory.createMessage(headers, in);
411 }
413 httpIn.close();
414 httpConnection.disconnect();
416 } catch (SOAPException ex) {
417 throw ex;
418 } catch (Exception ex) {
419 log.log(Level.SEVERE,"SAAJ0010.p2p.cannot.read.resp", ex);
420 throw new SOAPExceptionImpl(
421 "Unable to read response: " + ex.getMessage());
422 }
423 }
424 return response;
425 }
427 // Object identifies where the request should be sent.
428 // It is required to support objects of type String and java.net.URL.
430 public SOAPMessage get(Object endPoint) throws SOAPException {
431 if (closed) {
432 log.severe("SAAJ0011.p2p.get.already.closed.conn");
433 throw new SOAPExceptionImpl("Connection is closed");
434 }
435 Class urlEndpointClass = null;
437 try {
438 urlEndpointClass = Class.forName("javax.xml.messaging.URLEndpoint");
439 } catch (Exception ex) {
440 //Do nothing. URLEndpoint is available only when JAXM is there.
441 }
443 if (urlEndpointClass != null) {
444 if (urlEndpointClass.isInstance(endPoint)) {
445 String url = null;
447 try {
448 Method m = urlEndpointClass.getMethod("getURL", (Class[])null);
449 url = (String) m.invoke(endPoint, (Object[])null);
450 } catch (Exception ex) {
451 log.severe("SAAJ0004.p2p.internal.err");
452 throw new SOAPExceptionImpl(
453 "Internal error: " + ex.getMessage());
454 }
455 try {
456 endPoint = new URL(url);
457 } catch (MalformedURLException mex) {
458 log.severe("SAAJ0005.p2p.");
459 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage());
460 }
461 }
462 }
464 if (endPoint instanceof java.lang.String) {
465 try {
466 endPoint = new URL((String) endPoint);
467 } catch (MalformedURLException mex) {
468 log.severe("SAAJ0006.p2p.bad.URL");
469 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage());
470 }
471 }
473 if (endPoint instanceof URL)
474 try {
475 PriviledgedGet pg = new PriviledgedGet(this, (URL) endPoint);
476 SOAPMessage response =
477 (SOAPMessage) AccessController.doPrivileged(pg);
479 return response;
480 } catch (Exception ex) {
481 throw new SOAPExceptionImpl(ex);
482 } else
483 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint);
484 }
486 static class PriviledgedGet implements PrivilegedExceptionAction {
488 HttpSOAPConnection c;
489 URL endPoint;
491 PriviledgedGet(HttpSOAPConnection c, URL endPoint) {
492 this.c = c;
493 this.endPoint = endPoint;
494 }
496 public Object run() throws Exception {
497 return c.get(endPoint);
498 }
499 }
501 SOAPMessage get(URL endPoint) throws SOAPException {
502 boolean isFailure = false;
504 URL url = null;
505 HttpURLConnection httpConnection = null;
507 int responseCode = 0;
508 try {
509 /// Is https GET allowed??
510 if (endPoint.getProtocol().equals("https"))
511 initHttps();
512 // Process the URL
513 JaxmURI uri = new JaxmURI(endPoint.toString());
514 String userInfo = uri.getUserinfo();
516 url = endPoint;
518 if (dL > 0)
519 d("uri: " + userInfo + " " + url + " " + uri);
521 // TBD
522 // Will deal with https later.
523 if (!url.getProtocol().equalsIgnoreCase("http")
524 && !url.getProtocol().equalsIgnoreCase("https")) {
525 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https");
526 throw new IllegalArgumentException(
527 "Protocol "
528 + url.getProtocol()
529 + " not supported in URL "
530 + url);
531 }
532 httpConnection = (HttpURLConnection) createConnection(url);
534 httpConnection.setRequestMethod("GET");
536 httpConnection.setDoOutput(true);
537 httpConnection.setDoInput(true);
538 httpConnection.setUseCaches(false);
539 HttpURLConnection.setFollowRedirects(true);
541 httpConnection.connect();
543 try {
545 responseCode = httpConnection.getResponseCode();
547 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults
548 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
549 isFailure = true;
550 } else if ((responseCode / 100) != 2) {
551 log.log(Level.SEVERE,
552 "SAAJ0008.p2p.bad.response",
553 new String[] { httpConnection.getResponseMessage()});
554 throw new SOAPExceptionImpl(
555 "Bad response: ("
556 + responseCode
557 + httpConnection.getResponseMessage());
559 }
560 } catch (IOException e) {
561 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds!
562 responseCode = httpConnection.getResponseCode();
563 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) {
564 isFailure = true;
565 } else {
566 throw e;
567 }
569 }
571 } catch (SOAPException ex) {
572 throw ex;
573 } catch (Exception ex) {
574 log.severe("SAAJ0012.p2p.get.failed");
575 throw new SOAPExceptionImpl("Get failed", ex);
576 }
578 SOAPMessage response = null;
579 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) {
580 try {
581 MimeHeaders headers = new MimeHeaders();
583 String key, value;
585 // Header field 0 is the status line so we skip it.
587 int i = 1;
589 while (true) {
590 key = httpConnection.getHeaderFieldKey(i);
591 value = httpConnection.getHeaderField(i);
593 if (key == null && value == null)
594 break;
596 if (key != null) {
597 StringTokenizer values =
598 new StringTokenizer(value, ",");
599 while (values.hasMoreTokens())
600 headers.addHeader(key, values.nextToken().trim());
601 }
602 i++;
603 }
605 InputStream httpIn =
606 (isFailure
607 ? httpConnection.getErrorStream()
608 : httpConnection.getInputStream());
610 byte[] bytes = readFully(httpIn);
612 int length =
613 httpConnection.getContentLength() == -1
614 ? bytes.length
615 : httpConnection.getContentLength();
617 // If no reply message is returned,
618 // content-Length header field value is expected to be zero.
619 if (length == 0) {
620 response = null;
621 log.warning("SAAJ0014.p2p.content.zero");
622 } else {
624 ByteInputStream in = new ByteInputStream(bytes, length);
625 response = messageFactory.createMessage(headers, in);
626 }
628 httpIn.close();
629 httpConnection.disconnect();
631 } catch (SOAPException ex) {
632 throw ex;
633 } catch (Exception ex) {
634 log.log(Level.SEVERE,
635 "SAAJ0010.p2p.cannot.read.resp",
636 ex);
637 throw new SOAPExceptionImpl(
638 "Unable to read response: " + ex.getMessage());
639 }
640 }
641 return response;
642 }
644 private byte[] readFully(InputStream istream) throws IOException {
645 ByteArrayOutputStream bout = new ByteArrayOutputStream();
646 byte[] buf = new byte[1024];
647 int num = 0;
649 while ((num = istream.read(buf)) != -1) {
650 bout.write(buf, 0, num);
651 }
653 byte[] ret = bout.toByteArray();
655 return ret;
656 }
657 //private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol";
658 //private static String SSL_PROVIDER =
659 // "com.sun.net.ssl.internal.ssl.Provider";
660 private static final String SSL_PKG;
661 private static final String SSL_PROVIDER;
664 static {
665 if (isIBMVM) {
666 SSL_PKG ="com.ibm.net.ssl.internal.www.protocol";
667 SSL_PROVIDER ="com.ibm.net.ssl.internal.ssl.Provider";
668 } else {
669 //if not IBM VM default to Sun.
670 SSL_PKG = "com.sun.net.ssl.internal.www.protocol";
671 SSL_PROVIDER ="com.sun.net.ssl.internal.ssl.Provider";
672 }
673 }
674 private void initHttps() {
675 //if(!setHttps) {
676 String pkgs = System.getProperty("java.protocol.handler.pkgs");
677 log.log(Level.FINE,
678 "SAAJ0053.p2p.providers",
679 new String[] { pkgs });
681 if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) {
682 if (pkgs == null)
683 pkgs = SSL_PKG;
684 else
685 pkgs = pkgs + "|" + SSL_PKG;
686 System.setProperty("java.protocol.handler.pkgs", pkgs);
687 log.log(Level.FINE,
688 "SAAJ0054.p2p.set.providers",
689 new String[] { pkgs });
690 try {
691 Class c = Class.forName(SSL_PROVIDER);
692 Provider p = (Provider) c.newInstance();
693 Security.addProvider(p);
694 log.log(Level.FINE,
695 "SAAJ0055.p2p.added.ssl.provider",
696 new String[] { SSL_PROVIDER });
697 //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER);
698 //setHttps = true;
699 } catch (Exception ex) {
700 }
701 }
702 //}
703 }
705 private void initAuthUserInfo(HttpURLConnection conn, String userInfo) {
706 String user;
707 String password;
708 if (userInfo != null) { // get the user and password
709 //System.out.println("UserInfo= " + userInfo );
710 int delimiter = userInfo.indexOf(':');
711 if (delimiter == -1) {
712 user = ParseUtil.decode(userInfo);
713 password = null;
714 } else {
715 user = ParseUtil.decode(userInfo.substring(0, delimiter++));
716 password = ParseUtil.decode(userInfo.substring(delimiter));
717 }
719 String plain = user + ":";
720 byte[] nameBytes = plain.getBytes();
721 byte[] passwdBytes = password.getBytes();
723 // concatenate user name and password bytes and encode them
724 byte[] concat = new byte[nameBytes.length + passwdBytes.length];
726 System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length);
727 System.arraycopy(
728 passwdBytes,
729 0,
730 concat,
731 nameBytes.length,
732 passwdBytes.length);
733 String auth = "Basic " + new String(Base64.encode(concat));
734 conn.setRequestProperty("Authorization", auth);
735 if (dL > 0)
736 d("Adding auth " + auth);
737 }
738 }
740 private static final int dL = 0;
741 private void d(String s) {
742 log.log(Level.SEVERE,
743 "SAAJ0013.p2p.HttpSOAPConnection",
744 new String[] { s });
745 System.err.println("HttpSOAPConnection: " + s);
746 }
748 private java.net.HttpURLConnection createConnection(URL endpoint)
749 throws IOException {
750 return (HttpURLConnection) endpoint.openConnection();
751 }
753 }