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: package org.apache.axis2.transport.nhttp;
020:
021: import org.apache.axis2.AxisFault;
022: import org.apache.axis2.Constants;
023: import org.apache.axis2.engine.AxisEngine;
024: import org.apache.axis2.description.AxisService;
025: import org.apache.axis2.context.ConfigurationContext;
026: import org.apache.axis2.context.MessageContext;
027: import org.apache.axis2.context.OperationContext;
028: import org.apache.axis2.transport.http.HTTPTransportUtils;
029: import org.apache.axis2.transport.http.HTTPTransportReceiver;
030: import org.apache.axis2.transport.RequestResponseTransport;
031: import org.apache.axiom.om.util.UUIDGenerator;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.http.*;
035: import org.apache.http.nio.NHttpServerConnection;
036: import org.apache.http.protocol.HTTP;
037: import org.apache.ws.commons.schema.XmlSchema;
038:
039: import javax.xml.namespace.QName;
040: import java.io.InputStream;
041: import java.io.OutputStream;
042: import java.io.IOException;
043: import java.io.OutputStreamWriter;
044: import java.util.*;
045: import java.net.NetworkInterface;
046: import java.net.SocketException;
047: import java.net.InetAddress;
048:
049: /**
050: * Processes an incoming request through Axis2. An instance of this class would be created to
051: * process each unique request
052: */
053: public class ServerWorker implements Runnable {
054:
055: private static final Log log = LogFactory
056: .getLog(ServerWorker.class);
057:
058: /** the incoming message to be processed */
059: private MessageContext msgContext = null;
060: /** the Axis2 configuration context */
061: private ConfigurationContext cfgCtx = null;
062: /** the message handler to be used */
063: private ServerHandler serverHandler = null;
064: /** the underlying http connection */
065: private NHttpServerConnection conn = null;
066: /** is this https? */
067: private boolean isHttps = false;
068: /** the http request */
069: private HttpRequest request = null;
070: /** the http response message (which the this would be creating) */
071: private HttpResponse response = null;
072: /** the input stream to read the incoming message body */
073: private InputStream is = null;
074: /** the output stream to write the response message body */
075: private OutputStream os = null;
076: private static final String SOAPACTION = "SOAPAction";
077: private static final String LOCATION = "Location";
078: private static final String CONTENT_TYPE = "Content-Type";
079: private static final String TEXT_HTML = "text/html";
080: private static final String TEXT_XML = "text/xml";
081:
082: /**
083: * Create a new server side worker to process an incoming message and optionally begin creating
084: * its output. This however does not force the processor to write a response back as the
085: * traditional servlet service() method, but creates the background required to write the
086: * response, if one would be created.
087: * @param cfgCtx the Axis2 configuration context
088: * @param conn the underlying http connection
089: * @param serverHandler the handler of the server side messages
090: * @param request the http request received (might still be in the process of being streamed)
091: * @param is the stream input stream to read the request body
092: * @param response the response to be populated if applicable
093: * @param os the output stream to write the response body if one is applicable
094: */
095: public ServerWorker(final ConfigurationContext cfgCtx,
096: final NHttpServerConnection conn, final boolean isHttps,
097: final ServerHandler serverHandler,
098: final HttpRequest request, final InputStream is,
099: final HttpResponse response, final OutputStream os) {
100:
101: this .cfgCtx = cfgCtx;
102: this .conn = conn;
103: this .isHttps = isHttps;
104: this .serverHandler = serverHandler;
105: this .request = request;
106: this .response = response;
107: this .is = is;
108: this .os = os;
109: this .msgContext = createMessageContext(request);
110: }
111:
112: /**
113: * Create an Axis2 message context for the given http request. The request may be in the
114: * process of being streamed
115: * @param request the http request to be used to create the corresponding Axis2 message context
116: * @return the Axis2 message context created
117: */
118: private MessageContext createMessageContext(HttpRequest request) {
119:
120: MessageContext msgContext = new MessageContext();
121: msgContext.setProperty(MessageContext.TRANSPORT_NON_BLOCKING,
122: Boolean.TRUE);
123: msgContext.setConfigurationContext(cfgCtx);
124: if (isHttps) {
125: msgContext.setTransportOut(cfgCtx.getAxisConfiguration()
126: .getTransportOut("https"));
127: msgContext.setTransportIn(cfgCtx.getAxisConfiguration()
128: .getTransportIn("https"));
129: msgContext.setIncomingTransportName("https");
130: } else {
131: msgContext.setTransportOut(cfgCtx.getAxisConfiguration()
132: .getTransportOut(Constants.TRANSPORT_HTTP));
133: msgContext.setTransportIn(cfgCtx.getAxisConfiguration()
134: .getTransportIn(Constants.TRANSPORT_HTTP));
135: msgContext
136: .setIncomingTransportName(Constants.TRANSPORT_HTTP);
137: }
138: msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, this );
139: msgContext.setServiceGroupContextId(UUIDGenerator.getUUID());
140: msgContext.setServerSide(true);
141: msgContext.setProperty(
142: Constants.Configuration.TRANSPORT_IN_URL, request
143: .getRequestLine().getUri());
144:
145: Map headers = new HashMap();
146: Header[] headerArr = request.getAllHeaders();
147: for (int i = 0; i < headerArr.length; i++) {
148: headers
149: .put(headerArr[i].getName(), headerArr[i]
150: .getValue());
151: }
152: msgContext.setProperty(MessageContext.TRANSPORT_HEADERS,
153: headers);
154: // find the remote party IP address and set it to the message context
155: if (conn instanceof HttpInetConnection) {
156: HttpInetConnection inetConn = (HttpInetConnection) conn;
157: InetAddress remoteAddr = inetConn.getRemoteAddress();
158: if (remoteAddr != null) {
159: msgContext.setProperty(MessageContext.REMOTE_ADDR,
160: remoteAddr.getHostAddress());
161: }
162: }
163:
164: // this is required to support Sandesha 2
165: msgContext.setProperty(
166: RequestResponseTransport.TRANSPORT_CONTROL,
167: new HttpCoreRequestResponseTransport(msgContext));
168: return msgContext;
169: }
170:
171: /**
172: * Process the incoming request
173: */
174: public void run() {
175:
176: String method = request.getRequestLine().getMethod()
177: .toUpperCase();
178: if ("GET".equals(method)) {
179: processGet();
180: } else if ("POST".equals(method)) {
181: processPost();
182: } else {
183: handleException("Unsupported method : " + method, null);
184: }
185:
186: if (msgContext != null
187: && msgContext.getOperationContext() != null
188: && !Constants.VALUE_TRUE.equals(msgContext
189: .getOperationContext().getProperty(
190: Constants.RESPONSE_WRITTEN))
191: && !"SKIP".equals(msgContext.getOperationContext()
192: .getProperty(Constants.RESPONSE_WRITTEN))) {
193:
194: response.setStatusCode(HttpStatus.SC_ACCEPTED);
195: serverHandler.commitResponse(conn, response);
196:
197: // make sure that the output stream is flushed and closed properly
198: try {
199: is.close();
200: } catch (IOException ignore) {
201: }
202:
203: // make sure that the output stream is flushed and closed properly
204: try {
205: os.flush();
206: os.close();
207: } catch (IOException ignore) {
208: }
209: }
210: }
211:
212: /**
213: *
214: */
215: private void processPost() {
216:
217: try {
218: Header contentType = request
219: .getFirstHeader(HTTP.CONTENT_TYPE);
220: Header soapAction = request.getFirstHeader(SOAPACTION);
221:
222: HTTPTransportUtils.processHTTPPostRequest(msgContext, is,
223: os, (contentType != null ? contentType.getValue()
224: : null), (soapAction != null ? soapAction
225: .getValue() : null), request
226: .getRequestLine().getUri());
227: } catch (AxisFault e) {
228: handleException("Error processing POST request ", e);
229: }
230: }
231:
232: /**
233: *
234: */
235: private void processGet() {
236:
237: String uri = request.getRequestLine().getUri();
238:
239: String contextPath = cfgCtx.getContextRoot();
240: if (!contextPath.startsWith("/")) {
241: contextPath = "/" + contextPath;
242: }
243: if (!contextPath.endsWith("/")) {
244: contextPath = contextPath + "/";
245: }
246:
247: String servicePath = cfgCtx.getServiceContextPath();
248: if (!servicePath.startsWith("/")) {
249: servicePath = "/" + servicePath;
250: }
251:
252: String serviceName = null;
253: if (uri.startsWith(servicePath)) {
254: serviceName = uri.substring(servicePath.length());
255: if (serviceName.startsWith("/")) {
256: serviceName = serviceName.substring(1);
257: }
258: if (serviceName.indexOf("?") != -1) {
259: serviceName = serviceName.substring(0, serviceName
260: .indexOf("?"));
261: }
262: }
263:
264: Map parameters = new HashMap();
265: int pos = uri.indexOf("?");
266: if (pos != -1) {
267: StringTokenizer st = new StringTokenizer(uri
268: .substring(pos + 1), "&");
269: while (st.hasMoreTokens()) {
270: String param = st.nextToken();
271: pos = param.indexOf("=");
272: if (pos != -1) {
273: parameters.put(param.substring(0, pos), param
274: .substring(pos + 1));
275: } else {
276: parameters.put(param, null);
277: }
278: }
279: }
280:
281: if (uri.equals("/favicon.ico")) {
282: response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
283: response.addHeader(LOCATION,
284: "http://ws.apache.org/favicon.ico");
285: serverHandler.commitResponse(conn, response);
286:
287: } else if (!uri.startsWith(servicePath)) {
288: response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
289: response.addHeader(LOCATION, servicePath + "/");
290: serverHandler.commitResponse(conn, response);
291:
292: } else if (serviceName != null
293: && parameters.containsKey("wsdl")) {
294: AxisService service = (AxisService) cfgCtx
295: .getAxisConfiguration().getServices().get(
296: serviceName);
297: if (service != null) {
298: try {
299: response.addHeader(CONTENT_TYPE, TEXT_XML);
300: serverHandler.commitResponse(conn, response);
301: service.printWSDL(os, getIpAddress());
302:
303: } catch (AxisFault e) {
304: handleException("Axis2 fault writing ?wsdl output",
305: e);
306: return;
307: } catch (SocketException e) {
308: handleException(
309: "Error getting ip address for ?wsdl output",
310: e);
311: return;
312: }
313: }
314:
315: } else if (serviceName != null
316: && parameters.containsKey("wsdl2")) {
317: AxisService service = (AxisService) cfgCtx
318: .getAxisConfiguration().getServices().get(
319: serviceName);
320: if (service != null) {
321: try {
322: response.addHeader(CONTENT_TYPE, TEXT_XML);
323: serverHandler.commitResponse(conn, response);
324: service.printWSDL2(os, getIpAddress());
325: } catch (AxisFault e) {
326: handleException(
327: "Axis2 fault writing ?wsdl2 output", e);
328: return;
329: } catch (SocketException e) {
330: handleException(
331: "Axis2 fault writing ?wsdl2 output", e);
332: return;
333: }
334: }
335:
336: } else if (serviceName != null && parameters.containsKey("xsd")) {
337: if (parameters.get("xsd") == null
338: || "".equals(parameters.get("xsd"))) {
339: AxisService service = (AxisService) cfgCtx
340: .getAxisConfiguration().getServices().get(
341: serviceName);
342: if (service != null) {
343: try {
344: response.addHeader(CONTENT_TYPE, TEXT_XML);
345: serverHandler.commitResponse(conn, response);
346: service.printSchema(os);
347:
348: } catch (AxisFault axisFault) {
349: handleException(
350: "Error writing ?xsd output to client",
351: axisFault);
352: return;
353: } catch (IOException e) {
354: handleException(
355: "Error writing ?xsd output to client",
356: e);
357: return;
358: }
359: }
360:
361: } else {
362: //cater for named xsds - check for the xsd name
363: String schemaName = (String) parameters.get("xsd");
364: AxisService service = (AxisService) cfgCtx
365: .getAxisConfiguration().getServices().get(
366: serviceName);
367:
368: if (service != null) {
369: //run the population logic just to be sure
370: service.populateSchemaMappings();
371: //write out the correct schema
372: Map schemaTable = service.getSchemaMappingTable();
373: XmlSchema schema = (XmlSchema) schemaTable
374: .get(schemaName);
375: if (schema == null) {
376: int dotIndex = schemaName.indexOf('.');
377: if (dotIndex > 0) {
378: String schemaKey = schemaName.substring(0,
379: dotIndex);
380: schema = (XmlSchema) schemaTable
381: .get(schemaKey);
382: }
383: }
384: //schema found - write it to the stream
385: if (schema != null) {
386: response.addHeader(CONTENT_TYPE, TEXT_XML);
387: serverHandler.commitResponse(conn, response);
388: schema.write(os);
389:
390: } else {
391: // no schema available by that name - send 404
392: response.setStatusCode(HttpStatus.SC_NOT_FOUND);
393: }
394: }
395: }
396:
397: } else if (serviceName == null || serviceName.length() == 0) {
398:
399: try {
400: response.addHeader(CONTENT_TYPE, TEXT_HTML);
401: serverHandler.commitResponse(conn, response);
402: os.write(HTTPTransportReceiver.getServicesHTML(cfgCtx)
403: .getBytes());
404:
405: } catch (IOException e) {
406: handleException("Error writing ? output to client", e);
407: }
408:
409: } else {
410: if (parameters.isEmpty()) {
411: AxisService service = (AxisService) cfgCtx
412: .getAxisConfiguration().getServices().get(
413: serviceName);
414: if (service != null) {
415: try {
416: response.addHeader(CONTENT_TYPE, TEXT_HTML);
417: serverHandler.commitResponse(conn, response);
418: os.write(HTTPTransportReceiver
419: .printServiceHTML(serviceName, cfgCtx)
420: .getBytes());
421:
422: } catch (IOException e) {
423: handleException(
424: "Error writing service HTML to client",
425: e);
426: return;
427: }
428: } else {
429: handleException("Invalid service : " + serviceName,
430: null);
431: return;
432: }
433:
434: } else {
435: try {
436: serverHandler.commitResponse(conn, response);
437: HTTPTransportUtils
438: .processHTTPGetRequest(
439: msgContext,
440: os,
441: (request.getFirstHeader(SOAPACTION) != null ? request
442: .getFirstHeader(SOAPACTION)
443: .getValue()
444: : null), request
445: .getRequestLine().getUri(),
446: cfgCtx, parameters);
447:
448: } catch (AxisFault axisFault) {
449: handleException(
450: "Error processing GET request for: "
451: + request.getRequestLine().getUri(),
452: axisFault);
453: }
454: }
455: }
456:
457: // make sure that the output stream is flushed and closed properly
458: try {
459: os.flush();
460: os.close();
461: } catch (IOException ignore) {
462: }
463: }
464:
465: private void handleException(String msg, Exception e) {
466:
467: if (e == null) {
468: log.error(msg);
469: } else {
470: log.error(msg, e);
471: }
472:
473: if (e == null) {
474: e = new Exception(msg);
475: }
476:
477: try {
478: AxisEngine engine = new AxisEngine(cfgCtx);
479: MessageContext faultContext = engine
480: .createFaultMessageContext(msgContext, e);
481: engine.sendFault(faultContext);
482:
483: } catch (Exception ex) {
484: response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
485: response.addHeader(CONTENT_TYPE, TEXT_XML);
486: serverHandler.commitResponse(conn, response);
487:
488: try {
489: os.write(msg.getBytes());
490: if (ex != null) {
491: os.write(ex.getMessage().getBytes());
492: }
493: } catch (IOException ignore) {
494: }
495:
496: if (conn != null) {
497: try {
498: conn.shutdown();
499: } catch (IOException ignore) {
500: }
501: }
502: }
503: }
504:
505: public HttpResponse getResponse() {
506: return response;
507: }
508:
509: public OutputStream getOutputStream() {
510: return os;
511: }
512:
513: public InputStream getIs() {
514: return is;
515: }
516:
517: public ServerHandler getServiceHandler() {
518: return serverHandler;
519: }
520:
521: public NHttpServerConnection getConn() {
522: return conn;
523: }
524:
525: /**
526: * Copied from transport.http of Axis2
527: *
528: * Returns the ip address to be used for the replyto epr
529: * CAUTION:
530: * This will go through all the available network interfaces and will try to return an ip address.
531: * First this will try to get the first IP which is not loopback address (127.0.0.1). If none is found
532: * then this will return this will return 127.0.0.1.
533: * This will <b>not<b> consider IPv6 addresses.
534: * <p/>
535: * TODO:
536: * - Improve this logic to genaralize it a bit more
537: * - Obtain the ip to be used here from the Call API
538: *
539: * @return Returns String.
540: * @throws java.net.SocketException
541: */
542: private static String getIpAddress() throws SocketException {
543: Enumeration e = NetworkInterface.getNetworkInterfaces();
544: String address = "127.0.0.1";
545:
546: while (e.hasMoreElements()) {
547: NetworkInterface netface = (NetworkInterface) e
548: .nextElement();
549: Enumeration addresses = netface.getInetAddresses();
550:
551: while (addresses.hasMoreElements()) {
552: InetAddress ip = (InetAddress) addresses.nextElement();
553: if (!ip.isLoopbackAddress()
554: && isIP(ip.getHostAddress())) {
555: return ip.getHostAddress();
556: }
557: }
558: }
559: return address;
560: }
561:
562: private static boolean isIP(String hostAddress) {
563: return hostAddress.split("[.]").length == 4;
564: }
565: }
|