| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.synapse.transport.nhttp; |
| |
| import org.apache.axis2.AxisFault; |
| import org.apache.axis2.addressing.EndpointReference; |
| import org.apache.axis2.context.ConfigurationContext; |
| import org.apache.axis2.context.MessageContext; |
| import org.apache.axis2.description.AxisOperation; |
| import org.apache.axis2.description.AxisService; |
| import org.apache.axis2.description.Parameter; |
| import org.apache.axis2.engine.AxisEngine; |
| import org.apache.axis2.transport.http.HTTPTransportReceiver; |
| import org.apache.axis2.util.JavaUtils; |
| import org.apache.axis2.util.MessageContextBuilder; |
| import org.apache.axis2.wsdl.WSDLConstants; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.http.HttpRequest; |
| import org.apache.http.HttpResponse; |
| import org.apache.http.HttpStatus; |
| import org.apache.http.nio.NHttpServerConnection; |
| import org.apache.http.protocol.HTTP; |
| import org.apache.synapse.transport.nhttp.util.RESTUtil; |
| import org.apache.ws.commons.schema.XmlSchema; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.InetAddress; |
| import java.net.NetworkInterface; |
| import java.net.SocketException; |
| import java.util.*; |
| |
| /** |
| * Default http Get processor implementation for Synapse. |
| */ |
| public class DefaultHttpGetProcessor implements HttpGetRequestProcessor { |
| private static final Log log = LogFactory.getLog(DefaultHttpGetProcessor.class); |
| |
| private static final String LOCATION = "Location"; |
| private static final String CONTENT_TYPE = "Content-Type"; |
| private static final String TEXT_HTML = "text/html"; |
| private static final String TEXT_XML = "text/xml"; |
| |
| protected ConfigurationContext cfgCtx; |
| |
| protected ServerHandler serverHandler; |
| |
| public void init(ConfigurationContext cfgCtx, ServerHandler serverHandler) throws AxisFault { |
| this.cfgCtx = cfgCtx; |
| this.serverHandler = serverHandler; |
| } |
| |
| /** |
| * Process the HTTP GET request. |
| * |
| * @param request The HttpRequest |
| * @param response The HttpResponse |
| * @param msgContext The MessageContext |
| * @param conn The NHttpServerConnection |
| * @param os The OutputStream |
| */ |
| public void process(HttpRequest request, |
| HttpResponse response, |
| MessageContext msgContext, |
| NHttpServerConnection conn, |
| OutputStream os, |
| boolean isRestDispatching) { |
| |
| String uri = request.getRequestLine().getUri(); |
| |
| String servicePath = cfgCtx.getServiceContextPath(); |
| if (!servicePath.startsWith("/")) { |
| servicePath = "/" + servicePath; |
| } |
| |
| String serviceName = getServiceName(request); |
| |
| Map<String, String> parameters = new HashMap<String, String>(); |
| int pos = uri.indexOf("?"); |
| if (pos != -1) { |
| msgContext.setTo(new EndpointReference(uri.substring(0, pos))); |
| StringTokenizer st = new StringTokenizer(uri.substring(pos + 1), "&"); |
| while (st.hasMoreTokens()) { |
| String param = st.nextToken(); |
| pos = param.indexOf("="); |
| if (pos != -1) { |
| parameters.put(param.substring(0, pos), param.substring(pos + 1)); |
| } else { |
| parameters.put(param, null); |
| } |
| } |
| } else { |
| msgContext.setTo(new EndpointReference(uri)); |
| } |
| |
| if (isServiceListBlocked(uri)) { |
| response.setStatusCode(HttpStatus.SC_FORBIDDEN); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| } else if (uri.equals("/favicon.ico")) { |
| response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY); |
| response.addHeader(LOCATION, "http://ws.apache.org/favicon.ico"); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| } else if (serviceName != null && parameters.containsKey("wsdl")) { |
| generateWsdl(request, response, msgContext, |
| conn, os, serviceName, parameters, isRestDispatching); |
| return; |
| } else if (serviceName != null && parameters.containsKey("wsdl2")) { |
| generateWsdl2(request, response, msgContext, |
| conn, os, serviceName, isRestDispatching); |
| return; |
| } else if (serviceName != null && parameters.containsKey("xsd")) { |
| generateXsd(request, response, msgContext, conn, os, serviceName, |
| parameters, isRestDispatching); |
| return; |
| } else if (serviceName != null && parameters.containsKey("info")) { |
| generateServiceDetailsPage(response, conn, os, serviceName); |
| } else if (uri.startsWith(servicePath) && |
| (serviceName == null || serviceName.length() == 0)) { |
| generateServicesList(response, conn, os, servicePath); |
| } else { |
| processGetAndDelete(request, response, msgContext, |
| conn, os, "GET", isRestDispatching); |
| return; |
| } |
| |
| // make sure that the output stream is flushed and closed properly |
| closeOutputStream(os); |
| } |
| |
| private void closeOutputStream(OutputStream os) { |
| try { |
| os.flush(); |
| os.close(); |
| } catch (IOException ignore) { |
| } |
| } |
| |
| /** |
| * Is the incoming URI is requesting service list and http.block_service_list=true in |
| * nhttp.properties |
| * @param incomingURI incoming URI |
| * @return whether to proceed with incomingURI |
| |
| */ |
| protected boolean isServiceListBlocked(String incomingURI) { |
| String isBlocked = NHttpConfiguration.getInstance().isServiceListBlocked(); |
| |
| return (("/services").equals(incomingURI) || ("/services" + "/").equals(incomingURI)) && |
| Boolean.parseBoolean(isBlocked); |
| } |
| |
| /** |
| * Returns the service name. |
| * |
| * @param request HttpRequest |
| * @return service name as a String |
| */ |
| protected String getServiceName(HttpRequest request) { |
| String uri = request.getRequestLine().getUri(); |
| |
| String servicePath = cfgCtx.getServiceContextPath(); |
| if (!servicePath.startsWith("/")) { |
| servicePath = "/" + servicePath; |
| } |
| |
| String serviceName = null; |
| if (uri.startsWith(servicePath)) { |
| serviceName = uri.substring(servicePath.length()); |
| if (serviceName.startsWith("/")) { |
| serviceName = serviceName.substring(1); |
| } |
| if (serviceName.contains("?")) { |
| serviceName = serviceName.substring(0, serviceName.indexOf("?")); |
| } |
| } else { |
| // this may be a custom URI |
| String incomingURI = request.getRequestLine().getUri(); |
| |
| Map serviceURIMap = (Map) cfgCtx.getProperty(NhttpConstants.EPR_TO_SERVICE_NAME_MAP); |
| if (serviceURIMap != null) { |
| Set keySet = serviceURIMap.keySet(); |
| for (Object key : keySet) { |
| if (incomingURI.toLowerCase().contains(((String) key).toLowerCase())) { |
| return (String) serviceURIMap.get(key); |
| } |
| } |
| } |
| } |
| |
| if (serviceName != null) { |
| int opnStart = serviceName.indexOf("/"); |
| if (opnStart != -1) { |
| serviceName = serviceName.substring(0, opnStart); |
| } |
| } |
| return serviceName; |
| } |
| |
| /** |
| * Generates the services list. |
| * |
| * @param response HttpResponse |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param servicePath service path of the service |
| */ |
| protected void generateServicesList(HttpResponse response, |
| NHttpServerConnection conn, |
| OutputStream os, String servicePath) { |
| try { |
| byte[] bytes = getServicesHTML( |
| servicePath.endsWith("/") ? "" : servicePath + "/").getBytes(); |
| response.addHeader(CONTENT_TYPE, TEXT_HTML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(bytes); |
| |
| } catch (IOException e) { |
| handleBrowserException(response, conn, os, |
| "Error generating services list", e); |
| } |
| } |
| |
| /** |
| * Generates service details page. |
| * |
| * @param response HttpResponse |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param serviceName service name |
| */ |
| protected void generateServiceDetailsPage(HttpResponse response, |
| NHttpServerConnection conn, |
| OutputStream os, String serviceName) { |
| AxisService service = cfgCtx.getAxisConfiguration(). |
| getServices().get(serviceName); |
| if (service != null) { |
| String parameterValue = (String) service.getParameterValue("serviceType"); |
| if ("proxy".equals(parameterValue) && !isWSDLProvidedForProxyService(service)) { |
| handleBrowserException(response, conn, os, |
| "No WSDL was provided for the Service " + serviceName + |
| ". A WSDL cannot be generated.", null); |
| } |
| try { |
| byte[] bytes = |
| HTTPTransportReceiver.printServiceHTML(serviceName, cfgCtx).getBytes(); |
| response.addHeader(CONTENT_TYPE, TEXT_HTML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(bytes); |
| |
| } catch (IOException e) { |
| handleBrowserException(response, conn, os, |
| "Error generating service details page for : " + serviceName, e); |
| } |
| } else { |
| handleBrowserException(response, conn, os, |
| "Invalid service : " + serviceName, null); |
| } |
| } |
| |
| /** |
| * Generates Schema. |
| * |
| * @param request HttpRequest |
| * @param response HttpResponse |
| * @param messageCtx Current MessageContext |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param serviceName service name |
| * @param parameters url parameters |
| * @param isRestDispatching Whether to handle this as REST |
| */ |
| protected void generateXsd(HttpRequest request, HttpResponse response, |
| MessageContext messageCtx, NHttpServerConnection conn, |
| OutputStream os, String serviceName, |
| Map<String, String> parameters, boolean isRestDispatching) { |
| if (parameters.get("xsd") == null || "".equals(parameters.get("xsd"))) { |
| AxisService service = cfgCtx.getAxisConfiguration() |
| .getServices().get(serviceName); |
| if (service != null) { |
| try { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| service.printSchema(baos); |
| response.addHeader(CONTENT_TYPE, TEXT_XML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(baos.toByteArray()); |
| closeOutputStream(os); |
| |
| } catch (Exception e) { |
| handleBrowserException(response, conn, os, |
| "Error generating ?xsd output for service : " + serviceName, e); |
| } |
| } else { |
| processGetAndDelete(request, response, messageCtx, conn, os, |
| serviceName, isRestDispatching); |
| } |
| |
| } else { |
| //cater for named xsds - check for the xsd name |
| String schemaName = parameters.get("xsd"); |
| AxisService service = cfgCtx.getAxisConfiguration() |
| .getServices().get(serviceName); |
| |
| if (service != null) { |
| //run the population logic just to be sure |
| service.populateSchemaMappings(); |
| //write out the correct schema |
| Map schemaTable = service.getSchemaMappingTable(); |
| XmlSchema schema = (XmlSchema) schemaTable.get(schemaName); |
| if (schema == null) { |
| int dotIndex = schemaName.indexOf('.'); |
| if (dotIndex > 0) { |
| String schemaKey = schemaName.substring(0, dotIndex); |
| schema = (XmlSchema) schemaTable.get(schemaKey); |
| } |
| } |
| //schema found - write it to the stream |
| if (schema != null) { |
| try { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| schema.write(baos); |
| response.addHeader(CONTENT_TYPE, TEXT_XML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(baos.toByteArray()); |
| closeOutputStream(os); |
| } catch (Exception e) { |
| handleBrowserException(response, conn, os, |
| "Error generating named ?xsd output for service : " + serviceName, e); |
| } |
| |
| } else { |
| // no schema available by that name - send 404 |
| response.setStatusCode(HttpStatus.SC_NOT_FOUND); |
| closeOutputStream(os); |
| } |
| } else { |
| processGetAndDelete(request, response, messageCtx, conn, os, |
| serviceName, isRestDispatching); |
| } |
| } |
| } |
| |
| /** |
| * Generate WSDL2. |
| * |
| * @param request HttpRequest |
| * @param response HttpResponse |
| * @param msgContext MessageContext |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param serviceName service name |
| * @param isRestDispatching weather nhttp should do rest dispatching |
| */ |
| protected void generateWsdl2(HttpRequest request, HttpResponse response, |
| MessageContext msgContext, |
| NHttpServerConnection conn, |
| OutputStream os, String serviceName, boolean isRestDispatching) { |
| AxisService service = cfgCtx.getAxisConfiguration(). |
| getServices().get(serviceName); |
| if (service != null) { |
| String parameterValue = (String) service.getParameterValue("serviceType"); |
| if ("proxy".equals(parameterValue) && !isWSDLProvidedForProxyService(service)) { |
| handleBrowserException(response, conn, os, |
| "No WSDL was provided for the Service " + serviceName + |
| ". A WSDL cannot be generated.", null); |
| } |
| try { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| service.printWSDL2(baos, getIpAddress()); |
| response.addHeader(CONTENT_TYPE, TEXT_XML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(baos.toByteArray()); |
| closeOutputStream(os); |
| |
| } catch (Exception e) { |
| handleBrowserException(response, conn, os, |
| "Error generating ?wsdl2 output for service : " + serviceName, e); |
| } |
| } else { |
| processGetAndDelete(request, response, msgContext, |
| conn, os, "GET", isRestDispatching); |
| } |
| } |
| |
| /** |
| * Generate WSDL. |
| * |
| * @param request HttpRequest |
| * @param response HttpResponse |
| * @param msgContext MessageContext |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param serviceName service name |
| * @param parameters parameters |
| * @param isRestDispatching if restDispatching is on |
| */ |
| protected void generateWsdl(HttpRequest request, HttpResponse response, |
| MessageContext msgContext, |
| NHttpServerConnection conn, |
| OutputStream os, String serviceName, |
| Map<String, String> parameters, boolean isRestDispatching) { |
| AxisService service = cfgCtx.getAxisConfiguration(). |
| getServices().get(serviceName); |
| if (service != null) { |
| try { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| String parameterValue = parameters.get("wsdl"); |
| if (parameterValue == null) { |
| service.printWSDL(baos, getIpAddress()); |
| } else { |
| // here the parameter value should be the wsdl file name |
| service.printUserWSDL(baos, parameterValue); |
| } |
| response.addHeader(CONTENT_TYPE, TEXT_XML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| os.write(baos.toByteArray()); |
| closeOutputStream(os); |
| |
| } catch (Exception e) { |
| handleBrowserException(response, conn, os, |
| "Error generating ?wsdl output for service : " + serviceName, e); |
| } |
| } else { |
| processGetAndDelete(request, response, msgContext, |
| conn, os, "GET", isRestDispatching); |
| } |
| } |
| |
| /** |
| * Calls the RESTUtil to process GET and DELETE Request |
| * |
| * @param request HttpRequest |
| * @param response HttpResponse |
| * @param msgContext MessageContext |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param method HTTP method, either GET or DELETE |
| * @param isRestDispatching weather transport should do rest dispatching |
| */ |
| protected void processGetAndDelete(HttpRequest request, HttpResponse response, |
| MessageContext msgContext, |
| NHttpServerConnection conn, OutputStream os, |
| String method, boolean isRestDispatching) { |
| try { |
| RESTUtil.processGetAndDeleteRequest( |
| msgContext, os, request.getRequestLine().getUri(), |
| request.getFirstHeader(HTTP.CONTENT_TYPE), method, isRestDispatching); |
| // do not let the output stream close (as by default below) since |
| // we are serving this GET/DELETE request through the Synapse engine |
| } catch (AxisFault axisFault) { |
| handleException(response, msgContext, conn, os, |
| "Error processing " + method + " request for: " + |
| request.getRequestLine().getUri(), axisFault); |
| } |
| |
| } |
| |
| /** |
| * Handles exception. |
| * |
| * @param response HttpResponse |
| * @param msgContext MessageContext |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param msg message |
| * @param e Exception |
| */ |
| protected void handleException(HttpResponse response, MessageContext msgContext, |
| NHttpServerConnection conn, |
| OutputStream os, String msg, Exception e) { |
| if (e == null) { |
| log.error(msg); |
| } else { |
| log.error(msg, e); |
| } |
| |
| if (e == null) { |
| e = new Exception(msg); |
| } |
| |
| try { |
| MessageContext faultContext = MessageContextBuilder.createFaultMessageContext( |
| msgContext, e); |
| AxisEngine.sendFault(faultContext); |
| |
| } catch (Exception ex) { |
| response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); |
| response.addHeader(CONTENT_TYPE, TEXT_XML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| |
| try { |
| os.write(msg.getBytes()); |
| if (ex != null) { |
| os.write(ex.getMessage().getBytes()); |
| } |
| } catch (IOException ignore) { |
| } |
| |
| if (conn != null) { |
| try { |
| conn.shutdown(); |
| } catch (IOException ignore) { |
| } |
| } |
| } |
| } |
| |
| /** |
| * Handles browser exception. |
| * |
| * @param response HttpResponse |
| * @param conn NHttpServerConnection |
| * @param os OutputStream |
| * @param msg message |
| * @param e Exception |
| */ |
| protected void handleBrowserException(HttpResponse response, |
| NHttpServerConnection conn, OutputStream os, |
| String msg, Exception e) { |
| if (e == null) { |
| log.error(msg); |
| } else { |
| log.error(msg, e); |
| } |
| |
| if (!response.containsHeader(HTTP.TRANSFER_ENCODING)) { |
| response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); |
| response.setReasonPhrase(msg); |
| response.addHeader(CONTENT_TYPE, TEXT_HTML); |
| serverHandler.commitResponseHideExceptions(conn, response); |
| try { |
| os.write(msg.getBytes()); |
| os.close(); |
| } catch (IOException ignore) { |
| } |
| } |
| |
| if (conn != null) { |
| try { |
| conn.shutdown(); |
| } catch (IOException ignore) { |
| } |
| } |
| } |
| |
| /** |
| * Checks whether a wsdl is provided for a proxy service. |
| * |
| * @param service AxisService |
| * @return whether the wsdl is provided or not |
| */ |
| protected boolean isWSDLProvidedForProxyService(AxisService service) { |
| boolean isWSDLProvided = false; |
| if (service.getParameterValue(WSDLConstants.WSDL_4_J_DEFINITION) != null || |
| service.getParameterValue(WSDLConstants.WSDL_20_DESCRIPTION) != null) { |
| isWSDLProvided = true; |
| } |
| return isWSDLProvided; |
| } |
| |
| /** |
| * Whatever this method returns as the IP is ignored by the actual http/s listener when |
| * its getServiceEPR is invoked. This was originally copied from axis2 |
| * |
| * @return Returns String. |
| * @throws java.net.SocketException if the socket can not be accessed |
| */ |
| protected static String getIpAddress() throws SocketException { |
| Enumeration e = NetworkInterface.getNetworkInterfaces(); |
| String address = "127.0.0.1"; |
| |
| while (e.hasMoreElements()) { |
| NetworkInterface netface = (NetworkInterface) e.nextElement(); |
| Enumeration addresses = netface.getInetAddresses(); |
| |
| while (addresses.hasMoreElements()) { |
| InetAddress ip = (InetAddress) addresses.nextElement(); |
| if (!ip.isLoopbackAddress() && isIP(ip.getHostAddress())) { |
| return ip.getHostAddress(); |
| } |
| } |
| } |
| return address; |
| } |
| |
| protected static boolean isIP(String hostAddress) { |
| return hostAddress.split("[.]").length == 4; |
| } |
| |
| /** |
| * Returns the HTML text for the list of services deployed. |
| * This can be delegated to another Class as well |
| * where it will handle more options of GET messages. |
| * |
| * @param prefix to be used for the Service names |
| * @return the HTML to be displayed as a String |
| */ |
| protected String getServicesHTML(String prefix) { |
| |
| Map services = cfgCtx.getAxisConfiguration().getServices(); |
| Hashtable erroneousServices = cfgCtx.getAxisConfiguration().getFaultyServices(); |
| boolean servicesFound = false; |
| |
| StringBuilder resultBuf = new StringBuilder(); |
| resultBuf.append("<html><head><title>Axis2: Services</title></head>" + "<body>"); |
| |
| if ((services != null) && !services.isEmpty()) { |
| |
| servicesFound = true; |
| resultBuf.append("<h2>" + "Deployed services" + "</h2>"); |
| |
| for (Object service : services.values()) { |
| |
| AxisService axisService = (AxisService) service; |
| Parameter parameter = axisService.getParameter( |
| NhttpConstants.HIDDEN_SERVICE_PARAM_NAME); |
| if (axisService.getName().startsWith("__") || |
| (parameter != null && JavaUtils.isTrueExplicitly(parameter.getValue()))) { |
| continue; // skip private services |
| } |
| |
| Iterator iterator = axisService.getOperations(); |
| resultBuf.append("<h3><a href=\"").append(prefix).append(axisService.getName()).append( |
| "?wsdl\">").append(axisService.getName()).append("</a></h3>"); |
| |
| if (iterator.hasNext()) { |
| resultBuf.append("Available operations <ul>"); |
| |
| for (; iterator.hasNext();) { |
| AxisOperation axisOperation = (AxisOperation) iterator.next(); |
| resultBuf.append("<li>").append( |
| axisOperation.getName().getLocalPart()).append("</li>"); |
| } |
| resultBuf.append("</ul>"); |
| } else { |
| resultBuf.append("No operations specified for this service"); |
| } |
| } |
| } |
| |
| if ((erroneousServices != null) && !erroneousServices.isEmpty()) { |
| servicesFound = true; |
| resultBuf.append("<hr><h2><font color=\"blue\">Faulty Services</font></h2>"); |
| Enumeration faultyservices = erroneousServices.keys(); |
| |
| while (faultyservices.hasMoreElements()) { |
| String faultyserviceName = (String) faultyservices.nextElement(); |
| resultBuf.append("<h3><font color=\"blue\">").append( |
| faultyserviceName).append("</font></h3>"); |
| } |
| } |
| |
| if (!servicesFound) { |
| resultBuf.append("<h2>There are no services deployed</h2>"); |
| } |
| |
| resultBuf.append("</body></html>"); |
| return resultBuf.toString(); |
| } |
| |
| } |