001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.transport.http;
021:
022: import org.apache.axiom.om.OMOutputFormat;
023: import org.apache.axis2.AxisFault;
024: import org.apache.axis2.Constants;
025: import org.apache.axis2.addressing.EndpointReference;
026: import org.apache.axis2.context.ConfigurationContext;
027: import org.apache.axis2.context.MessageContext;
028: import org.apache.axis2.description.Parameter;
029: import org.apache.axis2.description.TransportOutDescription;
030: import org.apache.axis2.handlers.AbstractHandler;
031: import org.apache.axis2.transport.MessageFormatter;
032: import org.apache.axis2.transport.OutTransportInfo;
033: import org.apache.axis2.transport.TransportSender;
034: import org.apache.axis2.transport.TransportUtils;
035: import org.apache.axis2.util.JavaUtils;
036: import org.apache.commons.httpclient.Header;
037: import org.apache.commons.httpclient.HttpException;
038: import org.apache.commons.httpclient.HttpMethod;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041:
042: import javax.xml.stream.FactoryConfigurationError;
043: import java.io.IOException;
044: import java.io.OutputStream;
045: import java.net.MalformedURLException;
046: import java.net.URL;
047: import java.util.Iterator;
048: import java.util.List;
049: import java.util.zip.GZIPOutputStream;
050:
051: public class CommonsHTTPTransportSender extends AbstractHandler
052: implements TransportSender {
053:
054: protected static final String PROXY_HOST_NAME = "proxy_host";
055:
056: protected static final String PROXY_PORT = "proxy_port";
057:
058: int soTimeout = HTTPConstants.DEFAULT_SO_TIMEOUT;
059:
060: /**
061: * proxydiscription
062: */
063: protected TransportOutDescription proxyOutSetting = null;
064:
065: private static final Log log = LogFactory
066: .getLog(CommonsHTTPTransportSender.class);
067:
068: protected String httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
069:
070: private boolean chunked = false;
071:
072: int connectionTimeout = HTTPConstants.DEFAULT_CONNECTION_TIMEOUT;
073:
074: public void cleanup(MessageContext msgContext) throws AxisFault {
075: HttpMethod httpMethod = (HttpMethod) msgContext
076: .getProperty(HTTPConstants.HTTP_METHOD);
077:
078: if (httpMethod != null) {
079: httpMethod.releaseConnection();
080: }
081: }
082:
083: public void init(ConfigurationContext confContext,
084: TransportOutDescription transportOut) throws AxisFault {
085:
086: // <parameter name="PROTOCOL">HTTP/1.0</parameter> or
087: // <parameter name="PROTOCOL">HTTP/1.1</parameter> is
088: // checked
089: Parameter version = transportOut
090: .getParameter(HTTPConstants.PROTOCOL_VERSION);
091: if (version != null) {
092: if (HTTPConstants.HEADER_PROTOCOL_11.equals(version
093: .getValue())) {
094: httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
095:
096: Parameter transferEncoding = transportOut
097: .getParameter(HTTPConstants.HEADER_TRANSFER_ENCODING);
098:
099: if ((transferEncoding != null)
100: && HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED
101: .equals(transferEncoding.getValue())) {
102: chunked = true;
103: }
104: } else if (HTTPConstants.HEADER_PROTOCOL_10.equals(version
105: .getValue())) {
106: httpVersion = HTTPConstants.HEADER_PROTOCOL_10;
107: } else {
108: throw new AxisFault("Parameter "
109: + HTTPConstants.PROTOCOL_VERSION
110: + " Can have values only HTTP/1.0 or HTTP/1.1");
111: }
112: }
113:
114: // Get the timeout values from the configuration
115: try {
116: Parameter tempSoTimeoutParam = transportOut
117: .getParameter(HTTPConstants.SO_TIMEOUT);
118: Parameter tempConnTimeoutParam = transportOut
119: .getParameter(HTTPConstants.CONNECTION_TIMEOUT);
120:
121: if (tempSoTimeoutParam != null) {
122: soTimeout = Integer
123: .parseInt((String) tempSoTimeoutParam
124: .getValue());
125: }
126:
127: if (tempConnTimeoutParam != null) {
128: connectionTimeout = Integer
129: .parseInt((String) tempConnTimeoutParam
130: .getValue());
131: }
132: } catch (NumberFormatException nfe) {
133:
134: // If there's a problem log it and use the default values
135: log
136: .error(
137: "Invalid timeout value format: not a number",
138: nfe);
139: }
140: }
141:
142: public void stop() {
143: // Any code that , need to invoke when sender stop
144: }
145:
146: public InvocationResponse invoke(MessageContext msgContext)
147: throws AxisFault {
148: try {
149: OMOutputFormat format = new OMOutputFormat();
150: // if (!msgContext.isDoingMTOM())
151: msgContext.setDoingMTOM(HTTPTransportUtils
152: .doWriteMTOM(msgContext));
153: msgContext.setDoingSwA(HTTPTransportUtils
154: .doWriteSwA(msgContext));
155: msgContext.setDoingREST(HTTPTransportUtils
156: .isDoingREST(msgContext));
157: format.setSOAP11(msgContext.isSOAP11());
158: format.setDoOptimize(msgContext.isDoingMTOM());
159: format.setDoingSWA(msgContext.isDoingSwA());
160: format.setCharSetEncoding(HTTPTransportUtils
161: .getCharSetEncoding(msgContext));
162:
163: Object mimeBoundaryProperty = msgContext
164: .getProperty(Constants.Configuration.MIME_BOUNDARY);
165: if (mimeBoundaryProperty != null) {
166: format.setMimeBoundary((String) mimeBoundaryProperty);
167: }
168:
169: TransportOutDescription transportOut = msgContext
170: .getConfigurationContext().getAxisConfiguration()
171: .getTransportOut(Constants.TRANSPORT_HTTP);
172:
173: //if a parameter has set been set, we will omit the SOAP action for SOAP 1.2
174: if (transportOut != null) {
175: if (!msgContext.isSOAP11()) {
176: Parameter param = transportOut
177: .getParameter(HTTPConstants.OMIT_SOAP_12_ACTION);
178: Object parameterValue = null;
179: if (param != null) {
180: parameterValue = param.getValue();
181: }
182:
183: if (parameterValue != null
184: && JavaUtils
185: .isTrueExplicitly(parameterValue)) {
186: //Check whether user has already overridden this.
187: Object propertyValue = msgContext
188: .getProperty(Constants.Configuration.DISABLE_SOAP_ACTION);
189: if (propertyValue == null
190: | !JavaUtils
191: .isFalseExplicitly(propertyValue)) {
192: msgContext
193: .setProperty(
194: Constants.Configuration.DISABLE_SOAP_ACTION,
195: Boolean.TRUE);
196: }
197: }
198: }
199: }
200:
201: // Transport URL can be different from the WSA-To. So processing
202: // that now.
203: EndpointReference epr = null;
204: String transportURL = (String) msgContext
205: .getProperty(Constants.Configuration.TRANSPORT_URL);
206:
207: if (transportURL != null) {
208: epr = new EndpointReference(transportURL);
209: } else if (msgContext.getTo() != null
210: && !msgContext.getTo().hasAnonymousAddress()) {
211: epr = msgContext.getTo();
212: }
213:
214: // Check for the REST behavior, if you desire rest behavior
215: // put a <parameter name="doREST" value="true"/> at the
216: // server.xml/client.xml file
217: // ######################################################
218: // Change this place to change the wsa:toepr
219: // epr = something
220: // ######################################################
221:
222: if (epr != null) {
223: if (!epr.hasNoneAddress()) {
224: writeMessageWithCommons(msgContext, epr, format);
225: TransportUtils.setResponseWritten(msgContext, true);
226: }
227: } else {
228: if (msgContext
229: .getProperty(MessageContext.TRANSPORT_OUT) != null) {
230: sendUsingOutputStream(msgContext, format);
231: TransportUtils.setResponseWritten(msgContext, true);
232: } else {
233: throw new AxisFault(
234: "Both the TO and MessageContext.TRANSPORT_OUT property "
235: + "are null, so nowhere to send");
236: }
237: }
238: } catch (FactoryConfigurationError e) {
239: log.debug(e);
240: throw AxisFault.makeFault(e);
241: } catch (IOException e) {
242: log.debug(e);
243: throw AxisFault.makeFault(e);
244: }
245: return InvocationResponse.CONTINUE;
246: }
247:
248: /**
249: * Send a message (which must be a response) via the OutputStream sitting in the
250: * MessageContext TRANSPORT_OUT property. Since this class is used for both requests and
251: * responses, we split the logic - this method always gets called when we're
252: * writing to the HTTP response stream, and sendUsingCommons() is used for requests.
253: *
254: * @param msgContext the active MessageContext
255: * @param format output formatter for our message
256: * @throws AxisFault if a general problem arises
257: */
258: private void sendUsingOutputStream(MessageContext msgContext,
259: OMOutputFormat format) throws AxisFault {
260: OutputStream out = (OutputStream) msgContext
261: .getProperty(MessageContext.TRANSPORT_OUT);
262:
263: // I Don't think we need this check.. Content type needs to be set in
264: // any case. (thilina)
265: // if (msgContext.isServerSide()) {
266: OutTransportInfo transportInfo = (OutTransportInfo) msgContext
267: .getProperty(Constants.OUT_TRANSPORT_INFO);
268:
269: if (transportInfo == null)
270: throw new AxisFault("No transport info in MessageContext");
271:
272: ServletBasedOutTransportInfo servletBasedOutTransportInfo = null;
273: if (transportInfo instanceof ServletBasedOutTransportInfo) {
274: servletBasedOutTransportInfo = (ServletBasedOutTransportInfo) transportInfo;
275: List customHeaders = (List) msgContext
276: .getProperty(HTTPConstants.HTTP_HEADERS);
277: if (customHeaders != null) {
278: Iterator iter = customHeaders.iterator();
279: while (iter.hasNext()) {
280: Header header = (Header) iter.next();
281: if (header != null) {
282: servletBasedOutTransportInfo.addHeader(header
283: .getName(), header.getValue());
284: }
285: }
286: }
287: }
288:
289: format.setAutoCloseWriter(true);
290:
291: MessageFormatter messageFormatter = TransportUtils
292: .getMessageFormatter(msgContext);
293: if (messageFormatter == null)
294: throw new AxisFault("No MessageFormatter in MessageContext");
295:
296: // Once we get to this point, exceptions should NOT be turned into faults and sent,
297: // because we're already sending! So catch everything and log it, but don't pass
298: // upwards.
299:
300: try {
301: transportInfo.setContentType(messageFormatter
302: .getContentType(msgContext, format,
303: findSOAPAction(msgContext)));
304:
305: Object gzip = msgContext.getOptions().getProperty(
306: HTTPConstants.MC_GZIP_RESPONSE);
307: if (gzip != null && JavaUtils.isTrueExplicitly(gzip)) {
308: if (servletBasedOutTransportInfo != null)
309: servletBasedOutTransportInfo.addHeader(
310: HTTPConstants.HEADER_CONTENT_ENCODING,
311: HTTPConstants.COMPRESSION_GZIP);
312: try {
313: out = new GZIPOutputStream(out);
314: out.write(messageFormatter.getBytes(msgContext,
315: format));
316: ((GZIPOutputStream) out).finish();
317: out.flush();
318: } catch (IOException e) {
319: throw new AxisFault("Could not compress response");
320: }
321: } else {
322: messageFormatter
323: .writeTo(msgContext, format, out, false);
324: }
325: } catch (AxisFault axisFault) {
326: log.error(axisFault.getMessage(), axisFault);
327: }
328: }
329:
330: private void writeMessageWithCommons(MessageContext messageContext,
331: EndpointReference toEPR, OMOutputFormat format)
332: throws AxisFault {
333: try {
334: URL url = new URL(toEPR.getAddress());
335:
336: // select the Message Sender depending on the REST status
337: AbstractHTTPSender sender;
338:
339: sender = new HTTPSender();
340:
341: if (messageContext.getProperty(HTTPConstants.CHUNKED) != null) {
342: chunked = JavaUtils.isTrueExplicitly(messageContext
343: .getProperty(HTTPConstants.CHUNKED));
344: }
345:
346: if (messageContext
347: .getProperty(HTTPConstants.HTTP_PROTOCOL_VERSION) != null) {
348: httpVersion = (String) messageContext
349: .getProperty(HTTPConstants.HTTP_PROTOCOL_VERSION);
350: }
351: // Following order needed to be preserved because,
352: // HTTP/1.0 does not support chunk encoding
353: sender.setChunked(chunked);
354: sender.setHttpVersion(httpVersion);
355: sender.setFormat(format);
356:
357: sender.send(messageContext, url,
358: findSOAPAction(messageContext));
359: } catch (MalformedURLException e) {
360: log.debug(e);
361: throw AxisFault.makeFault(e);
362: } catch (HttpException e) {
363: log.debug(e);
364: throw AxisFault.makeFault(e);
365: } catch (IOException e) {
366: log.debug(e);
367: throw AxisFault.makeFault(e);
368: }
369: }
370:
371: private static String findSOAPAction(MessageContext messageContext) {
372: String soapActionString = null;
373:
374: Parameter parameter = messageContext.getTransportOut()
375: .getParameter(HTTPConstants.OMIT_SOAP_12_ACTION);
376: if (parameter != null
377: && JavaUtils.isTrueExplicitly(parameter.getValue())
378: && !messageContext.isSOAP11()) {
379: return "\"\"";
380: }
381:
382: Object disableSoapAction = messageContext.getOptions()
383: .getProperty(
384: Constants.Configuration.DISABLE_SOAP_ACTION);
385:
386: if (!JavaUtils.isTrueExplicitly(disableSoapAction)) {
387: // first try to get the SOAP action from message context
388: soapActionString = messageContext.getSoapAction();
389: if ((soapActionString == null)
390: || (soapActionString.length() == 0)) {
391: // now let's try to get WSA action
392: soapActionString = messageContext.getWSAAction();
393: if (messageContext.getAxisOperation() != null
394: && ((soapActionString == null) || (soapActionString
395: .length() == 0))) {
396: // last option is to get it from the axis operation
397: soapActionString = messageContext
398: .getAxisOperation().getSoapAction();
399: }
400: }
401: }
402:
403: //Since action is optional for SOAP 1.2 we can return null here.
404: if (soapActionString == null && messageContext.isSOAP11()) {
405: soapActionString = "\"\"";
406: }
407:
408: return soapActionString;
409: }
410: }
|