Source Code Cross Referenced for HTTPConduit.java in  » Web-Services-apache-cxf-2.0.1 » transports » org » apache » cxf » transport » http » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Services apache cxf 2.0.1 » transports » org.apache.cxf.transport.http 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**
0002:         * Licensed to the Apache Software Foundation (ASF) under one
0003:         * or more contributor license agreements. See the NOTICE file
0004:         * distributed with this work for additional information
0005:         * regarding copyright ownership. The ASF licenses this file
0006:         * to you under the Apache License, Version 2.0 (the
0007:         * "License"); you may not use this file except in compliance
0008:         * with the License. You may obtain a copy of the License at
0009:         *
0010:         * http://www.apache.org/licenses/LICENSE-2.0
0011:         *
0012:         * Unless required by applicable law or agreed to in writing,
0013:         * software distributed under the License is distributed on an
0014:         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
0015:         * KIND, either express or implied. See the License for the
0016:         * specific language governing permissions and limitations
0017:         * under the License.
0018:         */package org.apache.cxf.transport.http;
0019:
0020:        import java.io.IOException;
0021:        import java.io.InputStream;
0022:        import java.io.OutputStream;
0023:        import java.io.PushbackInputStream;
0024:        import java.net.HttpURLConnection;
0025:        import java.net.InetSocketAddress;
0026:        import java.net.MalformedURLException;
0027:        import java.net.Proxy;
0028:        import java.net.URL;
0029:        import java.net.URLConnection;
0030:        import java.util.Arrays;
0031:        import java.util.HashMap;
0032:        import java.util.HashSet;
0033:        import java.util.List;
0034:        import java.util.Map;
0035:        import java.util.Set;
0036:        import java.util.logging.Level;
0037:        import java.util.logging.Logger;
0038:
0039:        import javax.annotation.Resource;
0040:        import javax.xml.namespace.QName;
0041:
0042:        import org.apache.cxf.Bus;
0043:        import org.apache.cxf.common.logging.LogUtils;
0044:        import org.apache.cxf.common.util.Base64Utility;
0045:        import org.apache.cxf.configuration.Configurable;
0046:        import org.apache.cxf.configuration.jsse.TLSClientParameters;
0047:        import org.apache.cxf.configuration.security.AuthorizationPolicy;
0048:        import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
0049:        import org.apache.cxf.helpers.CastUtils;
0050:        import org.apache.cxf.helpers.HttpHeaderHelper;
0051:        import org.apache.cxf.helpers.IOUtils;
0052:        import org.apache.cxf.io.AbstractWrappedOutputStream;
0053:        import org.apache.cxf.io.CacheAndWriteOutputStream;
0054:        import org.apache.cxf.message.Exchange;
0055:        import org.apache.cxf.message.ExchangeImpl;
0056:        import org.apache.cxf.message.Message;
0057:        import org.apache.cxf.message.MessageImpl;
0058:        import org.apache.cxf.service.model.EndpointInfo;
0059:        import org.apache.cxf.transport.AbstractConduit;
0060:        import org.apache.cxf.transport.Destination;
0061:        import org.apache.cxf.transport.DestinationFactory;
0062:        import org.apache.cxf.transport.DestinationFactoryManager;
0063:        import org.apache.cxf.transport.MessageObserver;
0064:        import org.apache.cxf.transport.http.policy.PolicyUtils;
0065:        import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
0066:        import org.apache.cxf.ws.addressing.EndpointReferenceType;
0067:        import org.apache.cxf.ws.policy.Assertor;
0068:        import org.apache.cxf.ws.policy.PolicyEngine;
0069:        import org.apache.cxf.wsdl.EndpointReferenceUtils;
0070:        import org.apache.geronimo.mail.util.StringBufferOutputStream;
0071:
0072:        import static org.apache.cxf.message.Message.DECOUPLED_CHANNEL_MESSAGE;
0073:
0074:        /*
0075:         * HTTP Conduit implementation.
0076:         * <p>
0077:         * This implementation is a based on the java.net.URLConnection interface and
0078:         * dependent upon installed implementations of that URLConnection, 
0079:         * HttpURLConnection, and HttpsURLConnection. Currently, this implemenation
0080:         * has been known to work with the Sun JDK 1.5 default implementations. The
0081:         * HttpsURLConnection is part of Sun's implemenation of the JSSE. 
0082:         * Presently, the source code for the Sun JSSE implemenation is unavaiable
0083:         * and therefore we may only lay a guess of whether its HttsURLConnection
0084:         * implemenation correctly works as far as security is concerned.
0085:         * <p>
0086:         * The Trust Decision. If a MessageTrustDecider is configured/set for the 
0087:         * Conduit, it is called upon the first flush of the headers in the 
0088:         * WrappedOutputStream. This reason for this approach is two fold. 
0089:         * Theoretically, in order to get connection information out of the 
0090:         * URLConnection, it must be "connected". We assume that its implementation will
0091:         * only follow through up to the point at which it will be ready to send
0092:         * one byte of data down to the endpoint, but through proxies, and the 
0093:         * commpletion of a TLS handshake in the case of HttpsURLConnection. 
0094:         * However, if we force the connect() call right away, the default
0095:         * implementations will not allow any calls to add/setRequestProperty,
0096:         * throwing an exception that the URLConnection is already connected. 
0097:         * <p>
0098:         * We need to keep the semantic that later CXF interceptors may add to the 
0099:         * PROTOCOL_HEADERS in the Message. This architectual decision forces us to 
0100:         * delay the connection until after that point, then pulling the trust decision.
0101:         * <p>
0102:         * The security caveat is that we don't really know when the connection is 
0103:         * really established. The call to "connect" is stated to force the 
0104:         * "connection," but it is a no-op if the connection was already established. 
0105:         * It is entirely possible that an implementation of an URLConnection may 
0106:         * indeed connect at will and start sending the headers down the connection 
0107:         * during calls to add/setRequestProperty!
0108:         * <p>
0109:         * We know that the JDK 1.5 sun.com.net.www.HttpURLConnection does not send
0110:         * this information before the "connect" call, because we can look at the
0111:         * source code. However, we can only assume, not verify, that the JSSE 1.5 
0112:         * HttpsURLConnection does the same, in that it is probable that the 
0113:         * HttpsURLConnection shares the HttpURLConnection implemenation.
0114:         * <p>
0115:         * Due to these implementations following redirects without trust checks, we
0116:         * force the URLConnection implementations not to follow redirects. If 
0117:         * client side policy dictates that we follow redirects, trust decisions are
0118:         * placed before each retransmit. On a redirect, any authorization information
0119:         * dynamically acquired by a BasicAuth UserPass supplier is removed before
0120:         * being retransmitted, as it may no longer be applicable to the new url to
0121:         * which the connection is redirected.
0122:         */
0123:
0124:        /**
0125:         * This Conduit handles the "http" and "https" transport protocols. An
0126:         * instance is governed by policies either explicity set or by 
0127:         * configuration.
0128:         */
0129:        public class HTTPConduit extends AbstractConduit implements 
0130:                Configurable, Assertor {
0131:
0132:            /**
0133:             *  This constant is the Message(Map) key for the HttpURLConnection that
0134:             *  is used to get the response.
0135:             */
0136:            public static final String KEY_HTTP_CONNECTION = "http.connection";
0137:
0138:            /**
0139:             * This constant is the Message(Map) key for a list of visited URLs that
0140:             * is used in redirect loop protection.
0141:             */
0142:            private static final String KEY_VISITED_URLS = "VisitedURLs";
0143:
0144:            /**
0145:             * This constant is the Message(Map) key for a list of URLs that
0146:             * is used in authorization loop protection.
0147:             */
0148:            private static final String KEY_AUTH_URLS = "AuthURLs";
0149:
0150:            /**
0151:             * The Logger for this class.
0152:             */
0153:            private static final Logger LOG = LogUtils
0154:                    .getL7dLogger(HTTPConduit.class);
0155:
0156:            /**
0157:             * This constant holds the suffix ".http-conduit" that is appended to the 
0158:             * Endpoint Qname to give the configuration name of this conduit.
0159:             */
0160:            private static final String SC_HTTP_CONDUIT_SUFFIX = ".http-conduit";
0161:
0162:            /**
0163:             * Buffer to use to suck unread bytes off of input streams
0164:             */
0165:            private static final byte BUFFER[] = new byte[1024];
0166:
0167:            /**
0168:             * This field holds the connection factory, which primarily is used to 
0169:             * factor out SSL specific code from this implementation.
0170:             * <p>
0171:             * This field is "protected" to facilitate some contrived UnitTesting so
0172:             * that an extended class may alter its value with an EasyMock URLConnection
0173:             * Factory. 
0174:             */
0175:            protected HttpURLConnectionFactory connectionFactory;
0176:
0177:            /**
0178:             *  This field holds a reference to the CXF bus associated this conduit.
0179:             */
0180:            private final Bus bus;
0181:
0182:            /**
0183:             * This field is used for two reasons. First it provides the base name for
0184:             * the conduit for Spring configuration. The other is to hold default 
0185:             * address information, should it not be supplied in the Message Map, by the 
0186:             * Message.ENDPOINT_ADDRESS property.
0187:             */
0188:            private final EndpointInfo endpointInfo;
0189:
0190:            /**
0191:             * This field holds the "default" address for this particular conduit, which
0192:             * is set at construction.
0193:             */
0194:            private final String defaultEndpointAddress;
0195:
0196:            /**
0197:             * This field holds the "default" URL for this particular conduit, which
0198:             * is created on demand.
0199:             */
0200:            private URL defaultEndpointURL;
0201:
0202:            private Destination decoupledDestination;
0203:            private MessageObserver decoupledObserver;
0204:            private int decoupledDestinationRefCount;
0205:
0206:            // Configuratble/settable values
0207:
0208:            /**
0209:             * This field holds the QoS configuration settings for this conduit.
0210:             * This field is injected via spring configuration based on the conduit 
0211:             * name.
0212:             */
0213:            private HTTPClientPolicy clientSidePolicy;
0214:
0215:            /**
0216:             * This field holds the password authorization configuration.
0217:             * This field is injected via spring configuration based on the conduit 
0218:             * name.
0219:             */
0220:            private AuthorizationPolicy authorizationPolicy;
0221:
0222:            /**
0223:             * This field holds the password authorization configuration for the 
0224:             * configured proxy. This field is injected via spring configuration based 
0225:             * on the conduit name.
0226:             */
0227:            private ProxyAuthorizationPolicy proxyAuthorizationPolicy;
0228:
0229:            /**
0230:             * This field holds the configuration TLS configuration which
0231:             * is programmatically configured. 
0232:             */
0233:            private TLSClientParameters tlsClientParameters;
0234:
0235:            /**
0236:             * This field contains the MessageTrustDecider.
0237:             */
0238:            private MessageTrustDecider trustDecider;
0239:
0240:            /**
0241:             * This field contains the HttpBasicAuthSupplier.
0242:             */
0243:            private HttpBasicAuthSupplier basicAuthSupplier;
0244:
0245:            /**
0246:             * This boolean signfies that that finalizeConfig is called, which is
0247:             * after the HTTPTransportFactory configures this object via spring.
0248:             * At this point, any change by a "setter" is dynamic, and any change
0249:             * should be handled as such.
0250:             */
0251:            private boolean configFinalized;
0252:
0253:            /**
0254:             * Variables for holding session state if sessions are supposed to be maintained
0255:             */
0256:            private String sessionId;
0257:            private boolean maintainSession;
0258:
0259:            /**
0260:             * Constructor
0261:             * 
0262:             * @param b the associated Bus
0263:             * @param ei the endpoint info of the initiator
0264:             * @throws IOException
0265:             */
0266:            public HTTPConduit(Bus b, EndpointInfo ei) throws IOException {
0267:                this (b, ei, null);
0268:            }
0269:
0270:            /**
0271:             * Constructor
0272:             * 
0273:             * @param b the associated Bus.
0274:             * @param endpoint the endpoint info of the initiator.
0275:             * @param t the endpoint reference of the target.
0276:             * @throws IOException
0277:             */
0278:            public HTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType t)
0279:                    throws IOException {
0280:                super (getTargetReference(ei, t, b));
0281:
0282:                bus = b;
0283:                endpointInfo = ei;
0284:
0285:                defaultEndpointAddress = t == null ? ei.getAddress() : t
0286:                        .getAddress().getValue();
0287:
0288:                initializeConfig();
0289:            }
0290:
0291:            /**
0292:             * This method returns the registered Logger for this conduit.
0293:             */
0294:            protected Logger getLogger() {
0295:                return LOG;
0296:            }
0297:
0298:            /**
0299:             * This method returns the name of the conduit, which is based on the
0300:             * endpoint name plus the SC_HTTP_CONDUIT_SUFFIX.
0301:             * @return
0302:             */
0303:            public final String getConduitName() {
0304:                return endpointInfo.getName() + SC_HTTP_CONDUIT_SUFFIX;
0305:            }
0306:
0307:            /**
0308:             * This method is called from the constructor which initializes
0309:             * the configuration. The TransportFactory will call configureBean
0310:             * on this object after construction.
0311:             */
0312:            private void initializeConfig() {
0313:
0314:                // wsdl extensors are superseded by policies which in        
0315:                // turn are superseded by injection                          
0316:
0317:                PolicyEngine pe = bus.getExtension(PolicyEngine.class);
0318:                if (null != pe && pe.isEnabled()
0319:                        && endpointInfo.getService() != null) {
0320:                    clientSidePolicy = PolicyUtils.getClient(pe, endpointInfo,
0321:                            this );
0322:                }
0323:
0324:            }
0325:
0326:            /**
0327:             * This call gets called by the HTTPTransportFactory after it
0328:             * causes an injection of the Spring configuration properties
0329:             * of this Conduit.
0330:             */
0331:            void finalizeConfig() {
0332:                // See if not set by configuration, if there are defaults
0333:                // in order from the Endpoint, Service, or Bus.
0334:
0335:                if (this .clientSidePolicy == null) {
0336:                    clientSidePolicy = endpointInfo.getTraversedExtensor(
0337:                            new HTTPClientPolicy(), HTTPClientPolicy.class);
0338:                }
0339:                if (this .authorizationPolicy == null) {
0340:                    authorizationPolicy = endpointInfo.getTraversedExtensor(
0341:                            new AuthorizationPolicy(),
0342:                            AuthorizationPolicy.class);
0343:
0344:                }
0345:                if (this .proxyAuthorizationPolicy == null) {
0346:                    proxyAuthorizationPolicy = endpointInfo
0347:                            .getTraversedExtensor(
0348:                                    new ProxyAuthorizationPolicy(),
0349:                                    ProxyAuthorizationPolicy.class);
0350:
0351:                }
0352:                if (this .tlsClientParameters == null) {
0353:                    tlsClientParameters = endpointInfo.getTraversedExtensor(
0354:                            null, TLSClientParameters.class);
0355:                }
0356:                if (this .trustDecider == null) {
0357:                    trustDecider = endpointInfo.getTraversedExtensor(null,
0358:                            MessageTrustDecider.class);
0359:                }
0360:                if (this .basicAuthSupplier == null) {
0361:                    basicAuthSupplier = endpointInfo.getTraversedExtensor(null,
0362:                            HttpBasicAuthSupplier.class);
0363:                }
0364:                if (trustDecider == null) {
0365:                    if (LOG.isLoggable(Level.FINE)) {
0366:                        LOG.log(Level.FINE,
0367:                                "No Trust Decider configured for Conduit '"
0368:                                        + getConduitName() + "'");
0369:                    }
0370:                } else {
0371:                    if (LOG.isLoggable(Level.FINE)) {
0372:                        LOG.log(Level.FINE, "Message Trust Decider of class '"
0373:                                + trustDecider.getClass().getName()
0374:                                + "' with logical name of '"
0375:                                + trustDecider.getLogicalName()
0376:                                + "' has been configured for Conduit '"
0377:                                + getConduitName() + "'");
0378:                    }
0379:                }
0380:                if (basicAuthSupplier == null) {
0381:                    if (LOG.isLoggable(Level.FINE)) {
0382:                        LOG.log(Level.FINE,
0383:                                "No Basic Auth Supplier configured for Conduit '"
0384:                                        + getConduitName() + "'");
0385:                    }
0386:                } else {
0387:                    if (LOG.isLoggable(Level.FINE)) {
0388:                        LOG.log(Level.FINE, "HttpBasicAuthSupplier of class '"
0389:                                + basicAuthSupplier.getClass().getName()
0390:                                + "' with logical name of '"
0391:                                + basicAuthSupplier.getLogicalName()
0392:                                + "' has been configured for Conduit '"
0393:                                + getConduitName() + "'");
0394:                    }
0395:                }
0396:                if (this .tlsClientParameters != null) {
0397:                    if (LOG.isLoggable(Level.FINE)) {
0398:                        LOG.log(Level.FINE, "Conduit '" + getConduitName()
0399:                                + "' has been configured for TLS "
0400:                                + "keyManagers "
0401:                                + tlsClientParameters.getKeyManagers()
0402:                                + "trustManagers "
0403:                                + tlsClientParameters.getTrustManagers()
0404:                                + "secureRandom "
0405:                                + tlsClientParameters.getSecureRandom());
0406:                    }
0407:                } else {
0408:                    if (LOG.isLoggable(Level.FINE)) {
0409:                        LOG.log(Level.FINE, "Conduit '" + getConduitName()
0410:                                + "' has been configured for plain http.");
0411:                    }
0412:                }
0413:
0414:                // Get the correct URLConnection factory based on the 
0415:                // configuration.
0416:                retrieveConnectionFactory();
0417:
0418:                // We have finalized the configuration. Any configurable entity
0419:                // set now, must make changes dynamically.
0420:                configFinalized = true;
0421:            }
0422:
0423:            /**
0424:             * This method sets the connectionFactory field for this object. It is called
0425:             * after an SSL Client Policy is set or an HttpsHostnameVerifier
0426:             * because we need to reinitialize the connection factory.
0427:             * <p>
0428:             * This method is "protected" so that this class may be extended and override
0429:             * this method to put an EasyMock URL Connection factory for some contrived 
0430:             * UnitTest that will of course break, should the calls to the URL Connection
0431:             * Factory get altered.
0432:             */
0433:            protected void retrieveConnectionFactory() {
0434:                connectionFactory = AbstractHTTPTransportFactory
0435:                        .getConnectionFactory(this );
0436:            }
0437:
0438:            /**
0439:             * Prepare to send an outbound HTTP message over this http conduit to a 
0440:             * particular endpoint.
0441:             * <P>
0442:             * If the Message.PATH_INFO property is set it gets appended
0443:             * to the Conduit's endpoint URL. If the Message.QUERY_STRING
0444:             * property is set, it gets appended to the resultant URL following
0445:             * a "?".
0446:             * <P>
0447:             * If the Message.HTTP_REQUEST_METHOD property is NOT set, the
0448:             * Http request method defaults to "POST".
0449:             * <P>
0450:             * If the Message.PROTOCOL_HEADERS is not set on the message, it is
0451:             * initialized to an empty map.
0452:             * <P>
0453:             * This call creates the OutputStream for the content of the message.
0454:             * It also assigns the created Http(s)URLConnection to the Message
0455:             * Map.
0456:             * 
0457:             * @param message The message to be sent.
0458:             */
0459:            public void prepare(Message message) throws IOException {
0460:                Map<String, List<String>> headers = getSetProtocolHeaders(message);
0461:
0462:                // This call can possibly change the conduit endpoint address and 
0463:                // protocol from the default set in EndpointInfo that is associated
0464:                // with the Conduit.
0465:                URL currentURL = setupURL(message);
0466:
0467:                HttpBasicAuthSupplier.UserPass userPass = null;
0468:
0469:                // The need to cache the request is off by default
0470:                boolean needToCacheRequest = false;
0471:
0472:                HttpURLConnection connection = connectionFactory
0473:                        .createConnection(getProxy(clientSidePolicy),
0474:                                currentURL);
0475:                connection.setDoOutput(true);
0476:
0477:                //TODO using Message context to decided HTTP send properties 
0478:
0479:                connection.setConnectTimeout((int) clientSidePolicy
0480:                        .getConnectionTimeout());
0481:                connection.setReadTimeout((int) clientSidePolicy
0482:                        .getReceiveTimeout());
0483:                connection.setUseCaches(false);
0484:                // We implement redirects in this conduit. We do not
0485:                // rely on the underlying URLConnection implementation
0486:                // because of trust issues.
0487:                connection.setInstanceFollowRedirects(false);
0488:
0489:                // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
0490:                String httpRequestMethod = (String) message
0491:                        .get(Message.HTTP_REQUEST_METHOD);
0492:
0493:                if (null != httpRequestMethod) {
0494:                    connection.setRequestMethod(httpRequestMethod);
0495:                } else {
0496:                    connection.setRequestMethod("POST");
0497:                }
0498:
0499:                boolean isChunking = false;
0500:                // We must cache the request if we have basic auth supplier
0501:                // without preemptive basic auth.
0502:                if (basicAuthSupplier != null) {
0503:                    userPass = basicAuthSupplier.getPreemptiveUserPass(
0504:                            getConduitName(), currentURL, message);
0505:                    needToCacheRequest = userPass == null;
0506:                    LOG.log(Level.INFO,
0507:                            "Basic Auth Supplier, but no Premeptive User Pass."
0508:                                    + " We must cache request.");
0509:                }
0510:                if (getClient().isAutoRedirect()) {
0511:                    // If the AutoRedirect property is set then we cannot
0512:                    // use chunked streaming mode. We ignore the "AllowChunking" 
0513:                    // property if AutoRedirect is turned on.
0514:
0515:                    needToCacheRequest = true;
0516:                    LOG.log(Level.INFO, "AutoRedirect is turned on.");
0517:                } else {
0518:                    if (!connection.getRequestMethod().equals("GET")
0519:                            && getClient().isAllowChunking()
0520:                            && !needToCacheRequest) {
0521:                        //TODO: The chunking mode be configured or at least some
0522:                        // documented client constant.
0523:                        //use -1 and allow the URL connection to pick a default value
0524:                        connection.setChunkedStreamingMode(-1);
0525:                        isChunking = true;
0526:                    }
0527:                }
0528:
0529:                //Do we need to maintain a session?
0530:                maintainSession = Boolean.TRUE.equals((Boolean) message
0531:                        .get(Message.MAINTAIN_SESSION));
0532:
0533:                //If we have a sessionId and we are maintaining sessions, then use it
0534:                if (maintainSession && sessionId != null) {
0535:                    connection.setRequestProperty(HttpHeaderHelper.COOKIE,
0536:                            "JSESSIONID=" + sessionId);
0537:                }
0538:
0539:                // The trust decision is relagated to after the "flushing" of the
0540:                // request headers.
0541:
0542:                // We place the connection on the message to pick it up
0543:                // in the WrappedOutputStream.
0544:
0545:                message.put(KEY_HTTP_CONNECTION, connection);
0546:
0547:                // Set the headers on the message according to configured 
0548:                // client side policy.
0549:
0550:                setHeadersByPolicy(message, currentURL, headers);
0551:
0552:                message.setContent(OutputStream.class, new WrappedOutputStream(
0553:                        message, connection, needToCacheRequest, isChunking));
0554:
0555:                // We are now "ready" to "send" the message. 
0556:            }
0557:
0558:            public void close(Message msg) throws IOException {
0559:                InputStream in = msg.getContent(InputStream.class);
0560:                try {
0561:                    if (in != null) {
0562:                        int count = 0;
0563:                        while (in.read(BUFFER) != -1 && count < 25) {
0564:                            //don't do anything, we just need to pull off the unread data (like
0565:                            //closing tags that we didn't need to read
0566:
0567:                            //however, limit it so we don't read off gigabytes of data we won't use.
0568:                            ++count;
0569:                        }
0570:                    }
0571:                } finally {
0572:                    super .close(msg);
0573:                }
0574:            }
0575:
0576:            /**
0577:             * This call must take place before anything is written to the 
0578:             * URLConnection. The URLConnection.connect() will be called in order 
0579:             * to get the connection information. 
0580:             * 
0581:             * This method is invoked just after setURLRequestHeaders() from the 
0582:             * WrappedOutputStream before it writes data to the URLConnection.
0583:             * 
0584:             * If trust cannot be established the Trust Decider implemenation
0585:             * throws an IOException.
0586:             * 
0587:             * @param message      The message being sent.
0588:             * @throws IOException This exception is thrown if trust cannot be
0589:             *                     established by the configured MessageTrustDecider.
0590:             * @see MessageTrustDecider
0591:             */
0592:            private void makeTrustDecision(Message message) throws IOException {
0593:
0594:                HttpURLConnection connection = (HttpURLConnection) message
0595:                        .get(KEY_HTTP_CONNECTION);
0596:
0597:                if (trustDecider != null) {
0598:                    try {
0599:                        // We must connect or we will not get the credentials.
0600:                        // The call is (said to be) ingored internally if
0601:                        // already connected.
0602:                        connection.connect();
0603:                        trustDecider
0604:                                .establishTrust(getConduitName(),
0605:                                        connectionFactory
0606:                                                .getConnectionInfo(connection),
0607:                                        message);
0608:                        if (LOG.isLoggable(Level.FINE)) {
0609:                            LOG.log(Level.FINE, "Trust Decider "
0610:                                    + trustDecider.getLogicalName()
0611:                                    + " considers Conduit " + getConduitName()
0612:                                    + " trusted.");
0613:                        }
0614:                    } catch (UntrustedURLConnectionIOException untrustedEx) {
0615:                        // This cast covers HttpsURLConnection as well.
0616:                        ((HttpURLConnection) connection).disconnect();
0617:                        if (LOG.isLoggable(Level.INFO)) {
0618:                            LOG.log(Level.INFO, "Trust Decider "
0619:                                    + trustDecider.getLogicalName()
0620:                                    + " considers Conduit " + getConduitName()
0621:                                    + " untrusted.", untrustedEx);
0622:                        }
0623:                        throw untrustedEx;
0624:                    }
0625:                } else {
0626:                    // This case, when there is no trust decider, a trust
0627:                    // decision should be a matter of policy.
0628:                    if (LOG.isLoggable(Level.FINE)) {
0629:                        LOG
0630:                                .log(
0631:                                        Level.FINE,
0632:                                        "No Trust Decider for Conduit '"
0633:                                                + getConduitName()
0634:                                                + "'. An afirmative Trust Decision is assumed.");
0635:                    }
0636:                }
0637:            }
0638:
0639:            /**
0640:             * This function sets up a URL based on ENDPOINT_ADDRESS, PATH_INFO,
0641:             * and QUERY_STRING properties in the Message. The QUERY_STRING gets
0642:             * added with a "?" after the PATH_INFO. If the ENDPOINT_ADDRESS is not
0643:             * set on the Message, the endpoint address is taken from the 
0644:             * "defaultEndpointURL".
0645:             * <p>
0646:             * The PATH_INFO is only added to the endpoint address string should 
0647:             * the PATH_INFO not equal the end of the endpoint address string.
0648:             * 
0649:             * @param message The message holds the addressing information.
0650:             * 
0651:             * @return The full URL specifying the HTTP request to the endpoint.
0652:             * 
0653:             * @throws MalformedURLException
0654:             */
0655:            private URL setupURL(Message message) throws MalformedURLException {
0656:                String value = (String) message.get(Message.ENDPOINT_ADDRESS);
0657:                String pathInfo = (String) message.get(Message.PATH_INFO);
0658:                String queryString = (String) message.get(Message.QUERY_STRING);
0659:
0660:                String result = value != null ? value : getURL().toString();
0661:
0662:                // REVISIT: is this really correct?
0663:                if (null != pathInfo && !result.endsWith(pathInfo)) {
0664:                    result = result + pathInfo;
0665:                }
0666:                if (queryString != null) {
0667:                    result = result + "?" + queryString;
0668:                }
0669:                return new URL(result);
0670:            }
0671:
0672:            /**
0673:             * Retreive the back-channel Destination.
0674:             * 
0675:             * @return the backchannel Destination (or null if the backchannel is
0676:             * built-in)
0677:             */
0678:            public synchronized Destination getBackChannel() {
0679:                if (decoupledDestination == null
0680:                        && getClient().getDecoupledEndpoint() != null) {
0681:                    setUpDecoupledDestination();
0682:                }
0683:                return decoupledDestination;
0684:            }
0685:
0686:            /**
0687:             * Close the conduit
0688:             */
0689:            public void close() {
0690:                if (defaultEndpointURL != null) {
0691:                    try {
0692:                        URLConnection connect = defaultEndpointURL
0693:                                .openConnection();
0694:                        if (connect instanceof  HttpURLConnection) {
0695:                            ((HttpURLConnection) connect).disconnect();
0696:                        }
0697:                    } catch (IOException ex) {
0698:                        //ignore
0699:                    }
0700:                    //defaultEndpointURL = null;
0701:                }
0702:
0703:                // in decoupled case, close response Destination if reference count
0704:                // hits zero
0705:                //
0706:                if (decoupledDestination != null) {
0707:                    releaseDecoupledDestination();
0708:                }
0709:            }
0710:
0711:            /**
0712:             * @return the default target address
0713:             */
0714:            protected String getAddress() throws MalformedURLException {
0715:                return defaultEndpointAddress;
0716:            }
0717:
0718:            /**
0719:             * @return the default target URL
0720:             */
0721:            protected synchronized URL getURL() throws MalformedURLException {
0722:                return getURL(true);
0723:            }
0724:
0725:            /**
0726:             * @param createOnDemand create URL on-demand if null
0727:             * @return the default target URL
0728:             */
0729:            protected synchronized URL getURL(boolean createOnDemand)
0730:                    throws MalformedURLException {
0731:                if (defaultEndpointURL == null && createOnDemand) {
0732:                    defaultEndpointURL = new URL(defaultEndpointAddress);
0733:                }
0734:                return defaultEndpointURL;
0735:            }
0736:
0737:            /**
0738:             * While extracting the Message.PROTOCOL_HEADERS property from the Message,
0739:             * this call ensures that the Message.PROTOCOL_HEADERS property is
0740:             * set on the Message. If it is not set, an empty map is placed there, and
0741:             * then returned.
0742:             * 
0743:             * @param message The outbound message
0744:             * @return The PROTOCOL_HEADERS map
0745:             */
0746:            private Map<String, List<String>> getSetProtocolHeaders(
0747:                    Message message) {
0748:                Map<String, List<String>> headers = CastUtils
0749:                        .cast((Map<?, ?>) message.get(Message.PROTOCOL_HEADERS));
0750:                if (null == headers) {
0751:                    headers = new HashMap<String, List<String>>();
0752:                    message.put(Message.PROTOCOL_HEADERS, headers);
0753:                }
0754:                return headers;
0755:            }
0756:
0757:            /* PMD for non-use!
0758:            private void printHeaders(URLConnection connection) {
0759:                int i = 0;
0760:                String k = connection.getHeaderFieldKey(i);
0761:                String h = connection.getHeaderField(i);
0762:                while (h != null) {
0763:                    System.out.println(k + ": " + h);
0764:                    k = connection.getHeaderFieldKey(++i);
0765:                    h = connection.getHeaderField(i);
0766:                }           
0767:            }
0768:             */
0769:
0770:            /**
0771:             * This procedure sets the URLConnection request properties
0772:             * from the PROTOCOL_HEADERS in the message.
0773:             */
0774:            private void transferProtocolHeadersToURLConnection(
0775:                    Message message, URLConnection connection) {
0776:                Map<String, List<String>> headers = getSetProtocolHeaders(message);
0777:                for (String header : headers.keySet()) {
0778:                    List<String> headerList = headers.get(header);
0779:                    for (String value : headerList) {
0780:                        connection.addRequestProperty(header, value);
0781:                    }
0782:                }
0783:            }
0784:
0785:            /**
0786:             * This procedure logs the PROTOCOL_HEADERS from the 
0787:             * Message at the specified logging level.
0788:             * 
0789:             * @param level   The Logging Level.
0790:             * @param message The Message.
0791:             */
0792:            private void logProtocolHeaders(Level level, Message message) {
0793:                Map<String, List<String>> headers = getSetProtocolHeaders(message);
0794:                for (String header : headers.keySet()) {
0795:                    List<String> headerList = headers.get(header);
0796:                    for (String value : headerList) {
0797:                        LOG.log(level, header + ": " + value);
0798:                    }
0799:                }
0800:            }
0801:
0802:            /**
0803:             * Put the headers from Message.PROTOCOL_HEADERS headers into the URL
0804:             * connection.
0805:             * Note, this does not mean they immediately get written to the output
0806:             * stream or the wire. They just just get set on the HTTP request.
0807:             * 
0808:             * @param message The outbound message.
0809:             * @throws IOException
0810:             */
0811:            private void setURLRequestHeaders(Message message)
0812:                    throws IOException {
0813:                HttpURLConnection connection = (HttpURLConnection) message
0814:                        .get(KEY_HTTP_CONNECTION);
0815:
0816:                String ct = (String) message.get(Message.CONTENT_TYPE);
0817:                String enc = (String) message.get(Message.ENCODING);
0818:
0819:                if (null != ct) {
0820:                    if (enc != null && ct.indexOf("charset=") == -1) {
0821:                        ct = ct + "; charset=" + enc;
0822:                    }
0823:                    connection.setRequestProperty(
0824:                            HttpHeaderHelper.CONTENT_TYPE, ct);
0825:                } else if (enc != null) {
0826:                    connection.setRequestProperty(
0827:                            HttpHeaderHelper.CONTENT_TYPE, "text/xml; charset="
0828:                                    + enc);
0829:                } else {
0830:                    connection.setRequestProperty(
0831:                            HttpHeaderHelper.CONTENT_TYPE, "text/xml");
0832:                }
0833:
0834:                if (LOG.isLoggable(Level.FINE)) {
0835:                    LOG.fine("Sending " + connection.getRequestMethod()
0836:                            + " Message with Headers to " + connection.getURL()
0837:                            + " Conduit :" + getConduitName());
0838:                    logProtocolHeaders(Level.FINE, message);
0839:                }
0840:
0841:                transferProtocolHeadersToURLConnection(message, connection);
0842:
0843:            }
0844:
0845:            /**
0846:             * Set up the decoupled Destination if necessary.
0847:             */
0848:            private void setUpDecoupledDestination() {
0849:                EndpointReferenceType reference = EndpointReferenceUtils
0850:                        .getEndpointReference(getClient()
0851:                                .getDecoupledEndpoint());
0852:                if (reference != null) {
0853:                    String decoupledAddress = reference.getAddress().getValue();
0854:                    LOG
0855:                            .info("creating decoupled endpoint: "
0856:                                    + decoupledAddress);
0857:                    try {
0858:                        decoupledDestination = getDestination(decoupledAddress);
0859:                        duplicateDecoupledDestination();
0860:                    } catch (Exception e) {
0861:                        // REVISIT move message to localizable Messages.properties
0862:                        LOG.log(Level.WARNING,
0863:                                "decoupled endpoint creation failed: ", e);
0864:                    }
0865:                }
0866:            }
0867:
0868:            /**
0869:             * @param address the address
0870:             * @return a Destination for the address
0871:             */
0872:            private Destination getDestination(String address)
0873:                    throws IOException {
0874:                Destination destination = null;
0875:                DestinationFactoryManager factoryManager = bus
0876:                        .getExtension(DestinationFactoryManager.class);
0877:                DestinationFactory factory = factoryManager
0878:                        .getDestinationFactoryForUri(address);
0879:                if (factory != null) {
0880:                    EndpointInfo ei = new EndpointInfo();
0881:                    ei.setAddress(address);
0882:                    destination = factory.getDestination(ei);
0883:                    decoupledObserver = new InterposedMessageObserver();
0884:                    destination.setMessageObserver(decoupledObserver);
0885:                }
0886:                return destination;
0887:            }
0888:
0889:            /**
0890:             * @return the decoupled observer
0891:             */
0892:            protected MessageObserver getDecoupledObserver() {
0893:                return decoupledObserver;
0894:            }
0895:
0896:            private synchronized void duplicateDecoupledDestination() {
0897:                decoupledDestinationRefCount++;
0898:            }
0899:
0900:            private synchronized void releaseDecoupledDestination() {
0901:                if (--decoupledDestinationRefCount == 0) {
0902:                    LOG.log(Level.INFO, "shutting down decoupled destination");
0903:                    decoupledDestination.shutdown();
0904:                }
0905:            }
0906:
0907:            /**
0908:             * This predicate returns true iff the exchange indicates 
0909:             * a oneway MEP.
0910:             * 
0911:             * @param exchange The exchange in question
0912:             */
0913:            private boolean isOneway(Exchange exchange) {
0914:                return exchange != null && exchange.isOneWay();
0915:            }
0916:
0917:            /**
0918:             * @return true if expecting a decoupled response
0919:             */
0920:            private boolean isDecoupled() {
0921:                return decoupledDestination != null;
0922:            }
0923:
0924:            /**
0925:             * Get an input stream containing the partial response if one is present.
0926:             * 
0927:             * @param connection the connection in question
0928:             * @param responseCode the response code
0929:             * @return an input stream if a partial response is pending on the connection 
0930:             */
0931:            protected static InputStream getPartialResponse(
0932:                    HttpURLConnection connection, int responseCode)
0933:                    throws IOException {
0934:                InputStream in = null;
0935:                if (responseCode == HttpURLConnection.HTTP_ACCEPTED
0936:                        || responseCode == HttpURLConnection.HTTP_OK) {
0937:                    if (connection.getContentLength() > 0) {
0938:                        in = connection.getInputStream();
0939:                    } else if (hasChunkedResponse(connection)
0940:                            || hasEofTerminatedResponse(connection)) {
0941:                        // ensure chunked or EOF-terminated response is non-empty
0942:                        in = getNonEmptyContent(connection);
0943:                    }
0944:                }
0945:                return in;
0946:            }
0947:
0948:            /**
0949:             * @param connection the given HttpURLConnection
0950:             * @return true iff the connection has a chunked response pending
0951:             */
0952:            private static boolean hasChunkedResponse(
0953:                    HttpURLConnection connection) {
0954:                return HttpHeaderHelper.CHUNKED.equalsIgnoreCase(connection
0955:                        .getHeaderField(HttpHeaderHelper.TRANSFER_ENCODING));
0956:            }
0957:
0958:            /**
0959:             * @param connection the given HttpURLConnection
0960:             * @return true iff the connection has a chunked response pending
0961:             */
0962:            private static boolean hasEofTerminatedResponse(
0963:                    HttpURLConnection connection) {
0964:                return HttpHeaderHelper.CLOSE.equalsIgnoreCase(connection
0965:                        .getHeaderField(HttpHeaderHelper.CONNECTION));
0966:            }
0967:
0968:            /**
0969:             * @param connection the given HttpURLConnection
0970:             * @return an input stream containing the response content if non-empty
0971:             */
0972:            private static InputStream getNonEmptyContent(
0973:                    HttpURLConnection connection) {
0974:                InputStream in = null;
0975:                try {
0976:                    PushbackInputStream pin = new PushbackInputStream(
0977:                            connection.getInputStream());
0978:                    int c = pin.read();
0979:                    if (c != -1) {
0980:                        pin.unread((byte) c);
0981:                        in = pin;
0982:                    }
0983:                } catch (IOException ioe) {
0984:                    // ignore
0985:                }
0986:                return in;
0987:            }
0988:
0989:            /**
0990:             * This method returns the Proxy server should it be set on the 
0991:             * Client Side Policy.
0992:             * 
0993:             * @return The proxy server or null, if not set.
0994:             */
0995:            private Proxy getProxy(HTTPClientPolicy policy) {
0996:                Proxy proxy = null;
0997:                if (policy != null && policy.isSetProxyServer()) {
0998:                    proxy = new Proxy(Proxy.Type.valueOf(policy
0999:                            .getProxyServerType().toString()),
1000:                            new InetSocketAddress(policy.getProxyServer(),
1001:                                    policy.getProxyServerPort()));
1002:                }
1003:                return proxy;
1004:            }
1005:
1006:            /**
1007:             * This call places HTTP Header strings into the headers that are relevant
1008:             * to the Authorization policies that are set on this conduit by 
1009:             * configuration.
1010:             * <p> 
1011:             * An AuthorizationPolicy may also be set on the message. If so, those 
1012:             * policies are merged. A user name or password set on the messsage 
1013:             * overrides settings in the AuthorizationPolicy is retrieved from the
1014:             * configuration.
1015:             * <p>
1016:             * The precedence is as follows:
1017:             * 1. AuthorizationPolicy that is set on the Message, if exists.
1018:             * 2. Preemptive UserPass from BasicAuthSupplier, if exists.
1019:             * 3. AuthorizationPolicy set/configured for conduit.
1020:             * 
1021:             * REVISIT: Since the AuthorizationPolicy is set on the message by class, then
1022:             * how does one override the ProxyAuthorizationPolicy which is the same 
1023:             * type?
1024:             * 
1025:             * @param message
1026:             * @param headers
1027:             */
1028:            private void setHeadersByAuthorizationPolicy(Message message,
1029:                    URL url, Map<String, List<String>> headers) {
1030:                AuthorizationPolicy authPolicy = getAuthorization();
1031:
1032:                HttpBasicAuthSupplier.UserPass userpass = null;
1033:                if (basicAuthSupplier != null) {
1034:                    userpass = basicAuthSupplier.getPreemptiveUserPass(
1035:                            getConduitName(), url, message);
1036:                }
1037:
1038:                AuthorizationPolicy newPolicy = message
1039:                        .get(AuthorizationPolicy.class);
1040:                String userName = null;
1041:                String passwd = null;
1042:                if (null != newPolicy) {
1043:                    userName = newPolicy.getUserName();
1044:                    passwd = newPolicy.getPassword();
1045:                }
1046:                if (userName == null && userpass != null) {
1047:                    userName = userpass.getUserid();
1048:                    passwd = userpass.getPassword();
1049:                }
1050:                if (userName == null && authPolicy != null
1051:                        && authPolicy.isSetUserName()) {
1052:                    userName = authPolicy.getUserName();
1053:                }
1054:                if (userName != null) {
1055:                    if (passwd == null && authPolicy != null
1056:                            && authPolicy.isSetPassword()) {
1057:                        passwd = authPolicy.getPassword();
1058:                    }
1059:                    setBasicAuthHeader(userName, passwd, headers);
1060:                } else if (authPolicy != null
1061:                        && authPolicy.isSetAuthorizationType()
1062:                        && authPolicy.isSetAuthorization()) {
1063:                    String type = authPolicy.getAuthorizationType();
1064:                    type += " ";
1065:                    type += authPolicy.getAuthorization();
1066:                    headers.put("Authorization", Arrays
1067:                            .asList(new String[] { type }));
1068:                }
1069:                AuthorizationPolicy proxyAuthPolicy = getProxyAuthorization();
1070:                if (proxyAuthPolicy != null && proxyAuthPolicy.isSetUserName()) {
1071:                    userName = proxyAuthPolicy.getUserName();
1072:                    if (userName != null) {
1073:                        passwd = "";
1074:                        if (proxyAuthPolicy.isSetPassword()) {
1075:                            passwd = proxyAuthPolicy.getPassword();
1076:                        }
1077:                        setProxyBasicAuthHeader(userName, passwd, headers);
1078:                    } else if (proxyAuthPolicy.isSetAuthorizationType()
1079:                            && proxyAuthPolicy.isSetAuthorization()) {
1080:                        String type = proxyAuthPolicy.getAuthorizationType();
1081:                        type += " ";
1082:                        type += proxyAuthPolicy.getAuthorization();
1083:                        headers.put("Proxy-Authorization", Arrays
1084:                                .asList(new String[] { type }));
1085:                    }
1086:                }
1087:            }
1088:
1089:            /**
1090:             * This call places HTTP Header strings into the headers that are relevant
1091:             * to the ClientPolicy that is set on this conduit by configuration.
1092:             * 
1093:             * REVISIT: A cookie is set statically from configuration? 
1094:             */
1095:            private void setHeadersByClientPolicy(Message message,
1096:                    Map<String, List<String>> headers) {
1097:                HTTPClientPolicy policy = getClient(message);
1098:                if (policy == null) {
1099:                    return;
1100:                }
1101:                if (policy.isSetCacheControl()) {
1102:                    headers.put("Cache-Control", Arrays
1103:                            .asList(new String[] { policy.getCacheControl()
1104:                                    .value() }));
1105:                }
1106:                if (policy.isSetHost()) {
1107:                    headers.put("Host", Arrays.asList(new String[] { policy
1108:                            .getHost() }));
1109:                }
1110:                if (policy.isSetConnection()) {
1111:                    headers.put("Connection", Arrays
1112:                            .asList(new String[] { policy.getConnection()
1113:                                    .value() }));
1114:                }
1115:                if (policy.isSetAccept()) {
1116:                    headers.put("Accept", Arrays.asList(new String[] { policy
1117:                            .getAccept() }));
1118:                } else {
1119:                    headers.put("Accept", Arrays.asList(new String[] { "*" }));
1120:                }
1121:                if (policy.isSetAcceptEncoding()) {
1122:                    headers.put("Accept-Encoding",
1123:                            Arrays.asList(new String[] { policy
1124:                                    .getAcceptEncoding() }));
1125:                }
1126:                if (policy.isSetAcceptLanguage()) {
1127:                    headers.put("Accept-Language",
1128:                            Arrays.asList(new String[] { policy
1129:                                    .getAcceptLanguage() }));
1130:                }
1131:                if (policy.isSetContentType()) {
1132:                    headers.put(HttpHeaderHelper.CONTENT_TYPE, Arrays
1133:                            .asList(new String[] { policy.getContentType() }));
1134:                }
1135:                if (policy.isSetCookie()) {
1136:                    headers.put("Cookie", Arrays.asList(new String[] { policy
1137:                            .getCookie() }));
1138:                }
1139:                if (policy.isSetBrowserType()) {
1140:                    headers.put("BrowserType", Arrays
1141:                            .asList(new String[] { policy.getBrowserType() }));
1142:                }
1143:                if (policy.isSetReferer()) {
1144:                    headers.put("Referer", Arrays.asList(new String[] { policy
1145:                            .getReferer() }));
1146:                }
1147:            }
1148:
1149:            /**
1150:             * This call places HTTP Header strings into the headers that are relevant
1151:             * to the polices that are set on this conduit by configuration for the
1152:             * ClientPolicy and AuthorizationPolicy.
1153:             * 
1154:             * 
1155:             * @param message The outgoing message.
1156:             * @param url     The URL the message is going to.
1157:             * @param headers The headers in the outgoing message.
1158:             */
1159:            private void setHeadersByPolicy(Message message, URL url,
1160:                    Map<String, List<String>> headers) {
1161:                setHeadersByAuthorizationPolicy(message, url, headers);
1162:                setHeadersByClientPolicy(message, headers);
1163:            }
1164:
1165:            /**
1166:             * This is part of the Configurable interface which retrieves the 
1167:             * configuration from spring injection.
1168:             */
1169:            // REVISIT:What happens when the endpoint/bean name is null?
1170:            public String getBeanName() {
1171:                if (endpointInfo.getName() != null) {
1172:                    return endpointInfo.getName().toString() + ".http-conduit";
1173:                }
1174:                return null;
1175:            }
1176:
1177:            /**
1178:             * This method gets the Authorization Policy that was configured or 
1179:             * explicitly set for this HTTPConduit.
1180:             */
1181:            public AuthorizationPolicy getAuthorization() {
1182:                return authorizationPolicy;
1183:            }
1184:
1185:            /**
1186:             * This method is used to set the Authorization Policy for this conduit.
1187:             * Using this method will override any Authorization Policy set in 
1188:             * configuration.
1189:             */
1190:            @Resource
1191:            public void setAuthorization(AuthorizationPolicy authorization) {
1192:                this .authorizationPolicy = authorization;
1193:            }
1194:
1195:            public HTTPClientPolicy getClient(Message message) {
1196:                return PolicyUtils.getClient(message, clientSidePolicy);
1197:            }
1198:
1199:            /**
1200:             * This method retrieves the Client Side Policy set/configured for this
1201:             * HTTPConduit.
1202:             */
1203:            public HTTPClientPolicy getClient() {
1204:                return clientSidePolicy;
1205:            }
1206:
1207:            /**
1208:             * This method sets the Client Side Policy for this HTTPConduit. Using this
1209:             * method will override any HTTPClientPolicy set in configuration.
1210:             */
1211:            @Resource
1212:            public void setClient(HTTPClientPolicy client) {
1213:                this .clientSidePolicy = client;
1214:            }
1215:
1216:            /**
1217:             * This method retrieves the Proxy Authorization Policy for a proxy that is
1218:             * set/configured for this HTTPConduit.
1219:             */
1220:            public ProxyAuthorizationPolicy getProxyAuthorization() {
1221:                return proxyAuthorizationPolicy;
1222:            }
1223:
1224:            /**
1225:             * This method sets the Proxy Authorization Policy for a specified proxy. 
1226:             * Using this method overrides any Authorization Policy for the proxy 
1227:             * that is set in the configuration.
1228:             */
1229:            @Resource
1230:            public void setProxyAuthorization(
1231:                    ProxyAuthorizationPolicy proxyAuthorization) {
1232:                this .proxyAuthorizationPolicy = proxyAuthorization;
1233:            }
1234:
1235:            /**
1236:             * This method returns the TLS Client Parameters that is set/configured
1237:             * for this HTTPConduit.
1238:             */
1239:            public TLSClientParameters getTlsClientParameters() {
1240:                return tlsClientParameters;
1241:            }
1242:
1243:            /**
1244:             * This method sets the TLS Client Parameters for this HTTPConduit.
1245:             * Using this method overrides any TLS Client Parameters that is configured
1246:             * for this HTTPConduit.
1247:             */
1248:            @Resource
1249:            public void setTlsClientParameters(TLSClientParameters params) {
1250:                this .tlsClientParameters = params;
1251:                if (this .tlsClientParameters != null) {
1252:                    if (LOG.isLoggable(Level.FINE)) {
1253:                        LOG.log(Level.FINE, "Conduit '" + getConduitName()
1254:                                + "' has been (re) configured for TLS "
1255:                                + "keyManagers "
1256:                                + tlsClientParameters.getKeyManagers()
1257:                                + "trustManagers "
1258:                                + tlsClientParameters.getTrustManagers()
1259:                                + "secureRandom "
1260:                                + tlsClientParameters.getSecureRandom());
1261:                    }
1262:                } else {
1263:                    if (LOG.isLoggable(Level.FINE)) {
1264:                        LOG.log(Level.FINE, "Conduit '" + getConduitName()
1265:                                + "' has been (re)configured for plain http.");
1266:                    }
1267:                }
1268:                // If this is called after the HTTPTransportFactory called 
1269:                // finalizeConfig, we need to update the connection factory.
1270:                if (configFinalized) {
1271:                    retrieveConnectionFactory();
1272:                }
1273:            }
1274:
1275:            /**
1276:             * This method gets the Trust Decider that was set/configured for this 
1277:             * HTTPConduit.
1278:             * @return The Message Trust Decider or null.
1279:             */
1280:            public MessageTrustDecider getTrustDecider() {
1281:                return this .trustDecider;
1282:            }
1283:
1284:            /**
1285:             * This method sets the Trust Decider for this HTTP Conduit.
1286:             * Using this method overrides any trust decider configured for this 
1287:             * HTTPConduit.
1288:             */
1289:            @Resource
1290:            public void setTrustDecider(MessageTrustDecider decider) {
1291:                this .trustDecider = decider;
1292:            }
1293:
1294:            /**
1295:             * This method gets the Basic Auth Supplier that was set/configured for this 
1296:             * HTTPConduit.
1297:             * @return The Basic Auth Supplier or null.
1298:             */
1299:            public HttpBasicAuthSupplier getBasicAuthSupplier() {
1300:                return this .basicAuthSupplier;
1301:            }
1302:
1303:            /**
1304:             * This method sets the Trust Decider for this HTTP Conduit.
1305:             * Using this method overrides any trust decider configured for this 
1306:             * HTTPConduit.
1307:             */
1308:            @Resource
1309:            public void setBasicAuthSupplier(HttpBasicAuthSupplier supplier) {
1310:                this .basicAuthSupplier = supplier;
1311:            }
1312:
1313:            /**
1314:             * This function processes any retransmits at the direction of redirections
1315:             * or "unauthorized" responses.
1316:             * <p>
1317:             * If the request was not retransmitted, it returns the given connection. 
1318:             * If the request was retransmitted, it returns the new connection on
1319:             * which the request was sent.
1320:             * 
1321:             * @param connection   The active URL connection.
1322:             * @param message      The outgoing message.
1323:             * @param cachedStream The cached request.
1324:             * @return
1325:             * @throws IOException
1326:             */
1327:            private HttpURLConnection processRetransmit(
1328:                    HttpURLConnection connection, Message message,
1329:                    CacheAndWriteOutputStream cachedStream) throws IOException {
1330:
1331:                int responseCode = connection.getResponseCode();
1332:
1333:                // Process Redirects first.
1334:                switch (responseCode) {
1335:                case HttpURLConnection.HTTP_MOVED_PERM:
1336:                case HttpURLConnection.HTTP_MOVED_TEMP:
1337:                    connection = redirectRetransmit(connection, message,
1338:                            cachedStream);
1339:                    break;
1340:                case HttpURLConnection.HTTP_UNAUTHORIZED:
1341:                    connection = authorizationRetransmit(connection, message,
1342:                            cachedStream);
1343:                    break;
1344:                default:
1345:                    break;
1346:                }
1347:                return connection;
1348:            }
1349:
1350:            /**
1351:             * This method performs a redirection retransmit in response to
1352:             * a 302 or 305 response code.
1353:             * 
1354:             * @param connection   The active URL connection
1355:             * @param message      The outbound message.
1356:             * @param cachedStream The cached request.
1357:             * @return This method returns the new HttpURLConnection if
1358:             *         redirected. If it cannot be redirected for some reason
1359:             *         the same connection is returned.
1360:             *         
1361:             * @throws IOException
1362:             */
1363:            private HttpURLConnection redirectRetransmit(
1364:                    HttpURLConnection connection, Message message,
1365:                    CacheAndWriteOutputStream cachedStream) throws IOException {
1366:
1367:                // If we are not redirecting by policy, then we don't.
1368:                if (!getClient().isAutoRedirect()) {
1369:                    return connection;
1370:                }
1371:
1372:                // We keep track of the redirections for redirect loop protection.
1373:                Set<String> visitedURLs = getSetVisitedURLs(message);
1374:
1375:                String lastURL = connection.getURL().toString();
1376:                visitedURLs.add(lastURL);
1377:
1378:                String newURL = extractLocation(connection.getHeaderFields());
1379:                if (newURL != null) {
1380:                    // See if we are being redirected in a loop as best we can,
1381:                    // using string equality on URL.
1382:                    if (visitedURLs.contains(newURL)) {
1383:                        // We are in a redirect loop; -- bail
1384:                        if (LOG.isLoggable(Level.INFO)) {
1385:                            LOG.log(Level.INFO,
1386:                                    "Redirect loop detected on Conduit \""
1387:                                            + getConduitName() + "\" on '"
1388:                                            + newURL + "'");
1389:                        }
1390:                        return connection;
1391:                    }
1392:                    // We are going to redirect.
1393:                    // Remove any Server Authentication Information for the previous
1394:                    // URL.
1395:                    Map<String, List<String>> headers = getSetProtocolHeaders(message);
1396:                    headers.remove("Authorization");
1397:                    headers.remove("Proxy-Authorization");
1398:
1399:                    URL url = new URL(newURL);
1400:
1401:                    // If user configured this Conduit with preemptive authorization
1402:                    // it is meant to make it to the end. (Too bad that information
1403:                    // went to every URL along the way, but that's what the user 
1404:                    // wants!
1405:                    // TODO: Make this issue a security release note.
1406:                    setHeadersByAuthorizationPolicy(message, url, headers);
1407:
1408:                    connection = retransmit(connection, url, message,
1409:                            cachedStream);
1410:                }
1411:                return connection;
1412:            }
1413:
1414:            /**
1415:             * This function gets the Set of URLs on the message that is used to 
1416:             * keep track of the URLs that were used in getting authorization 
1417:             * information.
1418:             *
1419:             * @param message The message where the Set of URLs is stored.
1420:             * @return The modifiable set of URLs that were visited.
1421:             */
1422:            private Set<String> getSetAuthoriationURLs(Message message) {
1423:                @SuppressWarnings("unchecked")
1424:                Set<String> authURLs = (Set<String>) message.get(KEY_AUTH_URLS);
1425:                if (authURLs == null) {
1426:                    authURLs = new HashSet<String>();
1427:                    message.put(KEY_AUTH_URLS, authURLs);
1428:                }
1429:                return authURLs;
1430:            }
1431:
1432:            /**
1433:             * This function get the set of URLs on the message that is used to keep
1434:             * track of the URLs that were visited in redirects.
1435:             * 
1436:             * If it is not set on the message, an new empty set is stored.
1437:             * @param message The message where the Set is stored.
1438:             * @return The modifiable set of URLs that were visited.
1439:             */
1440:            private Set<String> getSetVisitedURLs(Message message) {
1441:                @SuppressWarnings("unchecked")
1442:                Set<String> visitedURLs = (Set<String>) message
1443:                        .get(KEY_VISITED_URLS);
1444:                if (visitedURLs == null) {
1445:                    visitedURLs = new HashSet<String>();
1446:                    message.put(KEY_VISITED_URLS, visitedURLs);
1447:                }
1448:                return visitedURLs;
1449:            }
1450:
1451:            /**
1452:             * This method performs a retransmit for authorization information.
1453:             * 
1454:             * @param connection The currently active connection.
1455:             * @param message The outbound message.
1456:             * @param cachedStream The cached request.
1457:             * @return A new connection if retransmitted. If not retransmitted
1458:             *         then this method returns the same connection.
1459:             * @throws IOException
1460:             */
1461:            private HttpURLConnection authorizationRetransmit(
1462:                    HttpURLConnection connection, Message message,
1463:                    CacheAndWriteOutputStream cachedStream) throws IOException {
1464:
1465:                // If we don't have a dynamic supply of user pass, then
1466:                // we don't retransmit. We just die with a Http 401 response.
1467:                if (basicAuthSupplier == null) {
1468:                    return connection;
1469:                }
1470:
1471:                URL currentURL = connection.getURL();
1472:
1473:                String realm = extractAuthorizationRealm(connection
1474:                        .getHeaderFields());
1475:
1476:                Set<String> authURLs = getSetAuthoriationURLs(message);
1477:
1478:                // If we have been here (URL & Realm) before for this particular message
1479:                // retransmit, it means we have already supplied information
1480:                // which must have been wrong, or we wouldn't be here again.
1481:                // Otherwise, the server may be 401 looping us around the realms.
1482:                if (authURLs.contains(currentURL.toString() + realm)) {
1483:
1484:                    if (LOG.isLoggable(Level.INFO)) {
1485:                        LOG.log(Level.INFO,
1486:                                "Authorization loop detected on Conduit \""
1487:                                        + getConduitName() + "\" on URL \""
1488:                                        + "\" with realm \"" + realm + "\"");
1489:                    }
1490:
1491:                    return connection;
1492:                }
1493:
1494:                HttpBasicAuthSupplier.UserPass up = basicAuthSupplier
1495:                        .getUserPassForRealm(getConduitName(), currentURL,
1496:                                message, realm);
1497:
1498:                // No user pass combination. We give up.
1499:                if (up == null) {
1500:                    return connection;
1501:                }
1502:
1503:                // Register that we have been here before we go.
1504:                authURLs.add(currentURL.toString() + realm);
1505:
1506:                Map<String, List<String>> headers = getSetProtocolHeaders(message);
1507:
1508:                setBasicAuthHeader(up.getUserid(), up.getPassword(), headers);
1509:
1510:                return retransmit(connection, currentURL, message, cachedStream);
1511:            }
1512:
1513:            /**
1514:             * This method retransmits the request.
1515:             * 
1516:             * @param connection The currently active connection.
1517:             * @param newURL     The newURL to connection to.
1518:             * @param message    The outbound message.
1519:             * @param stream     The cached request.
1520:             * @return           This function returns a new connection if
1521:             *                   retransmitted, otherwise it returns the given
1522:             *                   connection.
1523:             *                   
1524:             * @throws IOException
1525:             */
1526:            private HttpURLConnection retransmit(HttpURLConnection connection,
1527:                    URL newURL, Message message,
1528:                    CacheAndWriteOutputStream stream) throws IOException {
1529:
1530:                // Disconnect the old, and in with the new.
1531:                connection.disconnect();
1532:
1533:                connection = connectionFactory.createConnection(
1534:                        getProxy(clientSidePolicy), newURL);
1535:
1536:                connection.setDoOutput(true);
1537:                // TODO: using Message context to deceided HTTP send properties        
1538:                connection.setConnectTimeout((int) getClient()
1539:                        .getConnectionTimeout());
1540:                connection
1541:                        .setReadTimeout((int) getClient().getReceiveTimeout());
1542:                connection.setUseCaches(false);
1543:                connection.setInstanceFollowRedirects(false);
1544:
1545:                // If the HTTP_REQUEST_METHOD is not set, the default is "POST".
1546:                String httpRequestMethod = (String) message
1547:                        .get(Message.HTTP_REQUEST_METHOD);
1548:
1549:                if (null != httpRequestMethod) {
1550:                    connection.setRequestMethod(httpRequestMethod);
1551:                } else {
1552:                    connection.setRequestMethod("POST");
1553:                }
1554:                message.put(KEY_HTTP_CONNECTION, connection);
1555:
1556:                // Need to set the headers before the trust decision
1557:                // because they are set before the connect().
1558:                setURLRequestHeaders(message);
1559:
1560:                //
1561:                // This point is where the trust decision is made because the
1562:                // Sun implementation of URLConnection will not let us 
1563:                // set/addRequestProperty after a connect() call, and 
1564:                // makeTrustDecision needs to make a connect() call to
1565:                // make sure the proper information is available.
1566:                // 
1567:                makeTrustDecision(message);
1568:
1569:                // If this is a GET method we must not touch the output
1570:                // stream as this automagically turns the request into a POST.
1571:                if (connection.getRequestMethod().equals("GET")) {
1572:                    return connection;
1573:                }
1574:
1575:                // Trust is okay, write the cached request.
1576:                OutputStream out = connection.getOutputStream();
1577:
1578:                CacheAndWriteOutputStream.copyStream(stream.getInputStream(),
1579:                        out, 2048);
1580:                out.close();
1581:
1582:                if (LOG.isLoggable(Level.FINE)) {
1583:                    StringBuffer sbuf = new StringBuffer();
1584:                    StringBufferOutputStream sout = new StringBufferOutputStream(
1585:                            sbuf);
1586:                    CacheAndWriteOutputStream.copyStream(stream
1587:                            .getInputStream(), sout, 2048);
1588:                    sout.close();
1589:
1590:                    LOG.fine("Conduit \"" + getConduitName()
1591:                            + "\" Retransmit message to: "
1592:                            + connection.getURL() + ": " + sbuf);
1593:                }
1594:                return connection;
1595:            }
1596:
1597:            /**
1598:             * This function extracts the authorization realm from the 
1599:             * "WWW-Authenticate" Http response header.
1600:             * 
1601:             * @param headers The Http Response Headers
1602:             * @return The realm, or null if it is non-existent.
1603:             */
1604:            private String extractAuthorizationRealm(
1605:                    Map<String, List<String>> headers) {
1606:                List<String> auth = headers.get("WWW-Authenticate");
1607:                if (auth != null) {
1608:                    for (String a : auth) {
1609:                        if (a.startsWith("Basic realm=")) {
1610:                            return a.substring(a.indexOf("=") + 1);
1611:                        }
1612:                    }
1613:                }
1614:                return null;
1615:            }
1616:
1617:            /**
1618:             * This method extracts the value of the "Location" Http
1619:             * Response header.
1620:             * 
1621:             * @param headers The Http response headers.
1622:             * @return The value of the "Location" header, null if non-existent.
1623:             */
1624:            private String extractLocation(Map<String, List<String>> headers) {
1625:                List<String> locs = headers.get("Location");
1626:                if (locs != null && locs.size() > 0) {
1627:                    return locs.get(0);
1628:                }
1629:                return null;
1630:            }
1631:
1632:            /**
1633:             * This procedure sets the "Authorization" header with the 
1634:             * BasicAuth token, which is Base64 encoded.
1635:             * 
1636:             * @param userid   The user's id, which cannot be null.
1637:             * @param password The password, it may be null.
1638:             * 
1639:             * @param headers  The headers map that gets the "Authorization" header set.
1640:             */
1641:            private void setBasicAuthHeader(String userid, String password,
1642:                    Map<String, List<String>> headers) {
1643:                String userpass = userid;
1644:
1645:                userpass += ":";
1646:                if (password != null) {
1647:                    userpass += password;
1648:                }
1649:                String token = Base64Utility.encode(userpass.getBytes());
1650:                headers.put("Authorization", Arrays
1651:                        .asList(new String[] { "Basic " + token }));
1652:            }
1653:
1654:            /**
1655:             * This procedure sets the "ProxyAuthorization" header with the 
1656:             * BasicAuth token, which is Base64 encoded.
1657:             * 
1658:             * @param userid   The user's id, which cannot be null.
1659:             * @param password The password, it may be null.
1660:             * 
1661:             * @param headers The headers map that gets the "Proxy-Authorization" 
1662:             *                header set.
1663:             */
1664:            private void setProxyBasicAuthHeader(String userid,
1665:                    String password, Map<String, List<String>> headers) {
1666:                String userpass = userid;
1667:
1668:                userpass += ":";
1669:                if (password != null) {
1670:                    userpass += password;
1671:                }
1672:                String token = Base64Utility.encode(userpass.getBytes());
1673:                headers.put("Proxy-Authorization", Arrays
1674:                        .asList(new String[] { "Basic " + token }));
1675:            }
1676:
1677:            /**
1678:             * Wrapper output stream responsible for flushing headers and handling
1679:             * the incoming HTTP-level response (not necessarily the MEP response).
1680:             */
1681:            private class WrappedOutputStream extends
1682:                    AbstractWrappedOutputStream {
1683:                /**
1684:                 * This field contains the currently active connection.
1685:                 */
1686:                private HttpURLConnection connection;
1687:
1688:                /**
1689:                 * This boolean is true if the request must be cached.
1690:                 */
1691:                private boolean cachingForRetransmision;
1692:
1693:                /**
1694:                 * If we are going to be chunking, we won't flush till close which causes
1695:                 * new chunks, small network packets, etc..
1696:                 */
1697:                private final boolean chunking;
1698:
1699:                /**
1700:                 * This field contains the output stream with which we cache
1701:                 * the request. It maybe null if we are not caching.
1702:                 */
1703:                private CacheAndWriteOutputStream cachedStream;
1704:
1705:                private Message outMessage;
1706:
1707:                WrappedOutputStream(Message m, HttpURLConnection c,
1708:                        boolean possibleRetransmit, boolean isChunking) {
1709:                    super ();
1710:                    this .outMessage = m;
1711:                    connection = c;
1712:                    cachingForRetransmision = possibleRetransmit;
1713:                    chunking = isChunking;
1714:                }
1715:
1716:                /**
1717:                 * Perform any actions required on stream flush (freeze headers,
1718:                 * reset output stream ... etc.)
1719:                 */
1720:                @Override
1721:                protected void onFirstWrite() throws IOException {
1722:                    handleHeadersTrustCaching();
1723:                }
1724:
1725:                protected void handleHeadersTrustCaching() throws IOException {
1726:                    // Need to set the headers before the trust decision
1727:                    // because they are set before the connect().
1728:                    setURLRequestHeaders(outMessage);
1729:
1730:                    //
1731:                    // This point is where the trust decision is made because the
1732:                    // Sun implementation of URLConnection will not let us 
1733:                    // set/addRequestProperty after a connect() call, and 
1734:                    // makeTrustDecision needs to make a connect() call to
1735:                    // make sure the proper information is available.
1736:                    // 
1737:                    makeTrustDecision(outMessage);
1738:
1739:                    // Trust is okay, set up for writing the request.
1740:
1741:                    // If this is a GET method we must not touch the output
1742:                    // stream as this automagically turns the reqest into a POST.
1743:                    if (connection.getRequestMethod().equals("GET")) {
1744:                        return;
1745:                    }
1746:
1747:                    // If we need to cache for retransmission, store data in a
1748:                    // CacheAndWriteOutputStream. Otherwise write directly to the output stream.
1749:                    if (cachingForRetransmision) {
1750:                        cachedStream = new CacheAndWriteOutputStream(connection
1751:                                .getOutputStream());
1752:                        wrappedStream = cachedStream;
1753:                    } else {
1754:                        wrappedStream = connection.getOutputStream();
1755:                    }
1756:                }
1757:
1758:                public void flush() throws IOException {
1759:                    if (!chunking) {
1760:                        super .flush();
1761:                    }
1762:                }
1763:
1764:                /**
1765:                 * Perform any actions required on stream closure (handle response etc.)
1766:                 */
1767:                public void close() throws IOException {
1768:                    if (!written) {
1769:                        handleHeadersTrustCaching();
1770:                    }
1771:                    super .flush();
1772:                    super .close();
1773:                    handleResponse();
1774:                }
1775:
1776:                /**
1777:                 * This procedure handles all retransmits, if any.
1778:                 *
1779:                 * @throws IOException
1780:                 */
1781:                private void handleRetransmits() throws IOException {
1782:                    // If we have a cachedStream, we are caching the request.
1783:                    if (cachedStream != null) {
1784:
1785:                        if (LOG.isLoggable(Level.FINE)) {
1786:                            StringBuffer sbuf = new StringBuffer();
1787:                            StringBufferOutputStream sout = new StringBufferOutputStream(
1788:                                    sbuf);
1789:                            IOUtils.copy(cachedStream.getInputStream(), sout,
1790:                                    2048);
1791:                            sout.close();
1792:
1793:                            LOG.fine("Conduit \"" + getConduitName()
1794:                                    + "\" Transmit cached message to: "
1795:                                    + connection.getURL() + ": " + sbuf);
1796:                        }
1797:
1798:                        HttpURLConnection oldcon = connection;
1799:
1800:                        HTTPClientPolicy policy = getClient();
1801:
1802:                        // Default MaxRetransmits is -1 which means unlimited.
1803:                        int maxRetransmits = (policy == null) ? -1 : policy
1804:                                .getMaxRetransmits();
1805:
1806:                        // MaxRetransmits of zero means zero.
1807:                        if (maxRetransmits == 0) {
1808:                            return;
1809:                        }
1810:
1811:                        int nretransmits = 0;
1812:
1813:                        connection = processRetransmit(connection, outMessage,
1814:                                cachedStream);
1815:
1816:                        while (connection != oldcon) {
1817:                            nretransmits++;
1818:                            oldcon = connection;
1819:
1820:                            // A negative max means unlimited.
1821:                            if (maxRetransmits < 0
1822:                                    || nretransmits < maxRetransmits) {
1823:                                connection = processRetransmit(connection,
1824:                                        outMessage, cachedStream);
1825:                            }
1826:                        }
1827:                    }
1828:                }
1829:
1830:                /**
1831:                 * This procedure is called on the close of the output stream so
1832:                 * we are ready to handle the response from the connection. 
1833:                 * We may retransmit until we finally get a response.
1834:                 * 
1835:                 * @throws IOException
1836:                 */
1837:                private void handleResponse() throws IOException {
1838:
1839:                    // Process retransmits until we fall out.
1840:                    handleRetransmits();
1841:
1842:                    int responseCode = connection.getResponseCode();
1843:
1844:                    if (LOG.isLoggable(Level.FINE)) {
1845:                        LOG.fine("Response Code: " + responseCode
1846:                                + " Conduit: " + getConduitName());
1847:                        LOG.fine("Content length: "
1848:                                + connection.getContentLength());
1849:                        Map<String, List<String>> headerFields = connection
1850:                                .getHeaderFields();
1851:                        if (null != headerFields) {
1852:                            StringBuffer buf = new StringBuffer();
1853:                            buf.append("Header fields: ");
1854:                            buf.append(System.getProperty("line.separator"));
1855:                            for (String h : headerFields.keySet()) {
1856:                                buf.append("    ");
1857:                                buf.append(h);
1858:                                buf.append(": ");
1859:                                buf.append(headerFields.get(h));
1860:                                buf
1861:                                        .append(System
1862:                                                .getProperty("line.separator"));
1863:                            }
1864:                            LOG.fine(buf.toString());
1865:                        }
1866:                    }
1867:
1868:                    if (responseCode == HttpURLConnection.HTTP_NOT_FOUND) {
1869:                        throw new IOException(connection.getResponseMessage());
1870:                    }
1871:
1872:                    Exchange exchange = outMessage.getExchange();
1873:
1874:                    InputStream in = null;
1875:                    if (isOneway(exchange) || isDecoupled()) {
1876:                        in = getPartialResponse(connection, responseCode);
1877:                        if (in == null) {
1878:                            // oneway operation or decoupled MEP without 
1879:                            // partial response
1880:                            connection.getInputStream().close();
1881:                            return;
1882:                        }
1883:                    }
1884:
1885:                    Message inMessage = new MessageImpl();
1886:                    inMessage.setExchange(exchange);
1887:
1888:                    Map<String, List<String>> headers = new HashMap<String, List<String>>();
1889:                    for (String key : connection.getHeaderFields().keySet()) {
1890:                        headers.put(HttpHeaderHelper.getHeaderKey(key),
1891:                                connection.getHeaderFields().get(key));
1892:                    }
1893:                    inMessage.put(Message.PROTOCOL_HEADERS, headers);
1894:                    inMessage.put(Message.RESPONSE_CODE, responseCode);
1895:                    inMessage.put(Message.CONTENT_TYPE, connection
1896:                            .getContentType());
1897:                    inMessage.put(Message.ENCODING, connection
1898:                            .getContentEncoding());
1899:
1900:                    if (maintainSession) {
1901:                        String cookieStr = connection
1902:                                .getHeaderField("Set-Cookie");
1903:                        if (cookieStr != null) {
1904:                            String cookies[] = cookieStr.split(";");
1905:                            for (int i = 0; i < cookies.length; i++) {
1906:                                String nameValue[] = cookies[i].split("=");
1907:                                if (nameValue[0].equals("JSESSIONID")
1908:                                        || nameValue[0].equals("jsessionid")) {
1909:                                    sessionId = nameValue[1];
1910:                                }
1911:                            }
1912:                        }
1913:                    }
1914:
1915:                    in = in == null ? connection.getErrorStream() == null ? connection
1916:                            .getInputStream()
1917:                            : connection.getErrorStream()
1918:                            : in;
1919:
1920:                    if (in == null) {
1921:                        LOG.log(Level.WARNING, "Input Stream is null!");
1922:                    }
1923:                    inMessage.setContent(InputStream.class, in);
1924:
1925:                    incomingObserver.onMessage(inMessage);
1926:                }
1927:            }
1928:
1929:            /**
1930:             * Used to set appropriate message properties, exchange etc.
1931:             * as required for an incoming decoupled response (as opposed
1932:             * what's normally set by the Destination for an incoming
1933:             * request).
1934:             */
1935:            protected class InterposedMessageObserver implements 
1936:                    MessageObserver {
1937:                /**
1938:                 * Called for an incoming message.
1939:                 * 
1940:                 * @param inMessage
1941:                 */
1942:                public void onMessage(Message inMessage) {
1943:                    // disposable exchange, swapped with real Exchange on correlation
1944:                    inMessage.setExchange(new ExchangeImpl());
1945:                    inMessage.put(DECOUPLED_CHANNEL_MESSAGE, Boolean.TRUE);
1946:                    // REVISIT: how to get response headers?
1947:                    //inMessage.put(Message.PROTOCOL_HEADERS, req.getXXX());
1948:                    getSetProtocolHeaders(inMessage);
1949:                    inMessage.put(Message.RESPONSE_CODE,
1950:                            HttpURLConnection.HTTP_OK);
1951:
1952:                    // remove server-specific properties
1953:                    inMessage.remove(AbstractHTTPDestination.HTTP_REQUEST);
1954:                    inMessage.remove(AbstractHTTPDestination.HTTP_RESPONSE);
1955:                    inMessage.remove(Message.ASYNC_POST_RESPONSE_DISPATCH);
1956:
1957:                    incomingObserver.onMessage(inMessage);
1958:                }
1959:            }
1960:
1961:            public void assertMessage(Message message) {
1962:                PolicyUtils.assertClientPolicy(message, clientSidePolicy);
1963:            }
1964:
1965:            public boolean canAssert(QName type) {
1966:                return PolicyUtils.HTTPCLIENTPOLICY_ASSERTION_QNAME
1967:                        .equals(type);
1968:            }
1969:
1970:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.